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ś
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.
Kolofon