Data Nexus

Language · We run it ourselves

SQL

The language a relational database is actually configured in — tables, constraints, indexes and access policies.

An ORM writes the migration; the migration is SQL. Somebody reads it when a deploy goes wrong at midnight, and the only useful question then is whether the person who wrote it could read it too.

postgresql.org

01/Why this one

  1. 01

    The schema is where an invariant survives. A rule enforced in application code is enforced until the next integration writes directly to the table, and the second writer is always the one nobody remembered. A constraint declared in the schema is enforced against every writer that will ever exist, including the ones not written yet.


  2. 02

    Row-level security puts the access rule beside the data rather than in whichever service happens to be asking. That matters most in a multi-tenant system, where the alternative is every query in the codebase remembering to filter by tenant and one of them eventually not remembering — a class of bug that leaks another customer's rows and looks like a working page.


  3. 03

    Indexes are a design decision taken at the same time as the query, not a remedy applied after somebody complains. Which is why they are visible in the migration history: an index added six months later usually means the shape was wrong six months ago.


  4. 04

    Migrations give a schema a past that can be replayed. A current state somebody remembers is not a schema; it is a rumour with a database attached, and it is why restoring an environment turns into an afternoon of archaeology.


  5. 05

    We run two different ORMs across our own systems — Drizzle in one, Prisma in another — and read the SQL both of them generate. An ORM you cannot read past is a dependency you cannot debug, and the moment a query plan matters the abstraction stops helping and starts hiding.


  6. 06

    We drop to hand-written SQL where the set operation is the point: a report, a backfill, a reconciliation across tables. Expressing that through an object mapper produces something longer, slower and harder to check against the question it was meant to answer.

02/What you can check

An entry that cannot point at something you can open, run or read does not compile. That is a property of the type, not a promise in a paragraph.

In our code
carfleet/packages/db/drizzle — One hundred and ninety-seven migration files: seventy-seven tables, a hundred and sixty indexes, sixteen tables put under row-level security and thirteen policies written against them.

In our code
mozg/contracts/pg-schema/prisma/migrations — The same discipline under a different ORM — a versioned history rather than a schema somebody remembers.

On this site
Aggregates recomputed hourly out of Postgres on this site, with no identifier of a human reader anywhere in the source.Open it
03/Where it stops

Invariant

SQL is not a data model. A well-written migration over a wrong set of tables gets you to the wrong answer faster, and no amount of index tuning rescues a schema that made the domain impossible to state. We are also Postgres-shaped: the dialect knowledge here is Postgres and its extensions, and an estate running Oracle or SQL Server at scale wants people who live in those rather than people who translate into them.