Better AI Code Starts Here
A Maxim for Sustainable Design
In 2012, I put a phrase out into the world that has been quoted, tweeted, debated, and cited ever since:
“Remember, when designing your Web API, your data model is not your object model is not your resource model is not your message model.”
It has come to be known as Amundsen’s Maxim, an open admonishment to all API architects, designers, and implementers.
But why is this maxim important? What does it really teach us as designers and developers? And perhaps most pressing today: does it still hold in the age of AI-generated code, where machines are producing more of the software we rely on?
Now, More Than Ever
Too often, APIs collapse all four of these models into a single, rigid layer. The database schema becomes the code model, which becomes the resource endpoints, which then becomes the JSON messages on the wire. It feels efficient at first -- just map everything 1:1 -- but the cost comes later: brittle APIs that fall apart when the backend shifts, systems that can’t evolve without breaking clients, and designs that serve the producer rather than the consumer.
Here’s the point:
Data model is about storage.
Object model is about code.
Resource model is about how clients interact.
Message model is about what actually flows across the network.
They are not the same thing. Each deserves its own care, tuned to its own purpose.
This echoes Eric Evans’ insight in Domain-Driven Design: models live in bounded contexts. The structure that works for persistence isn’t the same structure you want in your domain layer and it shouldn’t be. Likewise, Roy Fielding’s REST dissertation emphasizes the importance of abstraction across layers: clients should see stable resources and affordances, not the churn of server-side implementation details.
When you respect those boundaries, APIs become more flexible, maintainable, and evolvable. Your database can change without breaking clients. Your message payloads can be optimized for real-world usage, not just database tables. And your resource model can be built around workflows and affordances -- what people and machines actually do with your API -- rather than being a CRUD-shaped mirror of internal structures.
So yes, Amundsen’s Maxim is a solid design principle. But does it really move the needle? Does separating models actually produce measurable improvements—faster time to market, lower development cost, fewer late-night emergencies? Or is it just theory dressed up as best practice?
The Hidden Cost of Maintenance
It’s tempting to think the hard work of an API ends when it ships. In reality, that’s when the real costs begin. Studies consistently show that 50–80% of the total lifecycle cost of software comes from maintenance rather than creation (Idealink). In some cases, the share is even higher: 90% or more (Mad Devs).
Studies consistently show that 50–80% of the total lifecycle cost of software comes from maintenance rather than creation.
Why? Because maintenance never stops. Databases evolve, frameworks change, business rules shift, and security expectations become more challenging. Every shortcut taken at design time such as collapsing data, object, resource, and message models into a single rigid layer, adds compounding costs down the road. Small schema changes ripple outward, breaking clients. Frontend and backend become entangled. What looked “efficient” at release turns into technical debt that drains entire teams just to keep things running.
That’s why Amundsen’s Maxim is more than a design principle; it’s a cost-control strategy. By keeping models distinct and decoupled, we can create boundaries that absorb change rather than amplify it. Backend storage can shift without touching client contracts. Resource models can evolve around new workflows. Message payloads can be tuned for efficiency without re-architecting the entire system.
The lesson is simple but often overlooked: creation is cheap, modification is expensive. Respecting the separation of models doesn’t just produce cleaner APIs, it pays dividends every year by reducing the spiraling cost of maintenance.
Ok, separating models reduces long-term costs and improves the developer experience. But we’re entering a world where more and more code is being generated by AI tools rather than hand-crafted by developers. Does the maxim still play a role in this automated future?
The AI Code Challenge
Generative AI is transforming code creation but the quality and maintainability of that code often lag behind its volume. Developers report that AI-generated code can be verbose, redundant, and difficult to understand or refactor. This complexity increases the effort required to maintain, debug, and secure it. And the warnings are piling up:
“Code written with AI is often harder to maintain and of lower quality as it’s often verbose or copy-pasted.” (dev.to)
Organizations adopting AI coding tools warn of a technical debt “nightmare” caused by bloated, redundant logic (Okoone)
AI-based code generation often falls short in maintainability, shifting effort into debugging rather than accelerating delivery (DevOps.com)
Developers on forums frequently complain that AI code creates tangled, hard-to-test structures that are slower to work with long term (Reddit)
These challenges underscore that AI-generated code isn’t inherently better. It often shifts the maintenance burden downstream, increasing costs and reducing velocity.
Without architectural guardrails like Amundsen’s Maxim, AI tools risk amplifying the very maintenance costs they promise to reduce.
This is where Amundsen’s Maxim brings critical value: even in the world of GenAI code generation:
By explicitly separating models (data, object, resource, and message), you establish clear architecture and intent—even for generated code.
This separation enables easier refactoring, testing, and validation; all essential when maintaining code that may otherwise be inscrutable.
Encouraging code generators (or prompt patterns) to generate within defined boundaries curbs redundancy, improves clarity, and reduces long-term maintenance friction.
In short, AI tools may accelerate initial development, but without architectural guardrails like Amundsen’s Maxim, they risk amplifying the very maintenance costs they promise to reduce. Training code generators to respect these distinctions isn’t just theory, it’s a practical step toward keeping AI-assisted code sustainable.
Closing Thoughts
Amundsen’s Maxim has carried forward for more than a decade because it speaks to a truth every API designer eventually learns: collapsing models feels fast at first, but it’s expensive in the long run. Separating the data model, object model, resource model, and message model isn’t just academic neatness, it’s the difference between brittle systems and resilient ones. Like Evans and Fielding taught us in their own contexts, separation creates strength.
We’ve seen why it matters: it prevents fragile coupling, lowers long-term maintenance costs, and improves the developer experience. That clarity improves not just resilience, but the daily experience of the developers who work with your API.
And we’ve seen that it still applies in our AI-driven future, where generated code can be just as inscrutable and debt-ridden as anything written by hand. In fact, the maxim may matter more now than ever: it provides the architectural guardrails that help keep even machine-produced code sustainable.
The call to action is simple: take the maxim to heart, and apply it in every design. Treat each model layer as its own concern. Respect the boundaries. Build APIs that reflect workflows and affordances rather than raw storage tables. Doing so doesn’t just improve today’s developer experience, it creates systems that evolve gracefully, withstand change, and keep costs from spiraling out of control.
If you’re looking for one principle that can improve the resilience of your code, the quality of your developer experience, and the economics of maintaining APIs, even in the age of AI. this is it:
Four models, not one.
I write Signals for people designing the next generation of APIs and intelligent systems. Each issue gives you clear examples, frameworks, and tools to use right away. Subscribe now and design smarter with AI, not just around it.


