Skip to content
All articles
Databases10 min read

Designing Postgres schemas for change

Your schema is the longest-lived part of your app. Design it to evolve.

Misbah Uddin Hasanat

March 22, 2026

Designing Postgres schemas for change

Code gets rewritten constantly. The database schema outlives almost everything else, and migrating it under load is one of the harder things in software. Time spent on schema design pays compounding dividends.

Let the database enforce invariants

Constraints, foreign keys and check rules are not bureaucracy — they're the last line of defense against bad data. Application code has bugs; the database is where correctness should be guaranteed.

RLS is not optional

On Supabase, every table reachable by the client needs Row-Level Security enabled with explicit policies. A table without RLS is a public table.

migration.sql
create table public.documents (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid not null references auth.users(id) on delete cascade,
  title text not null,
  created_at timestamptz not null default now()
);

alter table public.documents enable row level security;

create policy "owners read their documents"
  on public.documents for select
  to authenticated
  using (auth.uid() = owner_id);

Migrate forward, never edit in place

Every schema change is a migration that's reviewed, versioned and reversible. Editing production schema by hand is how teams lose data.

  • Enable RLS on every client-reachable table
  • Add indexes for the queries you actually run
  • Use foreign keys with explicit on-delete behavior
  • Keep migrations small and reversible

Written by

Misbah Uddin Hasanat

Senior Engineer

Systems and platform engineering — distributed systems, data pipelines and the unglamorous reliability work that keeps software running.

Keep reading

Related articles

Engineering notes, in your inbox.

Occasional, high-signal writing on AI, automation and building software that lasts.