Skip to content

TML-3474: shape the migration-statements project: the planner refuses data loss, and the user states renames, deletes, conversions and backfills on the command line - #30604

Closed
wmadden-electric wants to merge 4 commits into
mainfrom
tml-3474-migration-statements

Conversation

@wmadden-electric

Copy link
Copy Markdown
Contributor

At a glance

A user renames the Profile model to User, renames its name field and removes the Legacy model in the contract source, then says what they mean on the command line:

$ prisma migration plan --name tidy-users --rename Profile:User --rename User.name:User.fullName --delete Legacy
✔ Planned 5 operation(s)
  Rename table "Profile" to "User"                            widening
  Rename constraint "Profile_pkey" to "User_pkey"             widening
  Rename column "User"."name" to "fullName"                   widening
  Rename constraint "User_name_key" to "User_fullName_key"    widening
  Drop table "Legacy"                                          destructive

Without --delete Legacy, the command refuses and prints the statements that would resolve it:

✖ The plan would lose data. State what you mean about each operation:
  Table "Legacy" would be dropped and its rows lost.
    To rename it:  --rename Legacy:<new name>
    To delete it:  --delete Legacy

Today the same change plans dropTable "Profile" plus createTable "User", and the rows are gone after one consent prompt that covers the whole plan.

What this PR changes

This is a shaping PR. It adds projects/migration-statements/ with the project spec, the design notes and a plan placeholder. No code changes. Merging it records the design so the slices can reference it; the slices follow as their own PRs.

The problem

The migration planner diffs two states, a destination contract and an origin schema, and cannot tell intent from the diff. A renamed model looks exactly like a dropped table plus a created one. Today the planner plans the destruction, and the only protection is a consent prompt that covers every destructive operation in the plan at once, or a hand-written migration.ts. Projects that use db update and keep no migration history cannot hand-write anything.

The decision

Two things become true.

The planner refuses data loss unless the user has said what they mean about that specific operation. Destructive means data is lost: dropping a model or field, deleting a value rows still carry, or a type change the database cannot keep. Drops of indexes, constraints, checks, policies and defaults lose nothing and are widening. db update already refuses through its consent prompt; migration plan gains the same refusal for every plan, not only auto-baselines.

The user says it on the command line, in the vocabulary of their contract. Four verbs, rename, delete, convert and backfill, over four nouns, namespace, model, field and value, addressed as the contract source names them: User, User.name, Status.ARCHIVED, Address.street. The framework resolves the coordinates against the origin and destination contracts; the family turns the resolved statement into storage operations, so the same --rename User.name:User.fullName renames a column on Postgres and rewrites documents on Mongo. Nothing on the command line is a table, a column, a SQL expression or a literal value.

A statement is intent, not a hint. One that cannot be resolved or applied is an error and nothing is planned. --delete is per-operation consent and replaces the blanket --confirm. --convert and --backfill scaffold the placeholder migration that migration plan already knows how to write, with the placeholder in the operation that uses it, such as the using expression on a Postgres type change; the user fills it in migration.ts and re-emits, as today.

The design notes carry the full verb and noun matrix with what each statement does per family, sixteen scenarios that stress the model, a walkthrough of a type change with its evaluation, and a survey of how Prisma Migrate, ActiveRecord, Django, Drizzle, Atlas and Terraform handle the same problem.

Relationship to the shelved attempt

The first attempt, #30557 and #30570, stated intent in the contract source with @@hint(was: "Profile"). It was shelved on 2026-10-05 as unintuitive. Its planner substrate, a working schema shared by the planner and the hand-written facade, constraint names taken from the destination contract, and the reclassification of non-data drops as widening, is reused by the first slice. Its contract section is not.

Alternatives considered

  • Intent in the contract source, as Atlas's renamed_from and Terraform's moved block do. Shelved: unintuitive, and it forces rules about spent and ignored hints because the statement must survive until every environment has consumed it.
  • An interactive prompt over detected drop-and-create pairs, as Django and Drizzle do. Rejected: only works in a terminal, only when a candidate exists, and detection is unreliable. The refusal message does the prompt's job without guessing.
  • Conversion expressions or fill values on the command line. Rejected: users cannot be expected to type SQL on a command line, and a JSON literal has no type, which the data-type and cast design exists to fix. The command line carries intent only; the value goes in the migration file.
  • Keeping --confirm beside --delete. Rejected: two consent mechanisms confuse which one is required, and blanket consent is what the project removes.

Refs: TML-3474

Agent: scylla-59

🤖 Generated with Claude Code

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@wmadden-electric
wmadden-electric requested a review from a team as a code owner October 5, 2026 15:39
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (4)
  • projects/migration-statements/README.md is excluded by !projects/**
  • projects/migration-statements/design-notes.md is excluded by !projects/**
  • projects/migration-statements/plan.md is excluded by !projects/**
  • projects/migration-statements/spec.md is excluded by !projects/**

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration
  • Configuration used: Repository: prisma/orm/.coderabbit.yml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ec283f8b-9a2b-4b0b-9e21-ea9b23d350e8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

wmadden-electric and others added 3 commits October 5, 2026 17:41
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@wmadden-electric

Copy link
Copy Markdown
Contributor Author

The project spec, design notes and plan in this PR land on main through #30638 (slice 1), which was retargeted to main after its approval and carries these two commits. This PR closes as superseded once that merges.

@wmadden-electric

Copy link
Copy Markdown
Contributor Author

Superseded by #30638, which merged with the project folder (projects/migration-statements/) on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant