The first service an AI coding agent writes for you usually looks great. The second looks fine. By the fourth, you have several ways of handling errors, a couple of logging formats, and two opinions about where validation belongs. Nobody decided that. It just happened, one reasonable-looking pull request at a time.
That is drift, and it is the main reason AI-built backends get harder to work on just as they start to matter. This post looks at why it happens, why it gets worse rather than better, and what actually stops it: keeping the structure of your system in one place and letting AI do the parts it is good at.
Short version: drift happens because every AI session starts from a partial view of the system, so each service is a fresh guess at the conventions. The fix is to describe the structure once, generate it the same way every time, and use AI for the logic that lives inside that structure.
What drift looks like
Drift rarely shows up as a bug. It shows up as small, individually reasonable differences between services. A typical three-month-old AI-built platform has some version of this:
- One service returns
snake_caseresponse fields and another returnscamelCase, written by the same developer two months apart. - A user ID is a UUID in two services, an integer in a third, and a string in a fourth.
- Authentication is a decorator in one service, dependency injection in another, and hardcoded inline in a third.
- Nobody wants to change any of it, because no integration tests actually run in CI.
Any one of these is easy to fix. Together they mean that every cross-service change, such as adding the same event to all of the services, turns into archaeology.
Why it happens
- Each session sees part of the system. An agent working on service four may be shown service three, or none of them. It cannot follow a convention it has not seen.
- Generation is not deterministic. The same request produces different code on different days, so even identical prompts do not guarantee identical structure.
- Conventions live in people's heads and in old code. The agent copies whichever example it saw last, which may be the one a reviewer would have rejected.
Why it gets worse, not better
New services are modelled on recent ones, so small differences get copied and multiplied. Reviewers stop noticing them once they are everywhere. And the cost arrives late: a team that could add an event to three services in an afternoon finds, at service eight, that the same change touches eight different implementations of the same idea.
What does not fix it on its own
- Longer prompts and rules files. They help, but they are still instructions to a non-deterministic generator that starts every session fresh. They reduce drift; they do not remove its cause.
- More review. Review catches drift after it exists. At the speed AI writes code it becomes the bottleneck, and it only ever catches what the reviewer remembers to look for.
What does
Put the structure of the system in one place and generate from it. That is the idea behind Datrix, our compiler for backends:
- You describe the system once in a purpose-built DSL.
- A real compiler validates the whole spec, including cross-service types and event contracts, before any code is emitted.
- Generators produce the services, migrations, infrastructure, and clients from that one source, and the same input always produces the same output.
- A change to an entity is a change to the spec: regenerate, and every service that references it updates consistently, with matching migration scripts.
AI still has a job, and a better one. At the spec level the whole architecture is small enough to fit in context, so an AI assistant can reason about every service and event at once. Inside the generated structure, it writes the focused business logic it is good at. The Datrix case study shows what that looks like on a real platform.
How to measure drift in your own codebase
You do not need a tool to start. Make a table with one row per service and one column for each convention that matters: field naming, ID type, error response shape, authentication mechanism, logging format, and pagination style. Fill it in from the code. Any column with more than one value is drift, and the number of distinct values is a rough measure of how far it has gone. Run the check every few services, and before you add the next one.