In 1957, John Backus and his team at IBM presented a paper called The FORTRAN Automatic Coding System. The promise was in the name: you would describe the computation, and the machine would do the programming. It didn't end programming. It created far more of it — because the people writing FORTRAN no longer had to think in registers.
Every step up the ladder has looked like that. Machine code gave way to assembly, assembly to high-level languages. Each step let people write less about how and more about what. And each step moved the source of truth up a level: once compilers could be trusted, nobody maintained the assembly. They maintained the source.
Coding agents are the next step. What you hand an agent is, at bottom, a requirement: what the system should do. So it's tempting to conclude that programming is becoming natural language, and that the code is turning into an intermediate artefact nobody reads.
We think the direction is right. The conclusion is premature, and the reason why matters.
An agent is not a compiler
A compiler translates a formal language deterministically, and it preserves meaning. Compile the same source a hundred times and you get the same behaviour. That guarantee is why we stopped reading assembly.
An agent translates natural language probabilistically, and nothing guarantees it preserves meaning. Natural language is ambiguous to begin with. Give the same request twice and you can get two different programs, both of which pass their own tests.
In the late 1970s, Dijkstra wrote a short note called On the foolishness of "natural language programming". His point was that the value of a formal notation is its precision. He was right. And that's the problem teams are now living with.
What's breaking
We went through about 7,000 Hacker News comments from the past year about working with coding agents, with agents doing the first pass, and kept the 178 in which an engineer described something that actually went wrong for them. The most common complaints:
- Code that works today and nobody can change tomorrow. Logic duplicated at the wrong layer, the repo's design ignored, a feature another team had to rewrite before it fit. This was the single most frequent theme.
- Review that can't keep up. Agents produce more than people can read, and review turns into a bottleneck or a rubber stamp.
- Someone else's AI output landing on you. Large generated changes from colleagues, which you're expected to review, fix or own.
- Losing your grip on your own codebase. You merged it, but you no longer hold a mental model of it.
- "It passes" isn't "it does what was asked." The code compiles, the tests are green, and it solves a slightly different problem.
None of these is fixed by better prompts. They're what happens when the source of truth moves up a level but nothing guarantees the translation.
Requirements become the source code
If requirements are where intent now lives, they need everything source code has needed for fifty years:
- Versioning. Every change to a requirement is a recorded revision, not an edit to a chat history.
- Review. A change to what the system should do is checked before anyone builds on it.
- Tests. Each requirement carries acceptance criteria precise enough to check, not adjectives.
- Traceability. Every line of code can answer: which requirement am I for?
That isn't "write a long spec, then generate". Heavy specs have their own failure mode. One engineer in our sample described a thirty-minute fix that turned into a 37-page AI planning document and a week of process. Governance is not more paperwork. It's making intent precise, keeping it in one place, and checking every output against it.
How we do it
We built DynoKore Keel as the record behind that work: spec-driven development, with the spec kept as linked records in a database instead of markdown files.
One source of truth. Requirements, acceptance criteria, decisions and tasks are versioned records with typed links, and each requirement links back to the passage it came from. Changes go through reviewed changesets, and Keel won't let the author of a change approve it. Each task gets a short context built from its links, with the rules it must follow always included.
Changes reach the work. When a requirement or criterion gets a new revision, the links below it are marked for review, so tests and tasks written against the old version are found instead of forgotten.
Done on evidence. A task is offered only when its requirements and design are ready, and the server marks it done from the merged change and its CI verdict, not from an agent's report.
Keel doesn't decide how the agents work; that part is ours. On DynoInvoice, a separate reviewer agent checks every merge request and the agent that wrote the code never reviews it; questions about how a feature should behave, exemptions and relaxed checks go to a person, and routine approvals are delegated to agents; and the project's own CI enforces its engineering standards, such as library modules depending only downward.
Notice what isn't in that list: a person reading every line. We couldn't, and neither can you. The work is in making that safe.
What we got wrong
We learned the hard way that a sentence in a prompt is not a control. We told our agents never to print credentials. Three times, a credential fragment still ended up in an agent's output. The fix wasn't a better prompt. It was mechanism: tools that need credentials read them in their own process and never print them, and a hook now blocks reading credential files at all.
That lesson generalises. In a governed system, a mistake that happens twice gets a mechanism, not a reminder.
And we should be honest about scope. DynoInvoice, the system we've built this way, is weeks old, not years. We can show you how it's structured and how every change was checked. We can't yet show you what it looks like after five years of maintenance. Nobody can, for agent-built systems. That's exactly why the structure matters now.
Where this goes
Will code eventually disappear from view, the way assembly did? Maybe, but only once checking an agent's output becomes as trustworthy as a compiler. Until then, the job isn't to stop engineering. It's to move engineering up a level, to the requirements, and keep the checks underneath honest.
If your team is already shipping agent-written code into a system you'll maintain for years, three things you can start tomorrow:
- Write down what "done" means before the agent starts, in terms a test can check.
- Never let the agent that wrote a change be the one that approves it.
- When a mistake happens twice, replace the reminder with a mechanism.
Vibe coding is a fine way to build a prototype. A system you'll run for years needs something else: governed development.
Every requirement. Every decision. Every proof.
We're opening Keel to a small group of design partners: engineering teams already building with agents who need to answer for what they ship. dynokore.ai
Top comments (0)