O projekcie

Jeden silnik, trzy wdrożenia, dwa języki. Wydzielony z serwisu, który przez cały ten czas serwował czytelnikom treść bez przerwy.

Druga instancja jest dziś kwestią konfiguracji, nie forka; trzecia powstała w jeden wieczór. Ta strona opisuje, jak to jest zrobione i na jakich decyzjach stoi. Napisałem ją dla osób, które oceniają inżynierię, a nie ofertę.

Paweł Gąsienica-Samek · programista backendu, trzynaście lat w C# · kontakt@kolofon.dev

Stan na dziś

Silnik0.9.x, wersjonowany, tagowany
Wdrożenia3 instancje, 2 języki
ŚrodowiskoCloudflare Workers · brzeg sieci
BazaD1 / SQLite · 12 tabel
FrontReact 19 · TanStack Router · Vite
JęzykTypeScript, ściśle typowany
Warstwa aplikacjiPWA · offline · Web Push
Koszt stały0 zł przy zerowym ruchu

Historia wersji jest publiczna, z godzinami commitów włącznie — wraz z awariami i tym, co trzeba było odkręcać.

Problem inżynierski

Serwis literacki urósł na tyle, że jego kod przestał być kodem tej jednej strony. Zadanie: wydzielić z niego silnik, tak żeby druga instancja była kwestią konfiguracji, a nie forka — i zrobić to na żywym serwisie, który ma czytelników i nie może przestać działać.

Metoda sprowadzała się do jednego pytania zadanego przy każdym pliku: czy ten kod wie, jak nazywa się serwis, dla którego go napisano. Cztery etapy, zielone budowanie po każdym, zero przerwy w działaniu.

Cztery rzeczy warte obejrzenia

Diagnoza awarii produkcyjnej: biała strona

Serwer renderował kompletny HTML, przeglądarka go kasowała. Objaw prowadził w złą stronę — podejrzenie padło na zmiany z tego dnia. Śledztwo odblokował ślad stosu wskazujący na wnętrze routera: obiekt istniał, ale nie miał pola, którego framework się po nim spodziewał.

Przyczyna: dwie kopie tego samego pakietu w jednym bundlu, jedna tworzyła instancję, druga z niej czytała. Przyczyna źródłowa: brak pliku blokady zależności. Naprawa zajęła mniej niż diagnoza.

Luka bezpieczeństwa znaleziona przy przeglądzie, nie ze zgłoszenia

Klucz do trasy serwisowej z dziennikiem zdarzeń był stałą w kodzie silnika. Znaczyło to, że każda instancja postawiona na tym silniku ma ten sam klucz — także instancje cudze. Przy jednej instancji brzydka stała, przy wielu luka do zgłoszenia.

Klucz jest teraz sekretem instancji, bez wartości domyślnej. Wartość domyślna byłaby tym samym błędem przeniesionym w nowe miejsce.

Prywatność rozstrzygana przy budowaniu, nie przy renderowaniu

Ustawienie wyłączało podpis silnika w stopce i działało — w HTML nie było ani jednego wystąpienia nazwy. W pliku JavaScript serwowanym z tej samej domeny leżała jednak stała z nazwą i adresem, bo warunek czytany w czasie wykonania nie pozwala bundlerowi wyciąć martwej gałęzi.

Decyzja przeniesiona na czas budowania: gałąź zwija się do literału, tekst znika z wyjścia. Skan całego zbudowanego serwisu nie znajduje ani jednego wystąpienia. Błąd wyszedł nie z przeglądu kodu, tylko z przeszukania artefaktu.

Dwie wersje językowe bez centralnej mapy adresów

Każdy język to osobne wdrożenie z własną bazą. Wpisy noszą stały, neutralny językowo klucz, a trasa rozwiązująca po drugiej stronie zamienia go na lokalny adres. Nie ma mapy do utrzymywania i nie ma centrali, którą trzeba by pytać o pozwolenie.

Praca z modelem

Kolofon powstał z Claude.ai przy klawiaturze. Różnica między dobrym a złym użyciem takiego narzędzia widać w kodzie, więc opisuję tu podział pracy tak samo konkretnie jak resztę architektury.

Podział jest jasny. Ograniczenia, architektura i kryteria akceptacji są moje. Rozstrzygnięcia też: propozycję centralnego katalogu wersji językowych odrzuciłem, bo wprowadzała pośrednika, przeciwko któremu ten silnik powstał. Przegląd, testy na zbudowanym artefakcie i wdrożenie na produkcję — moje. Odpowiedzialność za każdy błąd na tej liście — również moja, łącznie z tymi, których nie wyłapał żaden test, tylko własny telefon w niedzielę wieczorem.

Model przyspiesza pisanie i przeszukiwanie. Nie zdejmuje z nikogo obowiązku wiedzenia, dlaczego coś ma działać tak, a nie inaczej — a dwadzieścia dwa wpisy w serii o architekturze są zapisem właśnie tego rozumowania.

Jak pracuję

  • Konwencje spisane, nie zapamiętane. Każda pomyłka, która wróciła drugi raz, kończy się linijką w dokumentacji.
  • Testy na zbudowanym artefakcie, nie na serwerze deweloperskim. Różnica między nimi jest dokładnie tym miejscem, w którym mieszkają najdroższe błędy.
  • Kontrola rozjazdu przed synchronizacją. Narzędzie, które nadpisuje cudzą pracę bez ostrzeżenia, jest gorsze od braku narzędzia.
  • Ograniczenia platformy traktowane jak recenzent. Sporo rzeczy jest tu zrobionych dobrze dlatego, że zrobienie ich źle po prostu by nie zadziałało.
  • Koszt nazwany wprost. Każda decyzja w serii o architekturze ma opisaną cenę, którą się za nią płaci.

Kontakt

Szukam pracy zdalnej jako programista. Trzynaście lat backendu w C#, a ten projekt jest dowodem, że biorę odpowiedzialność za całość — od decyzji architektonicznej po wdrożenie i awarię o północy.