Pre-LLMs, human devs often constructed codebases that struck a practical balance between codebase philosophy (ie DDD) and pragmatism. In the human world, splitting up every feature into repositories, value objects, factories, entities, etc didn’t *always* make sense
So we implemented the philosophy in areas where it mattered, and took pragmatic shortcuts in areas where it didn’t (ie direct domain model interactions in the controller vs service layer, it’s the beauty of a framework like Rails!)
I think one of the interesting findings here in the LLM era is that it can be challenging to encode these pragmatic decisions into a rules file
It’s very hard to tell an LLM, “if the feature is simple, skip the DDD philosophy and all the patterns we use and be pragmatic about it, but when it’s complex, use all established codebase patterns”
It’s much easier to say, “this codebase uses DDD. All controllers call a service, which loads an aggregate through a repository, and invokes behavior on the aggregate”
The LLM does great with those sorts of instructions.
Even though there’s a lot of extra code to write and most features don’t need all the ceremony, it feels increasingly valuable to have these consistent patterns in a codebase
Given the vast number of brand new startups funded 2025-2026, it wouldn’t be shocking to see many of these companies adopting old “over-engineered”, “overly-ceremonious”, “impractical” engineering ideas at the start of their codebases to optimize for consistent LLM outputs
It’s obviously more token costs to generate this much extra code, but the maintainability benefits seem to offset that in the long run