A payment button shouldn't drop your database
Here's a story you may have lived. You build a genuinely useful internal tool with an AI builder — a request tracker, a little admin panel, something your team actually opens every day. It works. Then you type one more sentence: "add a button to take a payment." A minute later the tool is throwing 500s, your records are gone, and the schema that held everything together has been quietly rewritten. One small ask, total collapse. This isn't bad luck. It's the predictable result of how most AI builders are built — and it's exactly the failure Doric is designed to make impossible.
The button was never the problem
A payment button looks trivial, but it is never just a button. To take a payment you need somewhere to record it: a payments table, a link from a payment back to an order and a customer, probably a status column, maybe a change to orders so it knows it's been paid. In other words, the "simple" request is really a request to reshape the data your whole app already depends on — while the app is live and full of real records.
That is one of the most delicate operations in software. Done carelessly, it doesn't fail politely in a corner. It takes the schema with it, and everything that reads from that schema goes down at once.
Why AI builders do this
Most AI builders don't hold a durable model of your system. They hold a chat. When you ask for the payment button, the model sees your latest messages and whatever code happens to fit in its window, and it regenerates. It doesn't remember why your orders table looks the way it does, because it never recorded the reason. It doesn't know what else reads that table, because it never built a map of dependencies. And a migration — the careful, ordered set of steps that changes a live database safely — is to it just more text to emit.
So it does the most natural-looking thing: it rewrites the schema to match the new code it just wrote. Drop a column here, recreate a table there. In a throwaway demo that's fine. On a database with your real data and a dozen quiet dependencies, that's the whole thing coming down. The model isn't malfunctioning — it's doing exactly what it can see, with no view of what it can't.
The real defect: the change had a blast radius the tool couldn't see. It treated a global, irreversible operation as if it were a local edit — because it had no model of the system to tell it otherwise.
How Doric makes this a non-event
Doric doesn't try to make the AI "more careful." Carefulness isn't a property you can prompt for. Instead it removes the conditions that let a small change become a catastrophe. Four things do most of the work.
1. It reads the map before it touches anything
Doric keeps a live model of your project — intent, architecture, components, and the real dependency graph of what reads and writes what. Before it changes the schema, it can see that orders is read by the dashboard, the export, and three other places. The change is planned with that knowledge, not in spite of it. Nothing gets edited blind.
2. Schema changes are expand-only, behind a contract
Your data sits behind a contract, and changes to it are expand-only by default: add a payments table, add a nullable column, backfill — but don't drop or rewrite what's already there and load-bearing. The additive path is the easy path, and the destructive one is blocked rather than merely discouraged. A payment button becomes a new table sitting next to your data, not a rewrite of it.
3. It remembers why
This is the part chat-based tools structurally can't do. Doric records the reasons behind your system — why the schema is shaped the way it is, which decisions are settled — and carries them forward. It doesn't flatten a column it doesn't recognise, because it knows that column is there on purpose. It's the difference between a tool that regenerates your app and one that remembers why it built it.
4. Done is proven, not promised
A change isn't "done" because the agent says so. Doric verifies it — the migration runs against a check, the things that depend on the schema are confirmed still working — before the change is allowed to land. A migration that would have dropped your data fails that check and never reaches your real database. And on the rare occasion something still looks wrong, you can roll the whole project back to any earlier point, because state is snapshotted, not hoped for.
What "dependable" actually means
Dependable doesn't mean nothing ever surprises you. It means a small change can't quietly become a large one; that destructive operations are blocked, not narrowly avoided; that the system remembers why it is the way it is; and that anything that does go wrong is reversible. You should be able to type "add a payment button" on a Friday afternoon and not hold your breath.
That's the whole point of building this way. Build software that doesn't rot — and trust that next week's small ask is still a small ask.
Related: why your AI thread rots → · ← All engineering notes