Turn a change to your contract into a migration you can review, with the migration plan command.

Location: ORM > Migrations > Generating a migration

This tutorial takes a change you have made to your contract, the `contract.prisma` file that replaced `schema.prisma`, and turns it into a migration that you read and approve before it runs against a database. To create a project first, see the [quickstart](/guides/prisma-orm-2-prisma-orm-quickstart-postgresql).

Two commands do the work, and you run them in this order every time you change your contract. `npx prisma contract emit` replaces `prisma generate`: it reads `contract.prisma` and writes `contract.json` and `contract.d.ts` next to it, so that everything downstream reads your latest edit and not the version before it. `npx prisma migration plan` then reads `contract.json` and writes a new migration for the change, without applying it. Planning never connects to a database, which means you can run it in CI or in a sandbox with no database credentials. To work out what your change is a change from, `migration plan` looks by default at the `db` [ref](/guides/migrations-the-migration-graph#terms), a file in `migrations/app/refs/` that records the contract version you last applied in development.

## Your first migration

Say your contract has a single model:

```prisma title="contract.prisma"
// use prisma-8

model User {
  id    Int     @id
  email String
  name  String?

  @@map("user")
}
```

Keep `// use prisma-8` as the first line of `contract.prisma`. If you [split your contract across several files](/guides/contract-authoring-psl-syntax#split-the-contract-across-several-files), every one of them needs that line too. `contract emit` and the Prisma editor extension read only the `.prisma` files that start with it, and skip the others without a warning. If no file starts with it, `contract emit` fails with an error whose `code` is `CONTRACT.SOURCE_LOAD_FAILED`.

Run both commands, passing `--name init` so that the new migration directory is named `<timestamp>_init`:

#### bun

```bash
bunx prisma contract emit
bunx prisma migration plan --name init
```

#### pnpm

```bash
pnpm prisma contract emit
pnpm prisma migration plan --name init
```

#### yarn

```bash
yarn prisma contract emit
yarn prisma migration plan --name init
```

#### npm

```bash
npx prisma contract emit
npx prisma migration plan --name init
```

```text
│  contract:    src/prisma/contract.json
│  migrations:  migrations/app
│  name:        init

✔ Planned 2 operation(s)
ℹ No db ref set — planning from an empty database. Run db init, db update, or db sign if a database already exists.

migrations/app/20260707T1005_init
├─ Create schema "public"
└─ Create table "user"

from:       (baseline)
to:         2d2cec8641643159cfc94e499e5eacbd75218e0389d938139b278f68618d6b13
app space:  migrations/app/20260707T1005_init

ℹ DDL preview

CREATE SCHEMA IF NOT EXISTS "public";
CREATE TABLE "public"."user" (
  "email" text NOT NULL,
  "id" int4 NOT NULL,
  "name" text,
  PRIMARY KEY ("id")
);
```

Read the rest of that output closely, because it tells you what was planned and where it was written:

- **`Planned 2 operation(s)`** counts the steps in the migration, which are listed underneath the directory name, and the DDL preview below them is the SQL those steps run.
- **`to:`** is the hash of your contract. Each version of your contract is a contract state, and Prisma ORM identifies a state by that hash rather than by a version number.
- **`from: (baseline)`** tells you this is a baseline migration, meaning a first migration that goes from an empty database to a contract state. The word means other things elsewhere, which [Baselines](/guides/migrations-the-migration-graph#baselines) sorts out.
- **`app space:`** is the directory the new migration was written to. Your app's migrations are in `migrations/app/`, and each [Prisma ORM extension package](/guides/extensions-using-extensions) that ships migrations gets a directory of its own beside it. Prisma ORM looks for `migrations/` in the directory you run commands from, which is normally the project root.

The `No db ref set` line is telling you that nothing in this project records which contract state your database matches, so `migration plan` assumed an empty database and planned from there. That assumption is right for a new project. If you already have a database, tell Prisma ORM what is in it before you plan anything, and which command does that depends on what the database holds:

- If Prisma ORM 7 migrated the database, follow the steps in the [upgrade guide](/guides/upgrade-prisma-orm-postgresql#4-transfer-migration-ownership) first.
- If the database is empty, you need nothing: `npx prisma db migrate` creates the tables for you, though it does not create the database itself.
- If every table in the database already matches your contract, run [`npx prisma db sign`](/guides/orm-db-sign) and then read [The automatic baseline](#the-automatic-baseline) below. `db sign` checks the tables against your contract and then writes the [marker](/guides/migrations-the-migration-graph#terms), which is the record in the database of which contract state it matches.
- If the database has only some of your tables, run [`npx prisma db init`](/guides/orm-db-init). It adds what is missing and nothing else, and it stops if matching your contract would need a change that could lose data, such as dropping a column.
- If this is a development or preview database that you want to match your contract without keeping migration files for it, run [`npx prisma db update`](/guides/orm-db-update). Unlike `db init`, it will make changes that could lose data, so it asks you to type the database name first.

A migration is a directory, and there is no SQL file in it. `migration.ts` is the one you read and sometimes edit, and the `ops.json` and `migration.json` beside it are what `db migrate` reads, as [What a migration contains](/guides/migrations-how-migrations-work#what-a-migration-contains) describes. Commit all three, along with `contract.prisma`, `contract.json`, `contract.d.ts`, and any new directories under `migrations/snapshots/`, which are saved copies of your contract. The change itself is in `migration.ts`, written as a list of method calls:

```ts title="migrations/app/20260707T1005_init/migration.ts"
#!/usr/bin/env -S node
import type { Contract as End } from '../../snapshots/2d2cec8641643159cfc94e499e5eacbd75218e0389d938139b278f68618d6b13/contract';
import endContract from '../../snapshots/2d2cec8641643159cfc94e499e5eacbd75218e0389d938139b278f68618d6b13/contract.json' with { type: 'json' };
import { Migration, MigrationCLI, col, primaryKey } from '@prisma/orm-postgres/migration';

export default class M extends Migration<never, End> {
  override readonly endContractJson = endContract;

  override get operations() {
    return [
      this.createSchema({ schema: 'public' }),
      this.createTable({
        schema: 'public',
        table: 'user',
        columns: [
          col('email', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
          col('id', 'int4', { notNull: true, codecRef: { codecId: 'pg/int4@1' } }),
          col('name', 'text', { codecRef: { codecId: 'pg/text@1' } }),
        ],
        constraints: [primaryKey(['id'])],
      }),
    ];
  }
}

MigrationCLI.run(import.meta.url, M);
```

Everything the migration will do is in that `operations` list, so reading it tells you exactly what will happen, and editing it is how you change what happens. A change as simple as this one needs no edit. [Editing a migration](/guides/migrations-editing-a-migration) covers the cases where you do edit the file, such as adding a data backfill, reordering operations, or running raw SQL, and the recompile step that makes your edit count.

## The second migration: planning a delta

Apply that first migration to your development database by running `npx prisma db migrate --advance-ref db`. This is the first command here that connects to a database, and it uses [`db.connection`](/guides/contract-authoring-the-data-contract) in `prisma.config.ts` unless you pass a connection string with `--db`. The `--advance-ref db` part is what records the result: it points the `db` ref at the contract state you just applied, so the next time you run `migration plan` it starts from there and plans only what has changed since.

Now add an optional `phone String?` field to `User`, and run both commands again:

#### bun

```bash
bunx prisma contract emit
bunx prisma migration plan --name add_user_phone
```

#### pnpm

```bash
pnpm prisma contract emit
pnpm prisma migration plan --name add_user_phone
```

#### yarn

```bash
yarn prisma contract emit
yarn prisma migration plan --name add_user_phone
```

#### npm

```bash
npx prisma contract emit
npx prisma migration plan --name add_user_phone
```

```text
│  contract:    src/prisma/contract.json
│  migrations:  migrations/app
│  name:        add_user_phone

✔ Planned 1 operation(s)

migrations/app/20260707T1006_add_user_phone
└─ Add column "phone" to "user"

from:       2d2cec8641643159cfc94e499e5eacbd75218e0389d938139b278f68618d6b13
to:         967cb9f63587c1d90f64716780f04eec22b263424e89f07139233fc2c3d710c4
app space:  migrations/app/20260707T1006_add_user_phone

ℹ DDL preview

ALTER TABLE "public"."user" ADD COLUMN "phone" text;
```

To plan from a contract state other than the one the `db` ref names, say which one with `--from`, as in `--from 20260707T1005_init`. When you give `--from` a migration directory name, you are naming the contract state that exists once that migration has run. The [`migration plan` reference](/guides/migration-migration-plan#options) lists every form the flag accepts.

### The db ref: skipping --from

Once your first migration is applied, every change you make after that follows the same routine, which does the job `migrate dev` did in Prisma ORM 7 whenever you wanted migration files out of it, as the [command table](/guides/introduction-2-orm-coming-from-prisma-orm-7#commands) shows:

1. Edit your contract.
2. Run `npx prisma contract emit`. If you skip this, `migration plan` reads the `contract.json` from before your edit, so it either prints `No changes detected` or plans a migration that is missing your latest change.
3. Run `npx prisma migration plan --name <name>`.
4. Review what it planned with `npx prisma migration show <dir>`, where `<dir>` is the new migration's directory name. If you edit `migration.ts`, [recompile it](/guides/migrations-editing-a-migration) before you go any further, because until you do, your edit is not part of what will run.
5. Apply it. In development that is `npx prisma db migrate --advance-ref db`. In CI and production it is `npx prisma db migrate --to <ref>`, without `--advance-ref`, where the ref names the state that environment should reach.

The step that is easy to get out of order is the last one, and the symptom is a migration that repeats work you already planned. If you plan a second migration before you have applied the first one with `--advance-ref db`, the `db` ref still names the older contract state, so Prisma ORM plans the first migration's changes a second time, and warns that planning from that state forks the migration graph. To fix that, delete the directory of the repeated migration, run `npx prisma db migrate --to <dir> --advance-ref db` with the name of the directory whose changes it repeated, and plan again. The `--to` is needed because your contract already holds the newer change, which has no migration yet. That command applies the first migration if your database does not have it, and points the `db` ref at it either way. To plan the second migration without applying the first, pass `--from <dir>` to `migration plan` instead, which leaves the `db` ref alone. Deleting the directory is how you discard any migration that has not run on a database, and the matching directory under `migrations/snapshots/` can stay.

Commit `migrations/app/refs/db.json` with the migration files. On a fresh clone it then points at the contract state the team's development databases are at, so the first `migration plan` chains from there. Without it, a clone that has migrations but no `db` ref is the last row of the table below, and `migration plan` stops with an error. When a merge conflicts on that file, take the hash your own database is at, which `npx prisma migration status` prints next to its `@db` label, or set the ref afterwards with `npx prisma migration ref set db <dir>`.

`db migrate --advance-ref db` is not the only command that updates the `db` ref. When `npx prisma db sign`, `db init`, and `db update` change the database configured in `prisma.config.ts`, they point the `db` ref at the new state as well. `db sign` is the one to watch, because it updates the ref even when you pass `--db` to sign a different database, so add `--no-advance-ref` when you do.

When you leave `--from` off, what `migration plan` does depends on whether `migrations/app/` already has migrations in it and whether the `db` ref exists. The combinations are:

| Migrations in `migrations/app/` | `db` ref | What `migration plan` does                    |
| ------------------------------- | -------- | --------------------------------------------- |
| None                            | None     | Plans from an empty database                  |
| None                            | Exists   | Writes a baseline migration, then your change |
| Some                            | Exists   | Plans from the state the `db` ref points at   |
| Some                            | None     | Stops with an error                           |

That last row is an error because Prisma ORM has migrations but nothing recording which contract state your database matches, and it will not guess. The error prints the commands that resolve it: one sets the `db` ref, one plans with `--from`, and one plans with `--from @empty`.

### The automatic baseline

If you started from an existing database, `db sign` and `db update` leave you in the second row of that table: you have no migration files yet, but the `db` ref names a contract state. What that means in practice is that you should run `migration plan` before you ever run `db migrate`. Planning writes a baseline migration ending at the state your database already matches, plus a second migration for your own change if `contract.json` has changed since. You need that baseline because `db migrate` fails against the database until one migration's `to:` hash matches the marker, and it never runs the baseline there, since the marker already records that the database matches that state. [Baselines](/guides/migrations-the-migration-graph#baselines) has the full rule.

There is one situation where that automatic baseline is not the one you want. If a deployed database, such as production, matches an older version of your contract than your development database does, a baseline planned the usual way would end at your development database's contract state, which is not the state production matches. Plan the baseline from the older version instead:

1. Change your contract back to the deployed version, for example with `git show <commit>:<contract path> > <contract path>`.
2. Run `npx prisma contract emit`.
3. Run `npx prisma migration plan --name baseline --from @empty`.
4. Run `npx prisma db sign --db "$PRODUCTION_DATABASE_URL" --no-advance-ref` against the deployed database. It writes the marker there, and `--no-advance-ref` leaves your `db` ref alone.
5. Restore your current contract and run `npx prisma contract emit` again.
6. Plan the change with `npx prisma migration plan --name <name> --from <baseline dir>`, the baseline's directory name.

None of these steps changes your `db` ref.

## When the planner needs your input

Some changes cannot be planned in full, because the answer depends on your data rather than on your contract. Adding a **required** field with no default to a table that already has rows is the common one: `migration plan` writes the `ADD COLUMN` and the `SET NOT NULL`, but only you know what the rows that already exist should contain, so it leaves that part for you to fill in as a **placeholder**. To see one, apply `add_user_phone`, then add a required `displayName String` field to `User` and run both commands again:

#### bun

```bash
bunx prisma contract emit
bunx prisma migration plan --name add_display_name
```

#### pnpm

```bash
pnpm prisma contract emit
pnpm prisma migration plan --name add_display_name
```

#### yarn

```bash
yarn prisma contract emit
yarn prisma migration plan --name add_display_name
```

#### npm

```bash
npx prisma contract emit
npx prisma migration plan --name add_display_name
```

```text
│  contract:    src/prisma/contract.json
│  migrations:  migrations/app
│  name:        add_display_name

⚠ Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit

from:       967cb9f63587c1d90f64716780f04eec22b263424e89f07139233fc2c3d710c4
to:         866ab885a8a4bd8d9f5adc47784c068be7895a7182c55cc29f9f9967d3019484
app space:  migrations/app/20260707T1008_add_display_name
```

Open the new `migration.ts` and you will find a `dataTransform` step between the `ADD COLUMN` and the `SET NOT NULL`, which is the step that updates the rows already in the table. The two `placeholder(...)` calls inside it mark where your queries go: the first finds the rows that still need a `displayName`, and the second fills the value in. Write both with the [SQL query builder](/guides/reference-2-reference-sql-query-builder), then recompile the file, which is the step the output above calls self-emit. Until you fill the placeholders in and recompile, this migration cannot run at all, so a forgotten placeholder costs you a failed deploy rather than a wrong database. [Editing a migration](/guides/migrations-editing-a-migration) walks through writing both queries and recompiling.

## Reviewing what you planned

You can inspect a planned migration before anything runs, with these commands, none of which connects to a database:

#### bun

```bash
# One migration in detail: operations, metadata, DDL preview
bunx prisma migration show 20260707T1006_add_user_phone
# The whole migration history, with your new migration in place
bunx prisma migration graph
# Integrity check: hashes match, no files missing, every migration starts from empty or where another ends, refs point at known states
bunx prisma migration check
```

#### pnpm

```bash
# One migration in detail: operations, metadata, DDL preview
pnpm prisma migration show 20260707T1006_add_user_phone
# The whole migration history, with your new migration in place
pnpm prisma migration graph
# Integrity check: hashes match, no files missing, every migration starts from empty or where another ends, refs point at known states
pnpm prisma migration check
```

#### yarn

```bash
# One migration in detail: operations, metadata, DDL preview
yarn prisma migration show 20260707T1006_add_user_phone
# The whole migration history, with your new migration in place
yarn prisma migration graph
# Integrity check: hashes match, no files missing, every migration starts from empty or where another ends, refs point at known states
yarn prisma migration check
```

#### npm

```bash
# One migration in detail: operations, metadata, DDL preview
npx prisma migration show 20260707T1006_add_user_phone
# The whole migration history, with your new migration in place
npx prisma migration graph
# Integrity check: hashes match, no files missing, every migration starts from empty or where another ends, refs point at known states
npx prisma migration check
```

`migration check` also tells you the result in its exit code, if you run it from a script. `0` means every check passed, `4` means an integrity failure such as a missing file or an `ops.json` that someone edited by hand, and `2` means it could not find a migration you named. The one thing it cannot tell you is whether you remembered to [recompile](/guides/migrations-editing-a-migration) after your last edit. To check that yourself, stage the migration with `git add`, then recompile it. If `git status` then shows no unstaged change to `ops.json` or `migration.json`, they were already up to date. This does not work for a backfill on a model with an `updatedAt` column. [Editing a migration](/guides/migrations-editing-a-migration#worked-example-making-a-column-required) explains why, and how to write that backfill.

> \[!NOTE]
> What's early
>
> Planning covers tables, columns, indexes, constraints, and the backfill placeholder shown above. What it cannot do yet is notice that you renamed something: if you rename an optional field, it plans the change as **drop column + add column**, and `migration plan` prints a warning that the change can lose data. Take that warning seriously, because `db migrate` runs destructive operations without asking, so read `migration show <dir>` before you apply anything. To rename a column and keep the data in it, edit `migration.ts`, replace that pair of operations with a [`rawSql` operation](/guides/migrations-editing-a-migration#raw-sql) that runs `ALTER TABLE ... RENAME COLUMN`, and recompile. One more difference from Prisma ORM 7: `migration plan` never asks you questions the way `migrate dev` did.

## Prompt your coding agent

Projects created with `npm create prisma@latest` include the [Prisma ORM skills](/guides/tools-skills#available-skills-for-prisma-orm-8) for your coding agent. In an existing project, run `npx prisma skills sync`. The skills are instruction files your agent reads, so you can ask it to:

- "Add a required `displayName` field to User, emit the contract, and plan the migration."
- "Plan a migration named `add-orders-table` and show me its DDL preview before I commit it."
- "Run `migration check` and explain any integrity failures."

## See also

- [Editing a migration](/guides/migrations-editing-a-migration): fill placeholders, add data steps, write raw SQL
- [Applying a migration](/guides/migrations-applying-a-migration): run what you planned
- [Studio with Prisma ORM](/guides/introduction-11-prisma-next): once applied, see the same operations as a visual diff in Prisma Studio
- [TypeScript Migrations in Prisma 8](https://www.prisma.io/blog/typescript-migrations-in-prisma-next): a blog post on the design of `migration.ts`

## Related pages

- [`Applying a migration`](/guides/migrations-applying-a-migration): The db migrate command applies the migrations you planned, until your database matches your contract, with a preview, checks on every operation, and safe re-runs.
- [`Editing a migration`](/guides/migrations-editing-a-migration): A migration is TypeScript you own. Fill in backfills, reorder steps, or write raw SQL, then recompile it with one command.
- [`How migrations work`](/guides/migrations-how-migrations-work): Change your contract, plan a migration, review it, apply it. Operations can check the database before and after they run.
- [`Rollbacks and recovery`](/guides/migrations-rollbacks-and-recovery): Rolling back is one more migration that makes the database match an earlier contract state. Recovery is fixing a failed migration and running it again.
- [`The migration graph`](/guides/migrations-the-migration-graph): You and a teammate each changed your Prisma contract on separate branches. The migration graph is how Prisma ORM applies both changes to every database after the branches merge.

## Related pages

- [Authentication & Tools](./authentication-tools-index.md)
- [Build](./build-index.md)
- [Changelog](../changelog.md)
- [Concepts](./concepts-index.md)
- [Console commands](./console-commands-index.md)
- [Contract Authoring](./contract-authoring-index.md)
- [Core Concepts](./core-concepts-index.md)
- [Data Modeling](./data-modeling-index.md)
- [Database](./database-index.md)
- [DB commands](./db-commands-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
