Apply schema changes to production during the build - #5
Merged
Conversation
Nothing applied schema changes to production. `prisma generate` builds a client that assumes the schema but never connects, so a merged schema change deployed a client whose queries referenced columns the database did not have. The failure surfaced only at runtime, and only in the logs. That is how `Schedule.runId`, `runUrl`, and `runConclusion` -- added in 9348c93 and deployed -- reached production missing. Creating and listing schedules failed outright, and the run-resolution pass errored on every cron tick for five days. Dispatch itself kept working: it touches only pre-existing columns, and resolveTriggeredSchedules is isolated in its own catch, so the workflows fired and nothing surfaced the breakage. The columns have since been applied by hand, so this changes nothing about the current database. What it changes is that the next one cannot be missed. prisma/migrations/ becomes the source of truth, baselined at 0_init from the current schema and marked applied on production, so `migrate deploy` is a no-op against it rather than an attempt to CREATE TABLE over live rows. The build step is gated on VERCEL_ENV. A preview build pointed at the production database would otherwise apply an unmerged branch's migrations to production; if preview deployments get their own database, drop the gate so previews migrate too. A production build with no DATABASE_URL fails rather than skipping -- skipping would restore exactly the silence this removes, and failing is the safe direction, since Vercel keeps serving the previous deployment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The outage this closes
Schedule.runId,runUrl, andrunConclusionwere added in 9348c93, merged, and deployed. The production database never got the columns, so the deployed Prisma Client kept querying columns that did not exist:It was never applied because nothing could have applied it. There was no
prisma/migrations/,buildwasprisma generate && next build(generates a client, never connects), andscripts/prisma.mjsrefuses to run against production by design. Every prior schema change was applied to prod by hand; this one was missed and nothing anywhere reported it.Impact, traced per call site:
createSchedule/getSchedulesDispatch survived because
getDueScheduleIds,claimSchedule, andupdateScheduleStatustouch only pre-existing columns, andresolveTriggeredSchedulesis isolated in its own catch. Workflows kept firing, which is why this stayed quiet.Already done, outside this PR
The three columns have been applied to production by hand, so production is already working — this PR changes nothing about the current database state.
0_initis marked applied there, somigrate deployis a verified no-op rather than an attempt toCREATE TABLEover live rows.What this changes
prisma/migrations/becomes the source of truth, baselined at0_initfrom the current schema.scripts/migrate-deploy.mjsrunsprisma migrate deployin the build, gated onVERCEL_ENV=production.buildbecomesprisma generate && node scripts/migrate-deploy.mjs && next build.Two deliberate choices, both reversible:
Previews skip migration. A preview build pointed at the production database would apply an unmerged branch's migrations to production. I could not verify how preview
DATABASE_URLis configured, so I assumed the unsafe case. If previews have their own database, drop the gate so they migrate too.A production build with no
DATABASE_URLfails rather than skipping. Skipping would restore exactly the silence this removes. Failing is the safe direction — Vercel keeps serving the previous deployment.DATABASE_URLmust be exposed to Vercel's Build step, not only the runtime. If it is currently runtime-only, the first production deploy after this merges will fail. Safely — the current deployment keeps serving — but it will block until the setting changes.Verification
0_initreplayed from empty against a shadow database diffs clean againstschema.prismaprisma migrate diffagainst production: no driftmigrate deployagainst production:No pending migrations to applyselectclauses re-run against production: passVERCEL_ENV), skips on preview, exits 1 on production withoutDATABASE_URLtsc --noEmitclean,eslintclean on source,npm run buildsucceedsNot included
The
db:deploy/db:migrate/db:statusscripts and the README documentation depend onscripts/prisma.mjs, which lands withdev-oauth-app-setup. They ride along with that branch so this one stays mergeable on its own.🤖 Generated with Claude Code