Programming Is Not a Skill

The claim is in the title, so I won’t hedge it: programming is not a skill. Not “is becoming less of one,” and not “is changing.” The thing we call the skill of programming, the ability to write code by hand in a particular language, stopped being the skill a long time ago. LLMs didn’t end it. They’re the latest step in a decline that started before most working programmers were born.

I know how that sounds. What follows is the history of the field testifying against the idea, one abstraction at a time, plus the research on what interviews actually measure. The pattern is old enough that we should have seen it by now.

Two tracks converging: the writing track eliminated hand-written code in four waves (machine coding, CRUD applications, glue code, production code), the checking track eliminated hand-checking in two (automated testing, continuous delivery), and both leave the same remainder: deciding what to build, specifying it, and checking the result, with hand-writing code near zero while the job title survived every wave
Two different halves of the job were automated on parallel tracks. The writing track removed the hand-writing; the checking track removed the hand-checking. Both end in the same place, because both automated the same kind of thing: a check that used to need human judgment, moved into something a machine can run.

The coder was the first to go

In the early 1950s the industry drew a line inside programming itself. The programmer studied the problem and drew the flowchart. The coder took the flowchart and produced the machine instructions. The coder was the junior role, lower on the salary scale, closer to the machine. Grace Hopper described the arrangement plainly in May 1954: the programmer’s final result, “to be passed on to a coder, will be a flow chart,” and “it is then the task of the coder to reduce this flow chart to coding, to a list in computer code.”

Hopper’s point was not to defend the division. It was to announce its end. The coder’s job, she said, was “time-consuming and fraught with mistakes” and would be “the first human operation to be replaced by the computer itself.” The thing being automated was code writing. The word for the automation was “automatic coding,” and the people building it said out loud that it would replace the coder or release him to become a programmer.

A few months later, John Backus’s IBM team published the preliminary report for FORTRAN with the goal stated just as bluntly: “Since FORTRAN should virtually eliminate coding and debugging.” Backus’s 1978 retrospective reprints that line with an exclamation point and adds, “we were not exactly a group of pessimists.” The skepticism from outside was real too: when the team pitched their plans to customers, “most of our listeners were extremely skeptical about all of our claims.”

The historians who have traced this period found the outcome neither side predicted. Nathan Ensmenger’s history of the coder role records that FORTRAN and COBOL, “although originally intended to eliminate the need for skilled programmers altogether, had the unintended effect of elevating their status”: by eliminating the tedium of low-level machine coding, the languages “allowed programmers to focus less on technical minutia and more on high-status activities such as design and analysis.” The automation that was aimed at the code writing didn’t eliminate the programmer. It promoted one. And the coder role didn’t fade away politely: Ensmenger found that most corporations simply “stopped formally differentiating between ‘programmers’ and ‘coders,’” adopting a single title for whatever the process had left.

The ladder kept rising between the waves, and the pitches were about legibility more than power. COBOL (1959) was designed so that its English-like syntax would, in lead designer Jean Sammet’s words, provide documentation to all who might wish to examine the programs, including the supervisory or management personnel; one historian of the language — in a paper titled “The Legibility of Programming” — calls the English-like appearance “a rhetorical move designed to make the concept of code itself more legible to non-programming management.” BASIC (1964) was built so that Dartmouth freshmen could learn to program in two one-hour lectures, and its authors wrote that the language had removed “programming” from the private domain of the experts. The frameworks of the 2000s took the remaining plumbing: Rails’ scaffolding generated the CRUD screens, and its doctrine of convention over configuration meant thousands of decisions “just need to be made once, and if someone else can do it for you, all the better.” Each of these widened who could read or write software, and the widening stayed.

The claim was kept, not broken

What happened next is the part worth dwelling on, because it kept happening, and each time it was delivered.

In 1982, James Martin published Application Development Without Programmers, arguing that fourth-generation languages would let end users build applications directly, and projecting that the industry would need 28 million programmers within a decade unless productivity improved. The book won a $100,000 award from the publisher for its projected impact. The 28 million did not materialize; that projection was wrong. But the book’s actual claim, that people would build applications without writing code, was kept so thoroughly that nobody counts it as a claim anymore. Every spreadsheet macro, every dashboard assembled from parts, every automation a non-programmer wired up last week is that claim being delivered. What happened to the programmers was what had happened a generation earlier: the new abstraction elevated the people who used it, and the industry kept needing fewer people per system while building more systems.

The 2010s version was no-code and low-code: visual tools that would let anyone assemble software from parts. The claim was kept. Millions of people who cannot write code assemble working software from parts every day. What the platforms did not do is retire the job title. The tools became things programmers use and things non-programmers use in the millions, and the hand-writing they eliminated stayed eliminated.

Now the pitch is LLMs, and the claim has the same shape: the machine writes the code, so the skill of writing code is over. Every previous generation of this claim was kept, not broken. The specific activity being automated really did stop being a job, and it stayed stopped: the coder went, and nobody misses the role. The layer being eliminated now is the last one, and what is left of hand-writing code as a professional activity is close to zero.

The mechanism

Why did the automation promote the programmer instead of replacing him? Because the machine absorbs what is both expensive to do and cheap to check. Writing machine instructions was expensive to do and cheap to check: does the program run, does the output match. So it went first. What stays human is what’s expensive to check: what to build, whether it’s correct, whether anyone wants it. The skill didn’t disappear at any step. It moved to the expensive-to-check side, and it has been moving to that side ever since.

The checking track starts earlier than testing, and it was never only about checks. In 1968, Dijkstra argued that the unbridled GOTO made a program’s progress terribly hard to follow, because “our intellectual powers are rather geared to master static relations”; structured programming made control flow something a reader could trace instead of something they simulated in their head. Object-orientation and the design-patterns catalog (1994) did the same at the scale of teams, providing “a common vocabulary for design” so a codebase could be legible to an organization rather than to its author. Functional programming made the same move from the other side: a pure function’s behavior is fully specified by its inputs, checkable without judgment.

Then the track turned explicit checks into executable ones. The JUnit authors promised a way to “cheaply and incrementally build a test suite that will help you measure your progress, spot unintended side effects”; automated testing moved the check of whether code still works from a human reading it to a machine running it. Continuous delivery moved the check of whether code can ship into a pipeline, defining the goal as getting changes “into production, or into the hands of users, safely and quickly in a sustainable way.” Every rung on this track made the implicit explicit, and the last two made it executable: a check that used to need judgment, run by a machine. Each one moved the skill further toward deciding and specifying, and away from hand-writing and hand-checking.

And the skill didn’t just move. It shrank. Backus, describing 1953, said it plainly: cramming programs into tiny stores made programming “really a black art,” where “each programmer had his own inscrutable tricks.” A program was legible to its author and to almost nobody else, and that opacity is what made programming look like genius. That legibility boundary is what made “can write code” a scarce, genius-flavored skill. As abstractions rose, the boundary moved up with it. A Python program is legible to its author, to their team, and to a model that has read the public corpus. The genius aura was never difficulty. It was scarcity of legibility.

So “programming is a skill” was always a claim about legibility, not about difficulty. The difficulty moved to specifying what you want and checking what you got. That’s the skill now. It was arguably the skill all along.

The manual labor hides the craft

Calling code writing a craft doesn’t rescue it from this argument. I disagree with that claim too. Hand-writing code is manual labor that hides the craft, because the mechanical work and the decisions happen in the same editor. The labor is translating a decision into the syntax a machine accepts. The craft is deciding what that machine should do, what it must never do, and how to tell whether the result is any good. Manual doesn’t mean easy or worthless. It means the human is doing work that need not remain human work.

Fred Brooks’s No Silver Bullet gives this distinction a vocabulary. He separates the essential task of forming a software system’s conceptual structure from the accidental task of representing that structure in programming languages and mapping it onto machines. Accidental here means a difficulty of the representation, not an error. His argument is that removing those difficulties doesn’t remove the conceptual work. That is why automating code production and retaining a difficult profession are compatible. The craft can survive the removal of the labor that used to obscure it.

Google’s SRE book makes a related distinction in “Eliminating Toil”: if a machine can do the task just as well, or the task can be designed away, it is a candidate for automation; when human judgment is essential, the work is probably not toil. The chapter explicitly says enjoyment isn’t the test. Someone can enjoy repetitive work without making it necessary. Its definition concerns operating production services, not all software development, so I wouldn’t label every line of code toil. But it supplies a useful test for the craft defense: what judgment would be lost if a machine performed this step?

There is a serious objection here. In “What Is Software Design?”, first published in 1992, Jack Reeves argues that source code is the design, and that coding, testing, and debugging are design activities. He rejects the picture of programmers merely constructing a design someone else finished. That objection rules out an easy but false version of my argument: that every decision is settled before anyone opens an editor. Sometimes writing a function exposes a contradiction in the requirement. Sometimes a failing test forces a different design. The judgment in those moments is the craft. Producing the characters is not what makes it craft, and delegating their production doesn’t excuse anyone from making the judgment.

Even the Software Craftsmanship Manifesto describes its commitments in terms of software quality, continuing value, professional community, and productive partnerships. It doesn’t make manual authorship a condition of quality. That doesn’t mean its signatories endorse LLMs. It means the manifesto’s commitments don’t settle the question of who must type the code.

Consider a retry mechanism for a payment request. An agent can generate the loop. The craft is recognizing that a timeout doesn’t prove the payment failed, deciding how to prevent a second charge, and testing what happens when the first request succeeds but its response is lost. A beautifully hand-written loop can still charge someone twice. Typing it yourself cannot supply the missing judgment. That is what I mean by labor hiding the craft: effort spent producing the artifact can look like evidence that the important decisions were made, when it tells us nothing of the sort.

The same reply handles the “enterprise level” version of the defense: fine for toy apps, the argument goes, but serious enterprise systems still demand real programming skill. Look at what that skill actually stands on. The hardest problem in enterprise infrastructure, getting a cluster of machines to agree, was deliberately packaged for exactly this audience: Raft’s paper is titled “In Search of an Understandable Consensus Algorithm,” its website says it was “designed to be easy to understand” and expresses the hope that consensus will become “available to a wider audience,” and enterprises consume it through etcd, which describes itself as “properly distributed using Raft” and is “used in production by many companies.” Nobody re-derives Paxos to ship a feature. Authentication is RFC 6749, a published framework. The rest is frameworks doing what Rails’ doctrine says out loud: thousands of decisions made once, by someone else.

So the enterprise-level skill, examined, is mostly the skill of assembling solved components under constraints. That is a real skill, and a demanding one. But notice what it is: deciding what to build, choosing between solved options, and specifying the seams. That is the skill this post says survives, not the skill it says declined. The defense concedes the thesis while trying to refute it. If enterprise programming were the hand-writing of novel algorithms, its practitioners would not be standing on Raft, RFC 6749, and a framework’s scaffolding, and they would not be reaching for an LLM the moment the novel part appears.

Checking the result means measuring it

Checking the result doesn’t mean reading the code as a peer reviewer, and it’s worth being precise about why. The fifty-year record on code review says reading diffs is a weak defect filter: Fagan’s IBM inspections measured defect-removal efficiency directly in 1976, and the studies since (AT&T Bell Labs 1987, HP 1996, Microsoft’s modern-review research) cluster informal review around 15-30% of defects found, roughly one bug in four, with the rest shipping. Bacchelli and Bird’s study of review at Microsoft found the practice survives for other reasons: knowledge transfer, team awareness, shared understanding. Defect finding was the motivation; it wasn’t the outcome.

The alternative isn’t reading harder. It’s measuring. The same scientific process I instilled in eThang Agent, my agent harness, is what checking means now: write the prediction before the change, name the command whose output would refute it, run it, record the verdict with evidence. The harness built itself; I never wrote or read a single line of its code. What I did was instill doctrine and discipline: an append-only ledger where a prediction can’t be recorded without naming its falsifying command, a verdict can’t be recorded without pasting evidence, and a finish gate that refuses to call work done while any prediction stands untested. Quality comes from that discipline, not from eyeballing the diff.

Security is the standing objection, so it deserves numbers instead of vibes. Chromium finds that around 70% of its serious security bugs are memory safety problems, and Android’s memory-safety share fell from 76% of vulnerabilities in 2019 to 24% in 2024 after moving new development to memory-safe languages (Google, September 2024). Those bugs were found by fuzzers, sanitizers, and static analysis, then prevented by changing the substrate, not by reviewers reading harder. OSS-Fuzz has found thousands of security vulnerabilities and stability bugs by guided fuzzing of Chrome components alone. The lesson isn’t that security doesn’t matter. It’s that the industry’s most serious security progress came from measurement and design constraints, not from a person reading the code and feeling confident.

None of this abolishes human review; it repositions it. When a human does read the code, the record says they should read like an inspector, not a passer: prepared, paced, and looking for a specific class of defect. But as the primary quality mechanism, reading the code is the weak instrument, and it was the weak instrument long before agents wrote any of it. The strong instruments are executable: a failing test, a named falsifying command, a fuzzing campaign, a measured defect rate over time.

The interviews were testing the wrong thing

If writing code by hand is not the skill, then the standard technical interview has been measuring the wrong thing for its entire existence. I’m not claiming originality for that observation; it’s a common thread in industry discussion. What the research adds is that the mismatch is measurable, and it was measurable before LLMs.

The cleanest experiment is Behroozi et al., FSE 2020: a randomized controlled trial with 125 participants, three interview formats (in-person, asynchronous video, voice-only), and the finding: interview performance was “reduced by more than half, by simply being watched.” The format of the interview, independent of ability, moves the score more than the skill does. A measurement instrument with that much format noise can’t be measuring the thing it claims to measure.

Schneider et al., 2019 went looking for where the interview’s predictive validity actually lives, in a study of over 2,500 mock technical interviews. The answer: the interview’s validity came entirely from the interpersonal component. Task proficiency, the thing the coding exercise is supposed to measure, didn’t predict job performance. The interview was already not measuring the skill. The LLM just made the mismatch impossible to ignore, because now a candidate can pass the exercise without the thing the exercise was pretending to measure.

Companies that still interview on coding exercises are testing the wrong thing. They were testing the wrong thing before LLMs existed. The LLM didn’t break the interview; it revealed the break.

Here is the harsh reality this history points at, and it applies to me too. Knowing a programming language really well isn’t a skill. Not in the sense people mean when they put it on a resume, lead with it in an interview, or build an identity around it. It’s not what makes people valuable, and it hasn’t been that way for a long time.

The evidence for that is old and it comes from the least sentimental source available. Google’s Project Oxygen mined years of its own performance reviews and feedback surveys to find what employees actually valued in their best managers, and technical expertise came in dead last of eight. Laszlo Bock, then Google’s VP of People Operations, told the New York Times the belief that a manager needed to be as deep a technical expert as their reports was “absolutely the least important thing.” This is a company built by and for deep technical experts, and its own data said depth in the technology was the least predictive thing it measured.

I’m not impressed with my own ability to know a language, and I’m not impressed with anyone else’s. Not because deep knowledge is fake, but because of what it’s for. Knowing a language well is table stakes for producing code, and producing code is the part the machine does. What remains valuable is everything this post has traced: deciding what to build, specifying it so a machine or a teammate can execute it, and checking the result. A person who knows TypeScript deeply but cannot do those three things is not more valuable than a person who does them well in any language. The reverse is not true.

There is a place for loving a language the way people love chess openings or typography. Esoteric interests are human and worth having. But they serve the person who holds them, not the people the software is for, and they shouldn’t be sold as professional value. The industry dressed this interest up as a skill for seventy years because the legibility boundary made it look scarce, and scarcity looks like genius. The boundary is gone. What’s left is the actual work.

Where this leaves us

The claim, restated: the thing we call the skill of programming, writing code by hand in a language, has been declining as the required skill for a long time. The history says the machine absorbs the cheap-to-check parts of the job and the skill moves to the expensive-to-check side, and that the same claim of elimination has been made, and kept, roughly once a decade since 1954. The interviews were measuring the wrong thing the whole time, and the research said so before LLMs made it undeniable.

What remains is the expensive-to-check side: deciding what to build, specifying it, and checking what you get. That is the skill. It always was.