24 grudnia. Większość ludzi nie pisze kodu w Wigilię.
Ja miałem w kolejce buga, który czekał kilka dni: wylogowanie z aplikacji mobilnej nie działało poprawnie. Użytkownik klikał „wyloguj” — i wyglądało na to, że się wylogował. Ale po chwili aplikacja zachowywała się tak, jakby sesja wciąż była aktywna.
Postanowiłem zamknąć to przed końcem roku.
Dwa managery
Flutter, na którym stoi mobilka, ma kilka popularnych rozwiązań do zarządzania stanem. W pewnym momencie aplikacja używała dwóch naraz — każdy do czegoś innego.
Jeden zarządzał tokenami: przechowywał je, odświeżał, wiedział, kiedy wygasają. Drugi zarządzał stanem UI: wiedział, czy użytkownik jest „zalogowany” z perspektywy ekranów, nawigacji, widoczności elementów.
Przez długi czas działało to sprawnie, bo oba systemy były zsynchronizowane przez zdarzenia. Jeden emitował, drugi słuchał.
Problem pojawił się w scenariuszu wylogowania. Wylogowanie wysyłało sygnał — ale jeden z systemów przestał go odbierać. Czy zmieniła się kolejność inicjalizacji, czy zdarzenie było wysyłane za wcześnie — trudno powiedzieć. Efekt: jeden system wiedział o wylogowaniu, drugi — nie.
Co to oznacza dla użytkownika
Użytkownik klikał „wyloguj”. Ekran logowania się pojawiał — wszystko wyglądało poprawnie. Ale w tle system tokenów wciąż miał ważne dane. Przy następnym uruchomieniu aplikacji lub po odświeżeniu — sesja wracała.
Dla użytkownika z jednym telefonem to uciążliwość. Dla kogoś, kto loguje się na wspólnym urządzeniu — to poważny problem bezpieczeństwa.
Naprawa
Rozwiązanie było prostsze niż przyczyna: jeden punkt wejścia dla operacji „wyloguj”.
Zamiast każdego systemu reagującego na zdarzenie — jedna funkcja, która wywołuje oba systemy po kolei, w określonej kolejności, i sprawdza, czy oba zakończyły pracę przed zamknięciem sesji.
Dwa niezależne systemy zostały. Ale ich koordynacja przestała polegać na zdarzeniach — teraz jest jawna, sekwencyjna i weryfikowalna.
Bug był widoczny w testach integracyjnych, które napisałem kilka tygodni wcześniej. Test scenariusza „wyloguj i sprawdź stan” padał — i to był sygnał, że coś jest nie tak. Gdyby nie ten test, mógłbym nie zauważyć problemu przez kolejne tygodnie.
Dwa niezależne systemy do zarządzania sesją to decyzja, która technicznie miała sens — każdy miał inne API, inne możliwości. W retrospektywie: ta złożoność przez kilka miesięcy czekała na to, żeby się objawić. Zasada, którą z tego zabieram: gdy dwa systemy zarządzają tym samym stanem — jeden musi być wyraźnie nadrzędny.