ADL vs Supabase
Supabase Already Proved Interpretation Works in Production
TL;DR: The strongest argument for interpreting a declaration instead of generating code was not written by us. It is PostgREST, the engine at the center of Supabase, which reads a Postgres schema and serves the API directly: no generated code, in production, at enormous scale. Supabase closed the “interpretation is for mocks” debate for good. What it left open is the vocabulary question: the schema and its policies can only say so much, and everything they cannot say gets written as SQL functions, triggers, policy predicates, and TypeScript Edge Functions — the application, smeared across four dialects. ADL’s bet is that all of it belongs in one declared artifact.
The steelman
Supabase may be the best-executed developer product of its generation.
Point it at nothing, and minutes later you have: a real Postgres database (not a proprietary datastore wearing one as a costume), a complete REST API over every table, authentication with social providers and JWTs, file storage, realtime subscriptions, vector search, client SDKs for every platform that matters, and a dashboard your least backend-inclined teammate can operate. All of it open source, all of it self-hostable, all of it resting on the single most trusted piece of data infrastructure in the industry. The bet (“Firebase, but it’s just Postgres”) respected developers instead of capturing them, and the adoption numbers are the reward.
But the piece of Supabase this essay cares about is the one most users never name: PostgREST. When you create a table in Supabase and immediately query it over HTTP, no code was generated. PostgREST introspects the schema and interprets it as an API (filtering, ordering, embedding related rows, enforcing row-level security) on every request. Change the schema, the API changes. There is no scaffold, no build artifact, no generated controller to drift out of sync.
Read that again as an architectural precedent. The previous essay in this series argued that json-server proved everyone accepts “the file is the API” for mocks. Supabase goes much further: it proves the industry accepts interpretation of a declaration in production, under real load, for real money. Anyone who tells you a production backend requires generated or hand-written application code has to explain away one of the most successful backend products in the world.
So the argument between Supabase and ADL is not interpretation versus code. Supabase already won that argument on ADL’s behalf. The argument is about what the declaration can say.
The vocabulary is the schema
Supabase’s declarable surface is Postgres itself: tables, columns, types, constraints, and row-level security policies. Within that surface, the model is genuinely declarative: an RLS policy is a predicate, not a procedure, and PostgREST enforces it uniformly.
Now walk the standard checklist of what a production backend must be correct about, and watch where each item lands:
Permissions. Row access maps well to RLS. But operation-level rules (“editors may update, only admins may delete”), field-level rules (“HR and the owner may read salary; only HR may write it; strip it from everyone else’s responses”), and state-conditional rules (“once published, only admins may edit”) are increasingly awkward predicates: column-level grants, WITH CHECK clauses, and security definer functions accumulating in migrations. In ADL these are three lines in a permissions: block, including field masking and per-transition guards.
Lifecycle. Supabase has no concept of a state machine. “A ticket moves from open to resolved only by its owner or an admin, only with a resolution present, setting closedAt on the way through” becomes a trigger in PL/pgSQL — hand-written, per table, with its own bugs. And nothing stops a plain UPDATE from teleporting the status field unless the trigger author remembered to check. ADL declares the machine, and the runtime intercepts direct writes to the controlled field: they execute a legal transition’s guards or are rejected. The lifecycle cannot be bypassed by a well-formed PATCH.
Validation. Check constraints handle single-column rules. Cross-field and cross-record rules (“the manager must belong to the same department”) are triggers again. And the failure mode differs: constraint violations surface one at a time as database errors in Postgres’s voice; ADL collects every violation and returns them at once under a stable error envelope keyed by field, which is the difference between an API contract and a stack trace with manners.
Derived values. Postgres generated columns are same-row only. “Order total = sum of its line items, recalculated when a line item changes” is, once more, a trigger. In ADL it is a declared aggregation with its recompute events named.
Everything else. History, audit trails, workflows, scheduled behavior: extensions, more triggers, or code in the fourth location, Edge Functions in TypeScript, running in a separate runtime with its own deploy lifecycle.
None of these individual solutions is wrong. Postgres triggers are battle-tested; Edge Functions are a fine escape hatch. The problem is the aggregate shape: the application definition ends up distributed across schema DDL, RLS predicates, PL/pgSQL bodies, and TypeScript — four dialects, three runtimes, no single artifact that is the application. Ask “what are the rules governing an Award?” and the honest answer is a scavenger hunt through migrations. The declaration didn’t fail; it ran out, and the overflow became code, which is the same wound json-server has, moved to production where it costs more.
ADL’s position is that the checklist above is not overflow. It is the application, and it belongs in the declaration: one file, one vocabulary, one reviewable artifact that a domain expert, an auditor, or an AI agent can read end to end.
The honest concessions
Two, both at full strength.
Supabase is a platform. Kalos is a runtime. Supabase ships hosted operations, backups, a dashboard, file storage, realtime subscriptions, client SDKs, and an onboarding experience measured in minutes. Kalos ships a server you operate yourself, and it renders no dashboard. The schema endpoint feeds a UI you build. If what you need is the shortest path from idea to deployed product with a login screen, Supabase is the better tool and it is not close. ADL’s case begins where the definition of the domain (its rules, lifecycles, and audit obligations) is the hard part of the system, not the hosting.
RLS is enforced inside the database. A Postgres RLS policy holds against every connection — PostgREST, a reporting tool, a developer with psql and production credentials. ADL’s enforcement lives in the runtime: authoritative for every request that passes through it, and silent about connections that don’t. Defense in depth at the data layer is a real property Supabase has and Kalos does not, and for some threat models it is decisive. The mitigations (narrow database credentials, no direct connections) are operational discipline, not architecture, and we won’t pretend otherwise.
Closer
Supabase proved the interpreter belongs in production; ADL asks why the application shouldn’t fit in what it interprets.