« All posts

Supabase's silent defaults: four incidents, one shared root cause

Four Supabase incidents in one week trace back to one cause: silent vendor defaults. Three rules for documenting, linting, and monitoring them.

Four seemingly unrelated Supabase incidents surfaced in a single week: default anon EXECUTE grants on SQL functions, an ON DELETE SET NULL cascade colliding with a CHECK NOT NULL constraint, RLS recursion between auth.users and a roles table, and a silent 1000-row cap caused by a missing .order() on a chained .select(). In every case Postgres raised no exception, the HTTP status was 200, and the payload was quietly incomplete or wrong. Unit tests passed against small seeded datasets, code review approved single-line chains — the failures only appeared at production scale, with real auth contexts and policy dependency chains.

The author frames these as one family rather than four bugs: undocumented platform defaults become implicit contracts baked into an application without ever appearing in its codebase. Three concrete rules are proposed in response. First, any undeclared vendor default should either be explicitly documented and asserted in an ADR or rules file, or hard-blocked by a structural guardrail such as an ESLint rule or SQL CHECK constraint. Second, AST-based lint rules outperform human code review once a pattern recurs dozens of times — the ctid case needed five noise-reduction mechanisms to cut 178 raw alerts down to 108 real targets. Third, instrumentation belongs in production, not in tests: daily drift probes should flag counters that suspiciously resemble hidden ceilings.

For backend and platform engineers, the takeaway is to treat vendor defaults as silent design decisions embedded in the codebase, not documentation footnotes — and to catch drift before it reaches customers.