Steven
Steven6 min czytania

Jedna zmienna CSS, pięć rund review i toolchain Swift, który skłamał

GeekBye v2.0.7 uczyniło całą nakładkę regulowanie przezroczystą — co brzmi jak jednolinijkowa zmiana w CSS i absolutnie nią nie było. Prawdziwa historia to to, co wychwyciło code review: dwie powierzchnie, które odmawiały gaśnięcia, pasek nagrywania, który wyglądał źle przy tej samej przezroczystości, oraz skompilowany plik binarny, który skakał o 672 bajty, bo nasza własna dokumentacja podała recenzentowi błędną wersję Swift.

Inżynieria
Projektowanie
Kompilacja
Wydania GeekBye
Jedna zmienna CSS, pięć rund review i toolchain Swift, który skłamał

„Uczyń nakładkę przezroczystą" to rodzaj zadania, które brzmi jak suwak i właściwość CSS opacity. GeekBye v2.0.7 dostarczyło dokładnie ten widoczny dla użytkownika efekt — jeden regulowany „tryb jasny", który pozwala przejrzeć panele asystenta na wylot, na to, co jest za nimi. Ale ciekawa część tego wydania to nie funkcja. To pięć rund review, których wymagało doprowadzenie jej do porządku, oraz opowieść wojenna o skompilowanym pliku binarnym, który zmienił rozmiar, bo nasza własna dokumentacja skłamała recenzentowi.

Właściwy sposób, by uczynić cały interfejs przezroczystym

Naiwna wersja tego polega na rozsypaniu wartości opacity po każdym komponencie panelu i uznaniu sprawy za zamkniętą. To pułapka utrzymaniowa: tuzin zakodowanych na sztywno wartości, które rozjeżdżają się w chwili, gdy ktokolwiek dotknie jednej z nich.

Zamiast tego v2.0.7 używa jednego współdzielonego narzędzia CSS i tokena per powierzchnia. Jest pojedyncza klasa overlay-surface, która oblicza tło panelu z dwóch właściwości niestandardowych: bazowej przezroczystości, którą każda powierzchnia deklaruje dla siebie (pasek sterowania, czat, dymki transkrypcji, bloki kodu i tak dalej — każdy wybiera własną), oraz globalnego mnożnika alfa, który jedzie na korzeniu nakładki i reprezentuje ustawienie suwaka użytkownika. Każda powierzchnia czyta ten sam mnożnik; każda zachowuje własny charakter. Co kluczowe, skaluje się tylko tło panelu — tekst, ikony i backdrop-blur pozostają w pełni wyrenderowane, więc „przezroczysty" nigdy nie oznacza „nieczytelny".

Dwie decyzje w tym projekcie warto wyróżnić, bo to one stanowią różnicę między funkcją, która pomaga, a taką, która generuje zgłoszenia do supportu:

  • Domyślnie wszystko jest w pełni nieprzezroczyste. Ustawienie przezroczystości startuje na 100% — czyli bez efektu. Nikomu nakładka nie stała się nagle przezroczysta po aktualizacji; przezroczystość jest, gdy po nią sięgniesz, i niewidoczna, dopóki tego nie zrobisz.
  • Ustawienie jest walidowane defensywnie. Jest jedno źródło prawdy dla minimum, maksimum, wartości domyślnej i kroku, oraz normalizator, który pilnuje przypadku pustego/brakującego. To ma znaczenie z powodu pułapki JavaScriptu: Number(null) to 0, więc brakujące ustawienie naiwnie przekonwertowane rzuciłoby nakładkę w jej najbardziej przezroczysty stan zamiast domyślnego. Normalizator wychwytuje to, zanim zdąży się wydarzyć.

Przeciągnij suwak, a cała nakładka przebarwia się na żywo, w trakcie nagrywania, bo każda powierzchnia jest zasubskrybowana do tego samego ustawienia. Bez restartu, bez przeładowania.

Co wychwyciło review (a test na jednym monitorze nigdy by nie wychwycił)

Tu „jedna zmienna CSS" przestała być prosta. W review wypłynęły trzy błędy, a dwa z nich to ta sama lekcja w innym kapeluszu.

Panel, który nie chciał gasnąć. Nakładka skrótów klawiszowych uparcie pozostawała solidna, podczas gdy wszystko inne stawało się przezroczyste. Przyczyna: renderuje się poza poddrzewem DOM, które niesie zmienną przezroczystości, więc nigdy nie odziedziczyła wartości spływającej kaskadą z korzenia nakładki. Właściwości niestandardowe CSS dziedziczą przez drzewo DOM — i jeśli węzeł nie jest w rzeczywistości potomkiem miejsca, w którym ustawiasz zmienną, po cichu nie dostaje nic. Naprawą było ustawienie zmiennej lokalnie także na tym panelu.

Podświetlenie hover, które znikało. Przyciski Fn/Assist straciły cyjanową poświatę hover. Narzędzie przezroczystości ustawiało tło z !important (musi wygrać z domyślnymi ustawieniami powierzchni), a to zwaliło z nóg podświetlenie tła hover: na tym samym elemencie. Kolizja specyficzności. Naprawą było uczynienie podświetlenia hover równie stanowczym, by mogło odzyskać przewagę przy najechaniu.

Oba te przypadki to ta sama leżąca u podstaw prawda: kaskada zmiennej CSS to realna granica poprawności, a nie szczegół stylizacji. Dziedziczenie i specyficzność decydują o tym, czy twoja wartość w ogóle dociera do elementu, a żadne z nich nie ujawnia się na zrzucie ekranu, dopóki nie jest błędne.

Ta sama przezroczystość, inny wygląd. Najsubtelniejszy: pasek nagrywania i pasek bezczynności były ustawione na identyczną bazową przezroczystość, a mimo to wyglądały widocznie inaczej. Dlaczego? Pigułka nagrywania używała cięższego backdrop-blur, a mocne rozmycie na ciemnym tle uśrednia w stronę ciemniejszego rezultatu — więc równa alfa dawała nierówny wygląd. Miał też słabe obramowanie tam, gdzie pasek bezczynności miał jasną krawędź z pierścieniem gradientowym. Naprawa dopasowała poziom rozmycia i pierścień. Lekcja przylgnęła: równa alfa to nie równy wygląd, gdy w grę wchodzi rozmycie. Przezroczystość to system oddziałujących na siebie efektów, a nie pojedyncza liczba.

Plik binarny, który zmienił rozmiar, bo dokumentacja skłamała

Teraz opowieść wojenna, i to mój ulubiony rodzaj — ten, w którym narzędzia i dokumentacja się nie zgadzają, a staranny recenzent zostaje złapany w krzyżowym ogniu.

GeekBye dostarcza kilka wstępnie skompilowanych plików binarnych Swift (natywne pomocniki do rzeczy takich jak wykrywanie spotkań). Ponieważ skompilowany plik binarny jest zacommitowany do repo, każdy deweloper musi go skompilować dokładnie tą samą wersją Swift — różne toolchainy produkują różne bajty z identycznego źródła, a to oznaczałoby fantomowy konflikt git za każdym razem, gdy ktoś go przebudowuje. Jest więc skrypt, który egzekwuje jedną konkretną wersję: Swift 6.3.x, sprawdzaną w czasie budowania.

Podczas review tego wydania recenzent zasygnalizował, że plik binarny powinien zostać przebudowany tak, by pasował do „toolchaina wymaganego przez repo". Deweloper zrobił dokładnie to — a rozmiar pliku binarnego zmienił się z 89,184 bajtów na 88,512. Wiadomość commita mówiła nawet, że „przywraca bajtowo stabilne wyjście" przez przebudowanie z użyciem Swift 6.2.1, „toolchaina wymaganego przez repo".

Tyle że 6.2.1 była błędną wersją. Skrypt egzekwujący build wymagał 6.3.x. Problem polegał na tym, że prozatorska dokumentacja — README i przewodnik dla kontrybutorów — wciąż w kilku miejscach mówiła 6.2.1. Więc sumienny recenzent i sumienny deweloper, obaj próbując zrobić właściwą rzecz, poszli za dokumentacją prosto w zacommitowanie dokładnie tego artefaktu, którego odrzucenie jest sensem istnienia skryptu egzekwującego. Plik binarny 6.2.1 w repo 6.3.x to dokładnie ten rozjazd, który zepsułby czystą przebudowę każdemu innemu deweloperowi.

Następny commit to naprawił — i ta naprawa jest sednem. Nie tylko przebudował plik binarny z powrotem do jego formy 6.3.x (rozmiar przywrócony do 89,184, dokładnie tam, gdzie zaczynał). Poprawił też nieaktualne odwołania do „6.2.1" w dokumentacji, żeby pułapka nie mogła zostać zastawiona ponownie. Plik binarny skoczył o 672 bajty i z powrotem w ciągu dwóch commitów, a prawdziwym błędem nigdy nie był plik binarny. To dwa źródła prawdy — skrypt egzekwujący i pisana dokumentacja — się nie zgadzały, a człowiek zaufał niewłaściwemu.

Trzy rzeczy, których nauczyło to wydanie

  1. Kaskada zmiennej CSS to granica poprawności. Dwa z trzech błędów z review sprowadzały się do zmiennej, która nie odziedziczyła, oraz !important, który zwalił stan hover. Jeśli wartość musi dotrzeć do elementu przez dziedziczenie albo wygrać przez specyficzność, ta ścieżka to logika — weryfikuj ją jak logikę, a nie jak stylizację.
  2. Równe parametry mogą dawać nierówne rezultaty. Pasek nagrywania i pasek bezczynności współdzieliły przezroczystość, a mimo to wyglądały inaczej, bo inne rozmycie zmieniło wynik. Gdy ujednolicasz system wizualny wokół jednej liczby, sprawdź, czy inne zmienne wokół każdej powierzchni po cichu nie zmieniają tego, co ta liczba znaczy.
  3. Gdy dokumentacja i egzekwowanie się nie zgadzają, napraw niezgodność — a nie tylko objaw. Błędem nie był zły plik binarny; to dokumentacja nazywała błędny toolchain, a skrypt egzekwujący mówił co innego. Przebudowanie pliku binarnego leczy objaw. Uzgodnienie każdego źródła prawdy jest faktyczną naprawą, bo następny recenzent też zaufa dokumentacji.

GeekBye v2.0.7 dostarczyło jednolitą przezroczystość, ze wszystkimi przypadkami brzegowymi zrewidowanymi i uporządkowaną dokumentacją toolchaina. To ostatnie z wydań „cichego rzemiosła" przed sprintem niezawodnościowym, który po nim nastąpił. O fundamencie pod spodem przeczytaj ile naprawdę kosztuje wersja 2 (v2.0.0); o siostrzanym wydaniu dopieszczającym spokojne oprogramowanie: naprawa migotania i chip trybu odpowiedzi (v2.0.3 + v2.0.5); a o tym, jak nakładka pozostaje ukryta podczas rozmowy, jak pozostać niewidocznym podczas udostępniania ekranu.

Powiązane artykuły

Spokojne oprogramowanie: jak zabiliśmy migający przełącznik i nauczyliśmy czat mówić, w jakim jest trybie
Steven
Steven6 min czytania

Spokojne oprogramowanie: jak zabiliśmy migający przełącznik i nauczyliśmy czat mówić, w jakim jest trybie

Dwa niewielkie wydania GeekBye, bez żadnej sztandarowej funkcji — tylko przełącznik w ustawieniach, który przestał migać, i asystent czatu, który wreszcie mówi ci, czy odpowiedział w trybie spotkania, czy w trybie kodowania. Oto ile naprawdę kosztuje „spokojne oprogramowanie", jeden szczegół po drugim.

Produkt
Projektowanie
Inżynieria
Anatomia dowożenia oprogramowania do perfekcji: jak przegląd kodu wyłapał to, czego testy nie mogły
Steven
Steven6 min czytania

Anatomia dowożenia oprogramowania do perfekcji: jak przegląd kodu wyłapał to, czego testy nie mogły

W całej serii GeekBye v2 powtarza się to samo: poprawka przechodzi każdy test na maszynie programisty, a potem przegląd kodu dowodzi, że zawiodłaby niemal u każdego. Oto workflow stojący za dziewięcioma wydaniami — bramka przeglądu, wychwyty na etapie poprawki i dyscyplina testowania-przed-wysyłką, która zamienia „u mnie działa" w „działa".

Inżynieria
Proces
Przegląd kodu
Twoja aplikacja na Maca dostaje dostęp do mikrofonu — i zapomina o nim przy każdym uruchomieniu
Steven
Steven5 min czytania

Twoja aplikacja na Maca dostaje dostęp do mikrofonu — i zapomina o nim przy każdym uruchomieniu

GeekBye poprosił o dostęp do mikrofonu, przyznałeś go, i zadziałało. Następne uruchomienie: zniknął. A aplikacja w ogóle nie pojawiła się w Ustawieniach systemowych → Mikrofon. Winowajcą była funkcja bezpieczeństwa macOS, po cichu uruchamiająca aplikację ze ścieżki, która znika — oto diagnoza i naprawa jednym kliknięciem.

macOS
Uprawnienia
Inżynieria