Niemal każdą przepisaną od nowa platformę SaaS, do której uratowania nas wezwano, da się sprowadzić do tych samych trzech słów wypowiedzianych przez pierwotny zespół już w pierwszym miesiącu: „dodamy to później”. Wielodostępność, kontrola dostępu oparta na rolach, prawdziwe rozliczenia. Sprawiają wrażenie rzeczy, które dorabia się, gdy ma się już klientów. Jest dokładnie odwrotnie — to właśnie one są kształtem budynku, a fundamentów nie da się zmienić, gdy mury już stoją, bez burzenia wszystkiego.
Zbudowaliśmy platformy, które dziś działają w ponad 40 krajach na jednej bazie kodu. Powód, który to umożliwił, jest równie nudny, co jednogłośny: model najmu, dostęp i rozliczenia zostały rozstrzygnięte pierwszego dnia, przed pierwszą funkcją. Oto jak wyglądają te decyzje i co kosztuje ich odkładanie.
01Dlaczego zespoły to pomijają (i dlaczego to pułapka)
Presja jest realna. Masz jednego klienta pilotażowego, demo w przyszłym tygodniu i backlog funkcji, które naprawdę wyglądają jak produkt. Wielodostępność jest dla tego pierwszego klienta niewidoczna — nie może jej zobaczyć, więc sprawia wrażenie zbędnego ozdobnika. Zespół buduje więc aplikację jednodostępną „na razie” i obiecuje ją później uogólnić.
Pułapka polega na tym, że „na razie jeden najemca” przecieka jednym założeniem do każdej warstwy: że na świecie istnieje dokładnie jedna organizacja. To założenie ląduje w końcu w twoich zapytaniach, twoim uwierzytelnianiu, twoich pamięciach podręcznych, twoich zadaniach w tle, twoich ścieżkach plików. Wyciągnięcie go później nie jest refaktoryzacją — to przepisanie od nowa, bo założenie nie tkwi w jednym miejscu, tkwi wszędzie.
Wbudowanie najmu pierwszego dnia kosztuje cię może dwa tygodnie architektury i dyscypliny. Wbudowanie go w drugim roku — gdy masz już prawdziwych klientów, prawdziwe dane i prawdziwe oczekiwania co do dostępności — to wielomiesięczne przepisywanie pod obciążeniem, z migracją danych, której nie wolno ci spartolić. Praca nie znika, gdy ją odkładasz. Ona się kumuluje.
02Najemca jest pierwszą kolumną
Nasza reguła jest prosta: każdy wiersz należący do klienta nosi tenant_id, i nie istnieje żadna ścieżka kodu zdolna do wykonania zapytania bez niego. Nie konwencja, którą ludzie pamiętają — ograniczenie, które egzekwują baza danych i framework, tak że zapomnienie staje się niemożliwe, a nie tylko niewskazane.
Najczystsza wersja, jakiej używamy, opiera się na samej bazie danych. Polityki bezpieczeństwa na poziomie wierszy oznaczają, że granica między najemcami jest egzekwowana warstwę poniżej kodu aplikacyjnego, tam, gdzie błąd ORM-a czy zapomniana klauzula WHERE nie może przeciec danych jednego klienta do drugiego. Aplikacja ustawia bieżącego najemcę na połączeniu; baza danych odmawia zwrócenia czegokolwiek innego.
-- the boundary lives below the app, not inside it ALTER TABLE orders ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting('app.tenant')::uuid); -- app sets the tenant per request; a forgotten WHERE -- clause now returns zero rows instead of someone else's data SET app.tenant = 'a91f…'; SELECT * FROM orders; -- only this tenant's orders, always
Ta jedna decyzja na stałe usuwa z twojej listy zmartwień całą kategorię katastrofalnych błędów — przeciek danych między najemcami. To dwa dni pracy o najwyższej dźwigni w całym projekcie.
Najkosztowniejszy błąd w SaaS to ten, który pokazuje jednemu klientowi dane innego klienta. Zaprojektuj architekturę tak, by nie mógł się wydarzyć, a nie tak, by był nieprawdopodobny.
— O izolacji jako ograniczeniu03RBAC to nie strona ustawień
Drugim „później”, które zamienia się w przepisanie od nowa, jest kontrola dostępu. Zespoły uruchamiają się z niejawnym, dwurolowym światem — admin i użytkownik — zaszytym na sztywno w instrukcjach if rozsianych po całej bazie kodu. Potem klient korporacyjny prosi o rolę „tylko rozliczenia” albo „audytora w trybie tylko do odczytu”, i odkrywasz logikę uprawnień w dwustu miejscach.
My modelujemy uprawnienia jako dane od samego początku: role, uprawnienia i przypisania między nimi żyją w tabelach, a każde chronione działanie zadaje pytanie pojedynczemu, centralnemu organowi: „czy ten aktor może zrobić tę rzecz na tym zasobie, w obrębie tego najemcy?”. Dodanie roli staje się jednym wierszem, a nie wydaniem. To właśnie pozwala platformie powiedzieć tak schematowi organizacyjnemu klienta korporacyjnego bez dotykania kodu aplikacyjnego.
- Uprawnienia to czasowniki na zasobach —
invoice:read,invoice:void— a nie mgliste poziomy w rodzaju „menedżer”. - Role to zestawy uprawnień, które najemca może komponować, tak że dwóch klientów może rozumieć przez „admina” co innego.
- Każda kontrola przechodzi przez jedną bramkę. Logika autoryzacji w jednym miejscu jest audytowalna; rozsiane instrukcje
ifto obciążenie.
04Rozliczenia to produkt, a nie hydraulika
Rozliczenia to miejsce, gdzie „dodamy to później” boli najbardziej, bo zanim je dodasz, masz klientów na umowach przyklepanych uściskiem dłoni, niespójne dane o tym, kto komu ile jest winien, i żadnego czystego pojęcia subskrypcji. Nadrabianie rozliczeń to uzgadnianie pieniędzy, a to jedyne miejsce, gdzie błąd nie jest tylko kłopotliwy — to zwrot, spór albo problem zgodności.
Model rozliczeń projektujemy równolegle z modelem najemcy: plany, subskrypcje, zużycie, faktury i cykl życia, który je łączy. Nawet jeśli pierwsi klienci są fakturowani ręcznie, model danych jest tam od pierwszego dnia, tak że późniejsze włączenie planów samoobsługowych jest funkcją, a nie migracją całej historii twoich przychodów.
Jedna baza kodu, jeden model wdrożenia, wielu najemców.
Żadnego forka per klient do utrzymywania ani do rozjeżdżania się.
Izolacja egzekwowana na poziomie bazy danych, nie przez konwencję.
05Czterdzieści krajów, jedna baza kodu
Dotarcie do ponad 40 krajów brzmi jak opowieść o skalowaniu. W rzeczywistości to opowieść o konfiguracji. Platforma nie ma gałęzi kodu właściwych dla poszczególnych krajów — ma model najemcy na tyle bogaty, by wyrazić jako dane to, co różni się między regionami: walutę, reguły podatkowe, język, formaty dat, klauzule prawne na fakturze.
Gdy granica między „kodem” a „konfiguracją” zostanie poprowadzona poprawnie już pierwszego dnia, nowy kraj jest zadaniem wdrożeniowym, a nie projektem inżynieryjnym. Gdy poprowadzi się ją źle, każdy nowy rynek jest forkiem, i w niespełna rok utrzymujesz tuzin subtelnie różniących się wersji tej samej aplikacji i wdrażasz każdą poprawkę tuzin razy. Efekt „jednej bazy kodu” jest następstwem decyzji podjętych przed pierwszym klientem.
Korzyść z robienia tego pierwszego dnia nie jest widoczna w pierwszym miesiącu — jest widoczna w trzecim roku, gdy wdrażasz funkcję raz, a 40 krajów dostaje ją jednocześnie, podczas gdy twój jednodostępny konkurent wciąż wtapia ją w swój siódmy fork klienta.
06Lista kontrolna pierwszego dnia
Jeśli uruchamiasz platformę SaaS w tym kwartale, oto decyzje, które należy podjąć przed napisaniem jakiejkolwiek funkcji — nie dlatego, że są pilne, lecz dlatego, że są nieodwracalne:
- Umieść
tenant_idna wszystkim i egzekwuj go poniżej aplikacji. Bezpieczeństwo na poziomie wierszy lub odpowiednik. Uczyń zapytanie między najemcami niemożliwym, a nie nieprawdopodobnym. - Modeluj uprawnienia jako dane. Role i przypisania w tabelach, jedna centralna bramka autoryzacji. Dodanie roli powinno być jednym wierszem.
- Zbuduj model danych rozliczeń wcześnie. Plany, subskrypcje, faktury — nawet jeśli na początku fakturujesz ręcznie. Nie nadrabiaj pieniędzy po fakcie.
- Oddziel kod od konfiguracji. Wszystko, co różni się w zależności od klienta lub regionu, jest danymi. Kraj numer 41 powinien być formularzem wdrożeniowym.
- Najpierw napisz ścieżkę provisioningu najemcy. Czyste utworzenie nowego najemcy, z sensownymi wartościami domyślnymi, to twoja najczęściej wykonywana operacja. Zautomatyzuj ją pierwszego dnia.
Nic z tego nie jest egzotyczne. To kilka tygodni dyscypliny na starcie w zamian za to, że nigdy nie będziesz musiał przepisywać wszystkiego od nowa. Zespoły, które osiągają 40 krajów na jednej bazie kodu, nie miały szczęścia — po prostu odmówiły powiedzenia „później” w odniesieniu do tych trzech rzeczy, których nie da się dodać później.

