There is a moment in every project where the schema design dies. Not when it’s drawn - drawing it is the fun part - but at the handoff, when implementation starts. The diagram says subscriptions has nine fields; someone now has to retype those nine fields as an ORM model, then do it again for fifty-nine more entities. From that day forward the code is the model, the database evolves through migrations, and the diagram becomes a souvenir.

The tooling world’s answer is to regenerate the diagram from the code, and the duct tape shows. Prisma users reach for a diagram generator and find schema generation fails under WSL because of a puppeteer dependency three layers down. Rails users run rake erd into a Graphviz-is-not-in-your-path error that persists after Graphviz is installed. Even when these pipelines work, they emit an auto-laid-out artifact - a rendering, not a design.

It doesn’t have to work this way. Here is the full loop from a real project - about sixty entities, designed once, shipped context by context - where the diagram stays the design the whole way through. It is an ERD single source of truth workflow in four phases, and at no point does the ERD write a single statement to the database.

Phase 1: Design the whole model up front

Before any code, the entire schema goes onto the canvas: entities aligned, relationship lines routed so they read cleanly, and - this is the load-bearing part - legends drawn around each group of entities to mark the bounded contexts. Identity, catalog, billing, fulfillment: each becomes a named, colored region, and coloring the entities makes every context traceable by hue. This is domain-driven design database schema work in its natural habitat - you are deciding where the seams are while moving boxes is still free.

Design up front does not mean design perfectly. It means capturing everything you currently know about the domain in one picture, so that implementation starts from a map instead of a blank file. Sixty entities sounds heavy; on an infinite canvas with locked legends - drag the region and everything inside moves as one block - it is an afternoon of thinking made visible.

Phase 2: Implement one context at a time, with AI writing the code

Now the trick that makes the design executable. Right-click a legend and Schemity exports the SQL for every entity inside it - the whole bounded context as CREATE TABLE statements in one action. That SQL, plus one line of instruction (“this is the billing module, generate the models”), goes to Claude Code, which writes the ORM models - and sometimes the CRUD layer - for that context.

Then the direction of authority flips, deliberately: migration files are generated from those models and applied by the project’s own source code, the way the ORM intends. The ERD provided the spec; the code owns the database from here. Notice what the diagram tool did not do - it never connected as a writer, never applied DDL. The bounded context ERD was the prompt, and a context at a time turns out to be the right unit for AI codegen too: small enough to fit in a model’s head, large enough to be a coherent module.

This is a different intersection of AI and ERDs than chatting with a model to design your schema - here the human designed, and AI does the typing. What they share is the privacy shape: private AI database design means your schema goes only where you point it, from a local file on your machine.

Phase 3: Connect to the live database and arrange it once

With the first contexts migrated into the local database, create a live connection and reverse-engineer it. This produces the second diagram of the project - the connected one - and yes, it arrives unarranged. Arranging it is the one repeated cost this workflow pays, and it is cheap for a reason worth stating: the original design is sitting in the next tab as the reference. You are not deciding a layout, you are applying one that already exists. Recreate the legends, group the contexts, and from this point the connected diagram is the living mirror of the real schema.

Phase 4: Refresh, drag, repeat forever

Eventually the code outgrows the original design - it always does. A new table A belongs in context C1, so it goes into the ORM model first, the generated migration runs, and then the update to the diagram is the whole point of this post: open the ERD (or hit Reset ERD), and re-sync pulls the fresh schema with the live database as the source of truth. Table A is simply there, waiting to be dragged into C1. Everything already arranged keeps its layout; anything dropped from the database is gone from the picture. Updating the diagram stops being a job and becomes a drag gesture.

From here the loop is closed: design informs code, code migrates the database, the database refreshes the diagram, and the diagram - grouped back into contexts - informs the next round of design.

The trick: let the diff write the prompt

Adding a table is the easy change. The subtler case is modifying what exists - splitting a field, tightening a type, hanging a new constraint on an old column. The default move is to describe the change to Claude Code in prose and let it edit the model files, and then you inherit a review job: did it interpret “split the address out of customers” the way you meant?

There is a better move, and it uses the diagram as the editor. Make the change on the connected ERD - the same gesture as designing - and press Save. Schemity compares the edited diagram against the live database and shows the migration SQL diff: the exact ALTER statements, in your engine’s dialect, that your edit implies. Now don’t apply it. Copy that SQL, hand it to Claude Code along with the reason for the change, and it updates the ORM model precisely - because a SQL diff is an unambiguous spec in a way prose never is. The review burden collapses: you already saw the intended change, statement by statement, before the AI touched a file.

Schemity's Migration confirm dialog after adding an address field to the users entity on the canvas - the generated ALTER TABLE statement with a copy button, and a Cancel button next to Apply SQL statement; in this workflow you copy the SQL for Claude Code and cancel

From there the loop runs as always: the ORM generates its migration files from the changed model, your source code applies them, and the next refresh brings your own change back to the canvas through re-sync - confirmed by the database itself. One design gesture became a precise diff, the diff became the prompt, the prompt became code, and the code migrated the database. You never left the canvas to specify the change.

The ERD never writes to the database

Run the whole loop again in your head and count the write paths to the database. There is exactly one: the ORM’s migrations, executed by your source code. The ERD reads - at reverse-engineering, at every re-sync - and never writes, not because the tool couldn’t, but because the workflow makes it unnecessary. Even the migration diff, the one feature built to write, spends its life here as a messenger: a generated spec you hand to the AI, never a statement you run. That asymmetry is what lets you point a diagram tool at a database without the low-grade fear that a canvas edit becomes a DDL statement.

It also explains why this works as an offline ERD tool workflow with nothing to configure: both diagrams are plain JSON files in the repo, committed alongside the models they specified, so the design history and the code history live in the same Git log. No sync service, no diagram server, no write credentials - a desktop ERD tool, a local database, and an AI assistant that receives exactly the SQL you right-clicked.

Design first, so the thinking happens while change is free. Ship by context, so every unit of work has a boundary. Let the database drive the diagram after that, so the truth maintains itself. Sixty entities in, the diagram is still the design - which is all anyone ever wanted from an ERD.