Can Modern Models Replace Traditional Harnesses? A Historical Look (Part 1)
The article traces how low‑level programming harnesses—from ENIAC wiring and punch‑card code to assembly, compilers, operating systems, database optimizers, and memory‑management techniques—have been progressively abstracted away by tools, type systems, and language semantics, highlighting what has been "eaten" and what still requires manual expertise.
ENIAC and the Era of Wiring
In 1946 the ENIAC occupied a 30‑meter long room, weighed ~30 tons and required hundreds of cables and dozens of switches to rewire for each new program. Programmers, then called “computers”, memorized which wire connected where, a highly personal knowledge that was passed orally.
Punch Cards and Tape
When punched tape and cards were introduced, programs became sequences of readable symbols, separating execution from storage. However the code was still machine‑specific, encoded in octal or hex, and inserting instructions required manual recalculation of jump offsets and tedious debugging.
Assembly Language
Early assembly systems such as Kathleen Booth’s symbols for the ARC and Grace Hopper’s A‑0 compiler translated symbolic instructions like MOV AX, 1 into machine bytes ( B8 01 00) and resolved labels to addresses, eliminating manual opcode memorisation and jump‑offset calculations.
Eaten: opcode memorisation, manual address calculation, hand‑written linking.
Compilers
FORTRAN
In 1954 John Backus proposed a “formula translation system” for the IBM 704, leading to FORTRAN. Initially programmers resisted, fearing loss of control and performance. The first FORTRAN compiler (1957) produced code 80‑90 % as fast as hand‑written assembly for many numerical tasks.
Eaten: manual loop unrolling, register‑allocation heuristics, hand‑tuned instruction scheduling.
Compiler Optimizations
Subsequent compiler optimisations (constant folding, dead‑code elimination, loop‑invariant code motion, vectorisation, inlining) gradually internalised programmer experience. Yet some low‑level tricks remain beyond compiler reach.
Operating Systems
Bare‑Metal Programming
Before OSes, writing a program meant handling interrupts, I/O sequencing, CPU yielding and memory management yourself, leading to duplicated “harness” code across teams.
Unix
Unix extracted common harnesses—interrupt handling, process scheduling, memory paging, I/O abstraction—into kernel services, popularising “everything is a file” and eliminating hand‑written I/O loops. Applications now use malloc for memory and epoll for I/O without knowing kernel internals.
Eaten: hand‑written interrupt handlers, manual paging, manual I/O polling.
However kernel scheduling introduces nondeterministic latency, page‑fault interruptions and syscall overhead, which can be unacceptable for hard‑real‑time workloads.
Databases
Hand‑crafted Execution Plans
Early relational databases relied on DBAs to manually craft join orders, choose indexes, and rewrite sub‑queries, a skill set that was undocumented and highly experiential.
Optimisers
Rule‑Based Optimisers (RBO) applied a fixed list of ~15 rules (e.g., “use index if available”), eliminating the need to remember to add indexes. Yet developers could “trick” RBO with specific SQL patterns, creating a new harness of rule‑aware query writing.
Cost‑Based Optimisers (CBO), grounded in the System R paper by Selinger et al., use statistics, histograms and cost estimates to choose plans, handling far more complex scenarios and effectively internalising experience‑based join ordering.
Eaten: manual join‑order decisions and index‑selection heuristics.
Memory Management
Manual Management
C’s malloc / free gave programmers full control, spawning patterns such as object pools, reference counting, arena allocators and RAII in C++ to mitigate allocation overhead and fragmentation.
Garbage Collection
Java’s automatic GC removed the need for explicit free, object pools and reference counting, but introduced pause‑time costs; early “Stop‑The‑World” GC could halt applications for seconds, spawning a deep‑skill area of GC‑tuning (heap size, generation ratios, algorithm choice).
Rust
Rust moves memory‑safety responsibilities into the type system via ownership and the borrow checker, eliminating data races and dangling pointers at compile time. This is not a “eating” but a transfer of burden from runtime/programmer to static analysis, at the price of a steep learning curve.
Key insight: Formal, machine‑semantic aspects can be fully internalised into tools, while performance‑critical or hardware‑specific parts still require manual tuning.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
AI Engineer Programming
In the AI era, defining problems is often more important than solving them; here we explore AI's contradictions, boundaries, and possibilities.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
