Cum proiectezi arhitecturi de backend care rezistă
Proiectează stările înainte de ecrane, fă scrierile repetabile, ține octeții în afara API-ului și pleacă de la premisa unei singure mașini.
Începe cu stările, nu cu ecranele
Majoritatea problemelor de backend pe care le-am moștenit nu erau probleme de performanță. Erau neînțelegeri despre ce are voie să fie o înregistrare.
Înainte să scriu o funcționalitate, notez stările prin care poate trece o înregistrare și tranzițiile dintre ele. În Touchstone, un task de test trece prin offered, claimed, submitted, reviewed, paid. Scrisă prima, lista aceea a făcut API-ul evident și a scos la iveală cazurile grele: ce se întâmplă când un tester abandonează un task revendicat, când doi testeri trimit același task, când o plată eșuează după aprobare.
Ecranele sunt o consecință a listei. O schemă proiectată pornind de la ecrane ajunge cu coloane nullable care codifică deciziile lipsă.
Fă scrierile repetabile
Joburile de ingerare eșuează și sunt reluate. Dacă o reluare duplică date, fiecare număr din aval e greșit și nimeni nu observă săptămâni întregi.
Tiparul pe care îl folosesc e o scriere idempotentă cu cheie pe identitatea naturală a rândului: un upsert pe perechea care ar trebui să fie unică, așa că rularea de două ori a aceluiași import produce același tabel. În Politrack, postările și comentariile sunt cheiate pe identificatorii lor de platformă; o rerulare actualizează textul și merge mai departe.
Costă un index unic și câteva linii. Te scutește de clasa de bug-uri care îți ia o zi să le găsești și care distruge încrederea în date.
Mută octeții în afara API-ului
Fișierele de dovadă sunt cealaltă greșeală recurentă. Un raport cu o înregistrare de ecran nu trebuie încărcat prin serverul de aplicație și nu trebuie ținut în baza de date.
În Touchstone, agenții cer un upload presemnat, pun fișierul direct în object storage și apoi trimit referința. API-ul nu atinge niciodată corpul fișierului, workerul îl citește din storage când trebuie să îl analizeze, iar înregistrarea e servită printr-un URL semnat care expiră.
Aceeași formă se aplică oricărui produs cu fișiere de la utilizatori: API-ul se ocupă de permisiuni și metadate, storage-ul se ocupă de octeți.
Pornește de la o singură mașină
Platforma mea rulează pe un singur VPS: Next.js, NestJS, Postgres, MinIO, un cache nginx și un model local pentru căi de rezervă. Constrângerea asta e utilă. Când memoria e finită, găsești query-ul care materializează un milion de rânduri, iar când discul e finit observi că nimeni nu a curățat niciodată logurile.
Din asta au ieșit două obiceiuri. Fiecare cale scumpă are o limită, și fiecare noapte produce un backup pe care cineva l-a și restaurat. Țin o procedură de restaurare scrisă și o exersez, pentru că un backup care nu a fost niciodată restaurat e un fișier, nu un backup.
Deployurile ar trebui să fie plictisitoare
Scopul e o lansare în care nu se întâmplă nimic surprinzător: un build reproductibil, o migrare care poate rula de două ori, o cale de întoarcere și un health check care eșuează zgomotos. Emoțiile la un deploy sunt un raport de bug care așteaptă să fie scris.
E mai puțin impresionant decât o diagramă de arhitectură distribuită și e exact ce ține un produs online un an întreg fără o echipă care să îl supravegheze.