Každý produkt, který jsem nasadil, přežil alespoň jedno rozhodnutí o frameworku. Databáze přežila všechna. Mění se verze Next.js, ORM i tým; řádky zůstávají spolu s předpoklady zapsanými ve schématu. SQL proto považuji za skutečný kontrakt produktu, nikoli za implementační detail skrytý za třídou repository.
Prisma pokrývá velkou část mé každodenní práce: typované CRUD, migrace v repozitáři a schéma, které přečte i nový kolega. Čtu ale také SQL, které generuje. Pokud stránka seznamu spouští dotaz pro každý řádek, report potřebuje window function nebo dashboard GROUP BY přes tři joiny, napíšu dotaz ručně přes $queryRaw nebo ho vložím do view. Než někdo přidá Redis, otevřu EXPLAIN ANALYZE. Za většinou tiketů „potřebujeme cache“ stojí chybějící index.
Omezení jsou nejlevnější QA, jaké kdy dostanete. NOT NULL, UNIQUE, FOREIGN KEY a CHECK se uplatní při každém zápisu z každé služby, i ze skriptu, který někdo v pátek spustí ručně. Zod chrání API, databáze chrání pravdu. Peníze ukládám jako celá minor units nebo NUMERIC, nikdy jako float. Platební webhooky mohou dorazit dvakrát, proto má idempotency key dostat unikátní index, ne komentář v handleru.
FinTech chyby vznikají při souběžném přístupu. Dva požadavky načtou zůstatek, oba usoudí, že je dostatečný, a oba zapíší změnu — jde o ztracenou aktualizaci, kterou žádná validace ve frontendu neopraví. Preferuji jeden atomický příkaz —UPDATE ... SET balance = balance - $1 WHERE id = $2 AND balance >= $1— a následně ověřím, že se změnil přesně jeden řádek. Alternativou je transakce se SELECT ... FOR UPDATE a append-only ledger tabulkou, aby šel každý pohyb později vysvětlit. U systémů s objemem zpracovaných plateb nad 1,5 milionu dolarů je „umět to později vysvětlit“ podstatou práce.
Indexy mají odpovídat dotazům, které skutečně spouštíte, ne sloupcům, jež vypadají důležitě. Ve složeném indexu jsou nejprve sloupce s rovností a nakonec rozsah nebo řazení; částečné indexy se hodí pro „aktivní“ řádky a keyset pagination je lepší než OFFSET 50000. Migrace na živé tabulce probíhají po krocích: přidat nullable sloupec, naplnit po dávkách, potom přidat omezení; index vytvořit pomocí CONCURRENTLY. Migrace, která v poledne zamkne tabulku objednávek, je výpadek s přiloženým pull requestem.
Odmítám „flexibilní“ tabulku entity-attribute-value místo schématu, JSONB jako úkryt pro klíčová pole, která nikdo nechce modelovat, soft delete všude bez ohledu na unikátní omezení a cache před dotazem, který nikdo neprověřil přes EXPLAIN. Pokud mi pošlete pomalou stránku nebo nejisté schéma, připojte i dotaz a počty řádků. Odpovím plánem, indexem a prvním omezením, které bych přidal.
Dejte příštímu produktu někoho, kdo zůstane.
Chcete začít? Napište mi – mohu začít hned a zůstat. Dlouhodobé retainery, ne dvoutýdenní předání. Proberme váš projekt, požadavky a nejlepší cestu vpřed.