Getting started

Your first review

A walkthrough of what happens when Bedrock reviews a real migration — using a many-to-many user/organization change as the worked example.

The change

Suppose your app started with each user belonging to a single organization (users.organization_id) and you now want to support users belonging to many.

prisma
model User {
  id              String       @id @default(cuid())
- organizationId  String
- organization    Organization @relation(fields: [organizationId], references: [id])
+ memberships     Membership[]
}

+ model Membership {
+   id              String       @id @default(cuid())
+   user            User         @relation(fields: [userId], references: [id])
+   userId          String
+   organization    Organization @relation(fields: [organizationId], references: [id])
+   organizationId  String
+   role            Role         @default(MEMBER)
+   @@unique([userId, organizationId])
+ }

What Bedrock detects

  • Relationship cardinality changed: 1:NN:N.
  • A foreign key column on users is implicitly removed.
  • Every read of user.organizationId in your codebase will break.
Why this is high-risk
This is not a simple additive change. It rewrites a permission boundary. Auth, billing, audit logs, and analytics events all likely read users.organization_id directly. A clean drop will take production down.

The plan Bedrock writes

sql
-- Phase 1 — ships immediately (additive, zero-downtime)
CREATE TABLE memberships (
  id              text PRIMARY KEY,
  user_id         text NOT NULL REFERENCES users(id),
  organization_id text NOT NULL REFERENCES organizations(id),
  role            text NOT NULL DEFAULT 'member',
  UNIQUE (user_id, organization_id)
);

-- Phase 2 — backfill in batches of 1000 with retries
INSERT INTO memberships (id, user_id, organization_id, role)
SELECT gen_random_uuid()::text, id, organization_id, 'owner'
FROM users
WHERE organization_id IS NOT NULL
ON CONFLICT DO NOTHING;

-- Phase 3 — flip reads behind feature flag (app code change, no SQL)
-- Phase 4 — soak 14 days, verify zero divergence
-- Phase 5 — drop users.organization_id (separate PR)

The PR comment

text
Bedrock / migration safety · review

Verdict       review
Risk score    71 / 100
Type          relationship cardinality change

Top issues
  1. users.organization_id removed in same migration as memberships table
     → recommend a separate cleanup migration after a 14-day soak.
  2. References to user.organizationId found in src/auth/, src/billing/, src/api/
     → flip reads behind a feature flag before destructive cleanup.
  3. No backfill detected. Existing users will lose organization context on deploy.

Open the full report → bedrock.dev/r/3a7f9c1b
Docs · First review — Bedrock