Prisma’s db push forces a database to match a schema with no record of how it got there. I treat push as local scratch only; anything shipping goes through a real migration file.
Prisma, like most modern ORMs, gives you two ways to get a schema change into a database: migrate dev, which writes a timestamped SQL file recording exactly what changed and when, and db push, which just forces the live database to match whatever the schema file currently says, no record kept. Push is faster to run and faster to iterate with, which is exactly why it is tempting to let an agent reach for it by default when it is moving quickly through a task — it is the path of least resistance, and an agent optimising for making the current step pass will find that path reliably.
The problem is what push throws away. A migration is not just a mechanism for applying a schema change, it is a record of every schema change your database has ever gone through, in order, each one inspectable before it runs and attributable to the work that introduced it. The file does not make a destructive change reversible; dropped data still needs an appropriate recovery plan. Push collapses all of that into "the database currently looks like this," with no trail explaining how it got there, no way to replay the sequence onto a fresh environment, and no way to tell, months later, which change added a column and why.
This matters more with agents in the loop than it did before, not less, because agents move faster and touch schemas more often than a solo engineer typically would across the same stretch of calendar time. A human making three schema changes a week leaves three migrations, reviewed, in git history. An agent making three schema changes in an afternoon, encouraged to iterate quickly and unblocked by push, can leave a live database that has silently diverged from what any migration file describes, with nobody able to reconstruct the actual sequence of changes from the repository alone.
The fix is a rule, not a tool: db push is for local scratch work only, disposable, on a database you are willing to throw away, while iterating on a shape that has not settled yet. The moment a schema change is meant to actually ship, it goes through migrate dev, gets a real migration file, and that file is committed alongside the code that depends on it. A schema change with no migration attached does not ship, full stop — not because push is unsafe as a mechanism, but because a database with no migration history is a database you cannot reason about later, and later always arrives.
There is a second reason this discipline matters that has nothing to do with agents specifically: staging and production environments need to apply the same sequence of changes in the same order, deterministically, and committed migration files give me a sequence I can apply and inspect in both places. I still check the starting state and the migration outcome; a file alone cannot guarantee that either environment followed it. Push against a shared environment is genuinely dangerous in a way it is not against a local database, because two different people, or two different agents, pushing slightly different schema states against the same database can produce a result neither of them intended, silently, with no record of what happened.
I think of migrations the way I think of commits — the actual value is not the final state, which you could get to by other means, it is the ordered, attributable path that got you there, because that path is what you consult when something is wrong and you need to know when it started. An agent that reaches for db push to move faster is optimising for the next five minutes at the cost of every future minute someone spends trying to understand how the database got the way it is, and that trade only ever looks like a good one to whoever is not going to pay it.