
Ile naprawdę kosztuje wersja 2: 206 commitów uczciwych stanów
GeekBye v2 nie było wydaniem funkcji. To 206 commitów wymierzonych w jedną myśl: aplikacja nigdy nie powinna kłamać na temat własnego stanu. Oto ile to kosztuje — łącznie z jednolinijkowym błędem w lockfile, który omal nie powstrzymał nas przed wypuszczeniem czegokolwiek z tego.
Większość wydań „wersji 2" to sterta nowych funkcji z większą liczbą na okładce. GeekBye v2 było przeciwieństwem. Nie przyniosło niemal żadnej nowej zdolności widocznej dla użytkownika. Jego 206 commitów było wymierzonych w jedną, mało efektowną myśl:
Aplikacja nigdy nie powinna pokazywać ci stanu, który nie jest prawdziwy.
Brzmi to oczywiście, dopóki nie policzysz, na ile sposobów aplikacja desktopowa po cichu kłamie. Pokazuje ptaszek przy uploadzie, który wciąż jest w locie. Mówi „połączono" nad gniazdem, które padło minutę temu. Milczy, gdy twoja transkrypcja jest gubiona. Żadna z tych rzeczy nie jest awarią. Są gorsze od awarii, bo aplikacja wygląda dobrze, będąc w błędzie — a dowiadujesz się dopiero później, gdy nagrania, którego potrzebowałeś, nie ma.
v2 było wydaniem, w którym ruszyliśmy na polowanie na każde z tych drobnych kłamstw.
Kłamstwo, którego nienawidziliśmy najbardziej: upload, który „się udał"
GeekBye potrafi robić kopie zapasowe twoich nagrań na Google Drive. W v1 połączenie z Drive było traktowane jako szczegół w tle — jeśli działało, świetnie; jeśli nie, awaria była w większości niewidoczna. Aplikacja dalej wyglądała na połączoną. Zakładałeś, że twoje nagrania są bezpieczne. Czasem nie były.
v2 przebudowało to wokół uczciwych stanów połączenia. Drive jest teraz zawsze w jednym z kilku jawnych, prawdziwych stanów — połączony, ponownie łączy się, rozłączony — a aplikacja rozgłasza każdą zmianę tego stanu do całego interfejsu w chwili, gdy się dzieje. Gdy połączenie jest zerwane, globalny baner ponownego łączenia mówi ci to, wszędzie, wprost. Aplikacja nie udaje już, że upload się udał, gdy tak nie było. Jeśli twoja kopia zapasowa nie może teraz powstać, wiesz o tym teraz — a nie za tydzień, gdy pójdziesz szukać pliku.
Pod spodem to niewielka architektura, a nie slogan: jedno źródło prawdy dla połączenia, zdarzenie, które rozchodzi się do każdej części interfejsu, gdy się zmienia, oraz baner czytający wprost z tego stanu. Nie ma ścieżki, w której połączenie jest zerwane, a interfejs wygląda na sprawny, bo czytają z tego samego faktu.
Kłamstwo utraconego nagrania
Drugą wielką naprawą uczciwości było odzyskiwanie nagrań. Transkrypcja w czasie rzeczywistym potrzebuje żywego połączenia. W v1, jeśli to połączenie zacinało się w połowie sesji — mignięcie Wi-Fi, tunel, ponowne łączenie VPN — audio z czasu przerwy mogło po prostu zniknąć. Transkrypcja miała dziurę, i nic ci o tym nie mówiło.
v2 zmieniło kontrakt: audio jest zachowywane lokalnie podczas przerwy w połączeniu i uzgadniane później. Gdy łącze pada, GeekBye trzyma audio bezpiecznie na twojej maszynie; gdy wraca, to zbuforowane audio jest wysyłane i wszywane w transkrypcję we właściwym miejscu. Złe trzydzieści sekund sieci nie kosztuje cię już trzydziestu sekund spotkania. To jest fundament, na którym późniejsze wydania niezawodnościowe budowały wprost — maszyneria ponownego łączenia i buforowania z dlaczego Twój notatnik AI zatrzymuje się przy słabym Wi-Fi to ta sama idea, tylko zahartowana.
Kłamstwo burzy wyskakujących okienek
Jest subtelniejsza nieuczciwość w tym, jak aplikacje raportują kłopoty: raportują je za dużo. Jeden niestabilny moment sieci potrafi wystrzelić ten sam błąd pięć razy, i nagle masz stos identycznych czerwonych toastów zasypujących jedyną wiadomość, która ma znaczenie. To też nie jest uczciwe — to szum udający informację.
Więc v2 dodało dławienie toastów kluczowane po kategorii oraz kierowanie błędów. Błędy są grupowane według tego, czym naprawdę są, a każda kategoria ma limit tempa, żeby jeden leżący u podstaw problem dawał jedną jasną wiadomość, a nie kanonadę. Problem z limitem tempa kieruje do wiadomości o limicie tempa; problem z połączeniem kieruje do wiadomości o połączeniu. Aplikacja mówi ci co jest prawdą, raz — ta sama dyscyplina, która później uchroniła 429 przed podszywaniem się pod wylogowanie w dniu, w którym nasza aplikacja zDDoSowała samą siebie.
Liczba 206 to sedno
Uczciwe stany, baner ponownego łączenia, odzyskiwanie nagrań, kierowanie toastów — to cztery idee. Wydanie liczyło 206 commitów. Gdzie poszła reszta?
W nieefektowny długi ogon, którego „nigdy nie pokazuj fałszywego stanu" faktycznie wymaga. Każde miejsce, w którym stary interfejs mógł rozjechać się z rzeczywistością, trzeba było znaleźć i przewłączyć tak, by czytał z prawdy, a nie z nieaktualnej kopii. Każda ścieżka ponownego łączenia musiała aktualizować współdzielony stan, zamiast zgadywać lokalnie. Dziesiątki drobnych napraw niezawodności w nagrywaniu, transkrypcji i uploadach — każda zamykająca konkretną szczelinę, w której aplikacja mogła wyglądać dobrze, będąc w błędzie.
Tyle kosztuje prawdziwa „wersja 2", gdy celem jest zaufanie, a nie funkcje. Nie ma jednego nagłówkowego commita. Jest 206 małych, a wydanie sprawia wrażenie jednej rzeczy — spokoju — tylko dlatego, że wszystkie 206 ciągną w tym samym kierunku.
Wydanie omal nie wyszło
Oto opowieść wojenna, i to dobra, bo jest o tym, jak nasza własna aplikacja skłamała nam.
Gdy tniesz wydanie, skrypt podbijający wersję stempluje nowy numer w całym projekcie. Przy v2.0.0 zaktualizował wersję aplikacji — ale package-lock.json, dokładna księga zależności npm, został wskazujący na starą wersję, 1.9.0. Lokalnie wszystko było w porządku; nikt nie przeinstalowuje zależności tylko po to, żeby zbudować. Ale CI uruchamia npm ci, a całym zadaniem npm ci jest odmówić dalszego działania, jeśli lockfile nie zgadza się z manifestem. To funkcja surowości — i zrobiła dokładnie to, co powinna, kładąc build przy największym wydaniu, jakie kiedykolwiek cięliśmy.
Potem pod spodem wypłynął drugi, podstępniejszy. Nowszy npm wyciął opcjonalną przechodnią zależność — pakiet encoding, który wciąga node-fetch — z lockfile jako zbędną. Tyle że czysta instalacja naszego CI tego potrzebowała, więc instalacja padła w sposób, który nie miał nic wspólnego z naszym kodem, a wszystko ze subtelnie błędną księgą.
Oba były jednolinijkowymi naprawami: zsynchronizować lockfile z prawdziwą wersją, przywrócić wyciętą pozycję. Oba są też idealnymi, pokornymi przykładami dokładnie tego, o czym było v2 — stanu, który twierdził, że jest prawdziwy, a nie był. Lockfile ma być uczciwym zapisem tego, od czego zależy aplikacja. Gdy odjechał od rzeczywistości, build zrobił dokładnie to, co nasza aplikacja robi teraz dla użytkowników: odmówił udawania, że wszystko jest w porządku. Wypuszczenie wydania niezawodnościowego okazuje się problemem niezawodności aż do samego dna.
Trzy rzeczy, których nauczyło nas v2
- Groźne awarie to te ciche. Awaria się ogłasza. Fałszywe „połączono", widmowy udany upload, transkrypcja z niewidzialną dziurą — to kosztuje cię zaufanie właśnie dlatego, że nic nie wygląda źle. Poluj na ciche kłamstwa.
- Uczciwość to architektura, a nie komunikat. Nie doczepisz „mów prawdę" do interfejsu jako banera. Baner musi czytać z tego samego jedynego źródła prawdy co wszystko inne, albo staje się kolejną rzeczą, która może być błędna. Jeden fakt, rozchodzący się na zewnątrz — nigdy dwie kopie, które mogą się różnić.
- Wersja 2 warta swojego numeru jest w większości niewidoczna. Jeśli twoje v2 to lista funkcji, to v1.5 z marketingiem. Prawdziwe v2 to 206 commitów, na które nikt nie potrafi wskazać z osobna, składających się na produkt, który po prostu przestaje ci kłamać.
GeekBye v2.0.0 to fundament, na którym budowało każde wydanie od tamtej pory. O tym, co ten fundament niesie, przeczytaj dlaczego Twój notatnik AI zatrzymuje się przy słabym Wi-Fi, dzień, w którym nasza aplikacja zDDoSowała samą siebie (v2.0.1) oraz transkrypcja na żywo, gdy firewall blokuje WebSockety (v2.0.8). O spokoju, któremu wszystko to służyło, co nowego w GeekBye v2.