Aproape tot ce livrăm — site-uri, jocuri, aplicații cu conturi și plăți — rulează pe Firebase: hosting multi-site, Authentication, Realtime Database, Firestore, Cloud Functions, Remote Config. Nu pentru că Firebase ar fi perfect, ci pentru că pentru un studio mic raportul dintre ce primești și ce administrezi rămâne imbatabil. Articolul acesta e lista de lecții pe care le-am plătit cu incidente reale — inclusiv una în care un deploy aparent nevinovat a șters o funcție din producție.
Lecția 1: deploy-urile de funcții se fac țintit, întotdeauna
Incidentul care ne-a schimbat procedurile: două site-uri diferite împart același proiect Firebase, iar funcțiile lor trăiesc în codebase-uri separate. Un coleg a rulat un banal firebase deploy --only functions dintr-unul din repo-uri. Firebase a interpretat comanda ca „starea completă dorită a funcțiilor" și a șters funcția de submitere a formularului de contact care aparținea celuilalt site. Fără eroare, fără avertisment vizibil — formularul de contact a murit în tăcere.
Regula de atunci, scrisă în documentația internă cu majuscule: niciodată --only functions, întotdeauna --only functions:numeFunctie. Și, complementar: emulatorul rulează suita completă de teste înainte de orice deploy de funcții. Costul respectării regulii: câteva secunde per deploy. Costul încălcării ei: un formular mort până observă cineva.
Lecția 2: serverul e singura sursă de adevăr pentru bani
În jocurile noastre există o economie de jetoane: jucătorii cumpără pachete, deblochează personaje, primesc recompense. Prima versiune ținea prețurile în clientul jocului. A doua versiune — cea corectă — le ține exclusiv în configurația funcțiilor de pe server: clientul trimite doar id-uri („vreau să deblochez suit:aegis"), serverul decide prețul, verifică soldul și scrie tranzacția idempotent.
Motivul e simplu: tot ce e în client e editabil de utilizator. Orice preț, orice validare, orice „are deja acest item" verificat doar în client e o invitație. Aceeași regulă se aplică la webhook-uri de plăți: idempotente (același eveniment primit de două ori nu creditează de două ori) și fail-closed (orice dubiu înseamnă refuz, nu acceptare).
Lecția 3: Remote Config e mai valoros decât pare
Două utilizări care ne-au scutit de release-uri de urgență:
- Maintenance mode pentru web. Un flag în Remote Config, citit cu realtime updates, cu comportament fail-open: dacă configul nu se poate încărca, site-ul rămâne funcțional. Putem pune orice produs în mentenanță în secunde, fără deploy.
- Versiune minimă pentru aplicațiile native. Gate-ul de versiune („aplicația ta e prea veche, actualizează") e condus din Remote Config, nu hardcodat. Când o versiune veche are un bug serios, o blocăm de la distanță — fără să așteptăm review-ul din store.
Lecția 4: regulile de securitate sunt produsul, nu o anexă
Realtime Database și Firestore îți expun datele direct către clienți — singura barieră sunt regulile. Practicile care ne-au rămas după un audit intern:
- Regulile se scriu deny-by-default: nimic nu e citibil sau scriibil până nu există un motiv explicit.
- Regulile au teste automate (rulate pe emulator) exact ca orice alt cod. O regulă fără test e o regulă care se va strica la următoarea modificare.
- Token-urile de identitate nu au ce căuta în URL-uri (ajung în log-uri); SDK-ul le trimite corect în headere.
- Datele sensibile nu se ating din client nici măcar „doar la citire" — trec prin funcții care aplică logica de autorizare pe server.
Lecția 5: hosting-ul are detalii care se văd în SEO și în viteză
cleanUrls+ redirect-uri 301 din prima zi — altfel acumulezi și/pagina.htmlși/paginaîn index, cu tot cu conținut duplicat.- Cache diferențiat pe tip de fișier: HTML cu
no-cache(se validează la fiecare request), CSS/JS cu max-age scurt și cache-busting prin query de versiune, imagini cu max-age lung. Ne-a scăpat de clasicul „am dat deploy dar utilizatorii văd versiunea veche". - Multi-site în același proiect funcționează bine (site-ul principal, subdomeniul de jocuri și alte produse împart proiectul), dar cere țintire explicită la deploy — vezi Lecția 1, care se aplică identic la hosting:
--only hosting:numeSite.
Ce evităm conștient
- Logică de business în client — orice atinge bani, solduri sau permisiuni stă în funcții.
- Funcții „la grămadă" — funcțiile multor produse în același codebase fără separare clară; e rețeta incidentului de la Lecția 1.
- Dependența de un singur mediu — totul trebuie să ruleze și pe emulator; dacă un flux se poate testa doar în producție, fluxul e prost proiectat.
- Optimizarea prematură a costurilor — free tier-ul și blaze-ul la volum mic sunt generoase; ne uităm la facturi lunar, nu ne arhitecturăm după ele din prima zi.
Concluzie
Firebase rămâne alegerea noastră implicită pentru produse noi: viteza de la idee la producție e reală. Dar „backend-as-a-service" nu înseamnă „backend fără disciplină" — înseamnă că disciplina se mută în reguli de securitate testate, deploy-uri țintite, configurare la distanță și o graniță fermă între ce decide clientul și ce decide serverul. Toate lecțiile de mai sus au costat ceva când le-am învățat; sperăm că citite gratis dor mai puțin.
Construiești pe Firebase și vrei o a doua opinie pe arhitectură? Scrie-ne la contact@hashcoreai.com.