Domain-Driven Agents
coldtake.dev
coldtake.dev
I.e. an `Author.ts` has an `Author.md`, `Book.ts` has an `Book.md`.
For agents, we've given them a skill to read & write the `md` files:
https://github.com/joist-orm/joist-orm/blob/main/packages/co...
And so agent-written `md` updates are showing up in PRs. So far it seems useful (our main repo is a 350k LOC TypeScript monolith).
Admittedly, this is way less sophisticated (& less complicated) than the "graph of edges in/out of every bounded context" in the OP, but that is probably again my "little-d" DDD perference, where I find some of "DDD at scale" patterns lead to, imo, over-engineering.
Ideally, the code can actually help you structure that information. I've written a bunch of Haskell and OCaml like this, where the types in each module let me structure my documentation in a way that is actually easier for people—and maybe also LLMs—to track. As a bonus, it makes it more natural to keep the two in sync.
We do that :-)
https://joist-orm.io/modeling/documentation/#two-way-syncing
Technically the md file can have more free-form docs, but for the overview/fields headers that, by convention, map to the entity/field jsdocs, we do sync them over.
*_learnings.md: I am also using one more _learning.md, that captures what agents learn while building this file. it depends the complexity of the source file. mainly if the source files is about low level, protocols and so on. even separate _learnings.md also helps. so have now. _learnings.md may be needed how much investigation we have done to produce a working protocol.c.
protocol.c, protocol.md, protocol_learnings.md
My experience has been the opposite. They work well on existing projects but are not so great at new ones (unless you are just vibe coding something simple).
I think it has something to do with it being able to rely on years of established structure/conventions on existing projects that makes them better IME
The single thing that seems to have helped is that we all agreed to use OpenSpec early on, and to commit the specs alongside the code.
I have no affiliation with OpenSpec and I don’t suspect it’s doing anything unique here, but having the intent develop alongside the code in the repository seems to have ensured that agents have a more holistic view of the project.
It’s a night/day difference when I use an agent against this codebase that integrates its changes using OpenSpec and those that ignore it.
Hmm, I'm still undecided when it comes to the best approach: commit or not commit. Now I'm leaning toward the second one, as the implementation can diverge from the spec files, even though I store some impl logs in specs. As a part of feature delivery, I update the main repo docs based on the spec and code written. IMO it makes context for the LLM a bit cleaner.
If the project is not throwaway, this first set of iterations is critical, without guidance LLMs will just bolt on things and produce special-cased spaghetti, even the very-frontier models.
Once the framework is established, it's smooth sailing.
But I haven't gotten to "very large" size yet, I expect it produces another kind of coherence problems.
Once there's enough code, tests, and docs to appease the prefill gods and give it an understanding of what I expect in terms of code quality, style, and TDD etiquette, I can expect more consistent performance.
That said, I've never been comfortable enough with it to let it write more than a few of functions at a time without a diff/checking-in.
I've seen folks with 500K LOC branches written by an LLM and my heart just sinks...
One repo at my work is DDD-esque, clean architecture, modular monolith.
It suuuucks to work on. Even without AI, editing any feature requires navigating through a bunch of app, infra, domain layers instead of living in a vertical feature slice. Indirection from cargo cult design patterns we don't need, like Mediator. With AI, degradation in quality and accuracy is plainly obvious. Everything needs hand-holding. AI is dumber than our average human dev so the creaky architecture made things 10x worse
The greenfield one we started with AI a few months ago is kicking along nicely. Warts exist, there are some really large files and questionable bits, but it's way easier to work on because we didn't make the architecture some convoluted dwemer mine thingy
That gives the first few agents the "north star" and then subsequent agents have the actual code, the runtime behavior, and the user experience expectations to work against after that, in addition to the architecture docs, that provide hard boundaries to vibe off of.
They work well on existing projects if the codebase already has a good architecture and has good consistent (and prevalent) patterns.
The cost of writing code is now converging to zero, but Communicating with people and setting precise boundaries and language settings for the service is I believe that it is still an extremely remain role for developers.
My concern is that none of our services have applied the DDD pattern. I can’t even refactor the existing legacies If they could build the service again, they would have strongly insisted that everything be built using DDD patterns...
I look forward to the next article.
aside from that, i think that this is overcomplicating a more basic idea. You should have agents that leave docs behind in your codebase, and those docs should be localized to the structure of your code, but you do not need any fancy format or manifest.
We've had a lot of success with having the agents write and maintain a docs.md file in each folder, and requiring that they both read and update that file whenever they make a change in that folder
Non-ironically that's pretty much the way I work with AI agents now.
So I don't think this is really the way to go imo. Relying on AI can increase productivity but it's extremely hard to manifest.