Exercise 2: Adding a Required Column to a Live Table — Possible Solution ========================================================================== Why not just add it? "excerpt String" on a table with rows fails: existing rows would have no value. A default like @default("") avoids the error but leaves every existing post with an empty excerpt, which is meaningless. Plan (each step is its own deploy): Deploy 1 — expand schema.prisma: excerpt String? $ npx prisma migrate dev --name add-post-excerpt SQL: ALTER TABLE "Post" ADD COLUMN "excerpt" TEXT; Code: unchanged. Old and new code both still work. Deploy 2 — write it Code: createPost and updates now always set excerpt, e.g. the first 200 characters of the content, trimmed at a word boundary. Deploy 3 — backfill old rows (data migration) $ npx prisma migrate dev --name backfill-post-excerpt --create-only migration.sql: UPDATE "Post" SET "excerpt" = LEFT(COALESCE("content", "title"), 200) WHERE "excerpt" IS NULL; (For a very large table, run the same UPDATE in batches from a script instead, e.g. by id range, to avoid long locks.) Deploy 4 — make it required schema.prisma: excerpt String $ npx prisma migrate dev --name require-post-excerpt SQL: ALTER TABLE "Post" ALTER COLUMN "excerpt" SET NOT NULL; This succeeds because no NULLs remain, and running code always writes a value since Deploy 2. Verify before Deploy 4: SELECT COUNT(*) FROM "Post" WHERE "excerpt" IS NULL; -- must be 0 WHY THIS WORKS AS AN ANSWER ------------------------------ At every step, the database accepts what both the currently running code and the next version do: the column starts optional, new code fills it in, the backfill covers old rows with real content, and only then does the NOT NULL constraint arrive. The backfill is re-runnable (WHERE "excerpt" IS NULL), and the check before the final step turns "I think it's done" into a measured fact.