Migrations and local development with the Supabase CLI
Databases & SQL's own domain (and Django's own migrations lesson) taught why schema changes need to be tracked, versioned, and applied in order rather than made directly against a live database by hand. The Supabase CLI is where that same discipline meets a project built around a dashboard that makes it tempting to skip it.
Intermediate
3 min read
The trap the dashboard makes easy to fall into
Supabase's own web dashboard lets you create a table, add a column, or write an RLS policy directly through a UI — genuinely convenient for quick experimentation, and genuinely risky as the only way a team manages schema changes, for the exact reason Databases & SQL's own domain already established: a change made directly against a live database, with no record of what changed or why, can't be reliably reproduced in a second environment, reviewed in a pull request, or rolled back cleanly if it turns out to be wrong.
Migrations: the same discipline, applied to Supabase specifically
supabase migration new add_documents_table# creates: supabase/migrations/20260912120000_add_documents_table.sql
-- in that file:CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, owner_id UUID REFERENCES auth.users(id));ALTER TABLE documents ENABLE ROW LEVEL SECURITY;CREATE POLICY "users see their own documents"ON documents FOR SELECTUSING (auth.uid() = owner_id);
A migration is a plain SQL file, checked into version control, that describes one specific schema change — creating a table, adding an index, defining an RLS policy — exactly the discipline this curriculum's own Django domain already taught for its own ORM-based migrations, just expressed here as raw SQL rather than Python model changes. Applying migrations in order, rather than making ad hoc dashboard changes, means the schema itself becomes something reviewable in a pull request and reproducible in any environment — the exact same IaC-style benefit Render's own Blueprint lesson named for infrastructure generally, here applied specifically to database schema.
Local development: running a full Supabase stack on your own machine
supabase start # spins up Postgres, the REST API, Auth, Storage, # and Realtime — ALL locally, via Docker (Docker & # Containers' own domain, applied here directly)supabase db reset # wipes the local DB and replays every migration from scratch
The Supabase CLI runs the entire platform locally using Docker containers (Docker & Containers' own domain covers exactly this underlying mechanism) — a real Postgres instance, a local PostgREST, local Auth, all matching what runs in production, letting a developer test schema changes, RLS policies, and Edge Functions entirely offline before ever touching the real, shared project.
Why replaying migrations from scratch is the actual test of whether they're correct
supabase db reset doesn't just clear data — it proves the migrations, played back in order from nothing, actually reconstruct the intended schema correctly. A migration that happens to work this once, because the database already had some manually-added column or policy from dashboard tinkering, can fail the moment it's replayed against a genuinely fresh database — exactly the kind of silent drift CI/CD's own domain warns about for any environment whose actual state has quietly diverged from what its own configuration claims.