Case Studies

How ForgeSchema Blocks an Accidental Column Drop From a Bad Merge

Two branches both touched schema/app.yaml. One added a phone column to customers. The other moved a few columns around for readability. Git resolved the conflict, the build was green, and the merged file was missing billing_address. Nobody noticed. YAML diffs of moved blocks are hard to read.

With a tool that turns a diff straight into SQL, the next deploy runs DROP COLUMN billing_address. The column and every value in it are gone, and the first sign is a support ticket about invoices.

Removal is blocked, not guessed

In ForgeSchema’s 0.2 alpha, removing anything from the desired schema blocks the plan. Tables, columns, indexes, unique constraints and foreign keys alike. The planner doesn’t guess that a missing column was meant to go.

./bin/forgeschema plan current.json schema/app.yaml > plan.json
echo $?   # 3

Exit 3 means a blocked plan. The artifact is still written, so a CI job can read it, and it lists each blocker with the object and the reason. The operations list is empty. Not just missing the drop, empty. The perfectly fine phone column isn’t planned either, because a blocked plan has no partial interpretation.

Apply refuses a blocked artifact outright, also with exit 3. There’s no flag to force it through.

The same protection against renames

A merge that renames a column by accident looks identical: one name gone, one new. ForgeSchema never infers a rename from similar names, so that blocks too. A real rename needs its own rename file saying from and to, which shows up in code review as a deliberate act.

When you really do want something gone

The 0.2 alpha can’t drop at all. That’s a limit, not a feature, and on main the next release lifts it the careful way: a drop only goes into a plan when the change file lists it by name, together with every index or key that depends on it, and the explained plan opens with a line naming each column and table it will delete. An unlisted removal stays blocked.

For the team in the story, the fix is a one-line revert of the merge. The database never knew.

See the schema format for how the desired file is written, or try the recorded browser demo.