Writing clean and maintainable code has arguably become very cheap. Our job as software engineers and architects shifts to the next layer: making the code fit the domain and resemble the business process.
Having experienced LLMs and harnesses operating on large codebases, moduliths and legacy big balls of mud, I’ve learned that if you don’t review their output thrice, you might overlook some rough and bold ideas, like throwing away assumed domain boundaries or sneaking in circular dependencies. As someone experienced with the codebase and the domain, writing it myself would probably have prevented that. But for velocity’s sake, we don’t want to go back to that. So the question arises: why does the LLM not simply adhere to patterns that are clear to an engineer familiar with the codebase and the domain? It’s obviously thinking and reasoning. It can explain the codebase to me just fine.
My opinion on this: we as engineers now have to welcome, maybe even promote, the agent in the architecture. An agent-first architecture. We, who ideally should be at least adept at the domain we write the code for, should do everything possible to pre-cook every meal for the agent, so its precious tokens are spent on coding, not on exploring the codebase. That means a ubiquitous language to reduce ambiguity, and an opinionated architecture that makes strong use of determinism (such as ArchUnit tests) and agent guardrails like rules.
Where agent tokens go
We already know that context doesn’t scale linearly: reliability drops as the context grows1, and models work with a finite attention budget2. This implies we have to ensure a high signal-to-noise ratio. Make every token count. Inducing domain concepts from code is expensive.
If humans already spend more than half of their time understanding code3, chances are agents have the same problem. It’s just more visible in the form of tokens.
Implicit vs. explicit
I’ve seen my agent fail abhorrently at inducing the rules and policies of a domain.
I am working in the Transactions business area of Kleinanzeigen. My agent often tries to sneakily convince me that we shouldn’t throw an exception when encountering a null price (e.g. if you sell an item only on Verhandlungsbasis) but rather remove the policy that keeps those items ineligible for our payment product and simply include them. Business would probably love this agent! As for our codebase, something this obvious to me is not obvious to the agent. Brave and bold, and honestly, that’s on our software for allowing the agent to think like this.
Policies live in three places: explicitly in the code, implicitly in the code, and outside the code. The agent can only work effectively with the first. You could argue that the latter two also work (or not, as per the example above), but in my eyes only in an extremely limited way, maybe for generic domains that don’t need a lot of expertise and are well documented in the outside world.
Ubiquitous language and boundaries
The agent needs a clear vocabulary, otherwise it will once again spend tokens trying to figure out whether “shop” and “store” are meant to be the same concept. Would a new joiner get confused by your domain concepts? Your agent probably will be too, but it might lie about that (well, some people do too, touché). It gets worse when the same word legitimately means different things in different contexts: an “order” in shipping is not an “order” in payments. That’s exactly what bounded contexts are for.
A consistent vocabulary per context makes it easy to shrink what the agent needs to load.
Then there’s determinism. Kelsey Hightower half-jokingly coined “zero-token architecture”4 for rebranding plain old automation, but I find the principle really cool when applied to development: replace agent work with determinism wherever possible. Tools like ArchUnit5, or Spring Modulith’s module verification6, send the agent a clear signal when it violates dependency rules. Maybe your supplier suddenly becomes the downstream system. An engineer might have caught that in a code review, maybe not at all. A simple, deterministic test checking module dependencies will do the job just fine. (Here I assume that code dependencies match upstream/downstream relationships. That mostly holds, but not always!)
Architecture cosplaying as Google Maps
You know how to finally build that pick-up-drop-off feature for shipping packages? Great! But how do you map it onto the code? Let’s make that as easy for the agent as possible. A strongly opinionated architecture with a vertical approach (rather than a horizontal, classical n-layer approach) narrows the solution search space and lets the agent follow the pattern. Well-structured features compound into a golden blueprint for the agent to build the next feature in a similar manner.
A good starting point could be clean architecture, with a strong opinion on separate high-level use cases rather than generic services orchestrating everything. To be clear: the use case is the slice, and the dependency rule applies within it. The layers are not your top-level folders. Add tactical DDD to further shape the domain objects, especially aggregate boundaries.
I’m not the only one thinking along these lines. Jimmy Bogard is pitching vertical slices as guardrails for AI development7, including architecture tests and a reference slice worth copying, and Three Dots Labs argues that DDD matters more when AI writes your code8. What I want to add is the token angle: apart from hygiene, having an opinionated architecture shapes where your agent spends its tokens: being a part-time archaeologist, or building your pick-up-drop-off feature.
Limitations
Best practices are still emerging, regardless of folks shilling AI masterclasses and certifications left and right. I can’t put much science and data behind this one yet, but I’m putting my bets on DDD techniques and similar frameworks becoming important again.
I’m not saying every small microservice needs as much orchestration as I might have described. But domain-rich monoliths or moduliths will for sure benefit from this. In any case, let’s not over-optimize ourselves to death yet. And if you do, please measure it, for the industry’s sake.
Closing words
I believe I just wrote a big advertisement for DDD. I hope it gets more traction again, as it is an ideal blueprint for keeping your business policies and domain knowledge explicit and agent-accessible, and in the end, for creating agent-first architectures that keep Claude and co. as productive as possible.
My view is that we as software engineers now have to focus on mastering the craft of shaping business processes into a fitting software architecture. Not that this was ever unimportant, quite the opposite. But as writing code gets cheaper, understanding and translating domain knowledge becomes even more vital.
https://research.trychroma.com/context-rot
https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents
https://doi.org/10.1109/TSE.2017.2734091
https://www.theregister.com/on-prem/2026/04/08/rebrand-automation-as-zero-token-architecture-to-master-ai/5221999
https://github.com/TNG/ArchUnit
https://docs.spring.io/spring-modulith/reference/verification.html
https://sessionize.com/s/jimmy-bogard/vertical-slice-architecture-effective-guardrails-f/185766
https://threedots.tech/post/ddd-and-ai-coding/


Tim, the null-price example is the clearest argument for making business policy executable. A dependency test can stop a payment module from importing the wrong module, but it cannot tell an agent that a negotiable-price listing must remain ineligible for payment. I would put that rule beside the use case as a named invariant, with one positive example, one rejected example, and an owner who can approve a policy change. Then a feature slice gives the agent a route through the code while the invariant supplies a stop condition. If it proposes to accept a null price, the test should fail with the domain reason, not merely a generic assertion error. I would also version the rule with the business decision record, so a legitimate change is reviewed as a policy change before it becomes a code edit. Which domain policy in your transactions area would you encode first to test whether this actually reduces architectural review time?