The most honest thing written about AI coding agents this year is a blog post that opens by admitting the failure. In Domain-Driven Agents, a software engineer describes what happens when you point an LLM at a system that has been shipping for four years instead of a greenfield repo. Ask for a “job offer status” field in a fresh project and you get one. Ask for it in a codebase with heavy dependency trees and strong coupling, and the model invents a fourth spelling of a concept that already exists three times, because the codebase itself never decided which one was real.

The author’s diagnosis is sharp: the model is not what needs upgrading. The code is not ready. Every wrong guess, every invented adapter where a direct call was fine, every call straight through where an adapter was the point, is a question about the system that the system does not answer anywhere. The model guesses because the codebase forces it to. The post then lays out a practical, incremental system for making legacy code legible to agents, built on domain-driven design and a per-repo manifest. It is the most concrete answer to the brownfield-agent problem I have seen, and it deserves serious attention from anyone building agentic tooling.

The economic insight at the core of the post is worth pausing on. The author splits engineering work into two halves that have never had the same price. Deciding what should change and why has stayed about as expensive as it always was. Typing it out has collapsed. An LLM will do the mechanical half of a cleanup, the extracted module, the refactor across two packages, the extra test coverage, at a cost that no longer resembles 2020. That split is real, and it reframes the entire debate about AI replacing engineers. The deciding part is still human. The typing part is now nearly free.

The author borrows vocabulary from John Ousterhout’s A Philosophy of Software Design and bends it deliberately. Ousterhout’s tactical versus strategic distinction describes two attitudes while coding. Here it describes a split of authorship. Strategic work is reading the system, working out what has to change and why, and whether the change serves the feature. Tactical work is carrying that decision into the files. The first needs the system in your head. The second got cheap. The author does the first fully involved, then acts as reviewer rather than implementer on the second.

The workflow is refreshingly concrete. Every repository carries a .workflow.json at its root, a manifest that tells the tooling what the repo is: which languages it holds, which directories an agent should read first, which checks must pass before work can ship. One block declares the domain, and that declaration is the only registration a repo needs. No second registry to drift out of sync. The block names the project, its bounded contexts, where each context’s glossary lives, its subdomain type, and every edge to a neighbouring context.

The example comes from the author’s own project, job-offer-box, a job application tracker built as two repositories: hyperion, a Rust backend, and a web frontend. The frontend’s manifest declares a single edge to the backend, and the detail is where the system earns its keep. The edge specifies direction (outbound, the web repo calls the backend), owner (the backend’s model wins on disagreement, so supplier), and pattern, picked from a closed vocabulary. Here the pattern is unclassified, because the web repo does two different things at once: it accepts the backend’s shape as-is when writing and translates it into its own shape when reading. The note spells out that a single label would be right about one case and wrong about the other.

Beside the manifest sits a CONTEXT.md per context, a living glossary with the precise meaning of every term and the deliberately rejected synonyms. Two files per context, both owned by the repo that owns the code. Nothing above them is authored. The context map, the one document showing every context in the portfolio and every edge between them, is derived. A generator walks every repo on disk, unions the domain blocks, and emits a single CONTEXT-MAP.md. The map is disposable and regenerable.

The precision this buys is exactly what an agent needs. Point it at the web repo and it knows that renaming Job Offer there belongs to the backend, that the adapters on the read path exist on purpose, and which words it is allowed to invent. With the map the model knows which context it is in. With the glossary it knows the words used there. The codebase finally answers the questions the model was guessing at.

The cleverest part is the disagreement check. Every edge is declared twice, once from each side, and that duplication is the whole point. A supplier names its own stance as published-language. A consumer names its own as conformist or anticorruption-layer. The generator cross-checks the pairs against a pairing table. The author runs it as a skill at three moments: after touching a manifest, when onboarding a repo, and before changing anything another context depends on. Each disagreement becomes a finding, filed as a DDD issue on the repo that owns the wrong side. The issue carries a fingerprint, so a re-run after a half-fix updates the same issue instead of opening a second one, and a finding that no longer appears closes its issue. From there it follows the same spine as everything else: an issue, an agent, a PR, the author’s review.

This is the part that should make agent-tooling vendors nervous. The author is not using a fancy agent framework. The system is a markdown file of instructions loaded when a task matches, plus separate model sessions with fresh context and narrow jobs, plus a few scripts. The skills and sub-agents are described almost in passing. The real work is the manifest, the glossary, and the discipline of declaring edges twice. The agent is the least interesting part of the pipeline.

The post is honest about what it does not solve. The strategic layer settles where a context ends and how it talks to its neighbours: the shape of the map. The inside of any single context is still ordinary code that lets you build a nonsense object and save it. The author’s next step is migrating one context at a time to a real domain model built from DDD primitives: value objects, aggregates, domain services. That process makes the codebase answer the questions the model was guessing at, one context at a time.

There is a broader lesson here for the industry. The current wave of agent hype treats context windows and tool-calling as the hard problems. This post suggests the hard problem is upstream: most codebases do not contain the answers agents need, because the code never decided what its words meant. No amount of model capability fixes that. The fix is making the code ready, and readiness is something you can build incrementally, piece by piece.

The author says they will share the whole system shortly, with the skills ready to use. That release will be worth watching. In the meantime, the post stands as a working counterexample to the claim that agents are blocked on model intelligence. The block is the code. The code can be fixed.