W zeszłym roku we wrześniu wprowadziliśmy dużą aktualizację Proton Mail na iOS i Androida.

Na powierzchni nowe aplikacje zapewniają nowoczesny design, lepszą wydajność i możliwości offline — ale kryje się za tym znacznie więcej. Za kulisami aplikacje są całkowitym przepisaniem Proton Mail na nowatorskim stosie technologicznym, projekcie o wewnętrznej nazwie Transformacja Inżynieryjna. Termin nowatorski jest celowy, ponieważ — według naszej najlepszej wiedzy — po raz pierwszy wybrana technologia została użyta w kontekście ustalonej aplikacji produkcyjnej.

Ten artykuł ma na celu rzucenie światła na fascynującą podróż, jaką przebył nasz zespół tworząc tę rewolucję, oraz odpowiedzenie na niektóre pytania, które zadawała nam społeczność po drodze. Przede wszystkim uzasadnieniem jest potrzeba zmiany status quo.

Jak to się wszystko zaczęło

Uświadomienie sobie, że rzeczy muszą się zmienić, uderzyło w piątkowy wieczór w październiku 2023 roku. Zmaterializowało się z zaskakującą jasnością, ale nie znikąd: było kulminacją miesięcy spędzonych na próbach znalezienia wspólnego mianownika dla pozornie niezwiązanych problemów wpływających na doświadczenia naszych użytkowników z produktami mobilnymi Mail i Kalendarz.

Ryzykując nadmierne uproszczenie, możemy podsumować punkty bólu w trzech obszarach:

  • Jakość: Mail iOS i Mail Android, traktowane w izolacji, nie spełniały oczekiwań pod względem jakości i wydajności.
  • Luka w funkcjach między iOS i Androidem: Niektóre funkcje były dostępne tylko na jednej platformie, bez jasności co do tego, kiedy druga nadgoni.
  • Prędkość inżynierii: Kluczowe aktualizacje i długo oczekiwane funkcje nie były dostarczane w odpowiednim czasie na obu platformach.

Niektóre problemy wykraczały poza mobile, a odpowiedź na nie wymagałaby dygresji z domeny technologii w fascynującą przestrzeń problemową skalowania organizacyjnego, a w szczególności szybko rozwijających się startupów technologicznych. Ale kruchość ekosystemu mobilnego była bardzo mocno zakorzeniona w technologii i architekturze.

Skalowanie inżynierii mobilnej

Skalowanie inżynierii mobilnej wiąże się z unikalnym zestawem wyzwań, które znacząco różnią się od skalowania zespołów backendowych i webowych. Różnice te wynikają z fragmentacji platform i realiów operacyjnych ekosystemu mobilnego. Zespoły mobilne zazwyczaj muszą wspierać wiele platform w różnych systemach operacyjnych i urządzeniach (telefony, tablety, czasem urządzenia wearables). iOS i Android mają własne języki programowania, frameworki i narzędzia, co prowadzi do dużej ilości powielonej pracy: wiele zespołów, zduplikowane bazy kodu i ciągłe kompromisy między pracą specyficzną dla platformy a związaną z produktem. Utrzymanie synchronizacji oferty produktowej wymaga ogromnej ilości koordynacji.

To, co jest wyzwaniem dla całej branży, było szczególnie dotkliwe dla Protona. Aplikacje funkcjonalne, takie jak Mail i Kalendarz, są z natury bardziej złożone niż większość aplikacji mobilnych na rynku. Kiedy dodasz do tego dodatkową warstwę logiki klienta wymaganą do obsługi szyfrowania end-to-end, kończysz ze szczególnie „grubymi” klientami. Dawniej zespół Androida był zajęty przepisywaniem Maila do lepszych standardów jakości — inwestycją, która zajęła znaczną część 18 miesięcy. iOS również pilnie potrzebował przebudowy architektury, nie wspominając o Kalendarzu. Koszt duplikacji pochłaniał wszystkie nasze zasoby inżynieryjne i stało się jasne, że nie odniesiemy sukcesu, robiąc więcej tego samego.

Najlepszą rzeczą w uświadomieniu sobie, że utknąłeś, jest to, że działa to jako czynnik wymuszający myślenie poza ograniczeniami obecnego status quo. Co zrobilibyśmy, gdybyśmy mogli zacząć od nowa, uwolnieni od ciężaru wyborów i zobowiązań, które nas tu doprowadziły? Kiedy przyjrzysz się bliżej, jak firmy, które odniosły sukces, radziły sobie z tym problemem w poprzedniej dekadzie, zdasz sobie sprawę, że podążały jedną z tylko dwóch możliwych strategii:

  1. Zasypywali problem pieniędzmi, budując coraz większe zespoły, ponieważ wysokie koszty operacyjne były równoważone kombinacją inwestycji bez dna i/lub ogromnych zwrotów. Nie była to opcja dla modelu biznesowego Proton wolnego od VC: nie możemy konkurować z wydatkami konkurentów opartych na reklamach i wspieranych przez inwestorów.
  2. Przeprojektowali swoje aplikacje, aby pozbyć się marnotrawstwa, co oznacza budowanie aplikacji przy użyciu (w miarę możliwości) udostępnianej bazy kodu.

Ponieważ opcja 1 nie wchodziła w grę, droga naprzód była ustalona.

Środek do celu: wybór odpowiedniego stosu technologicznego

Kolejnym krokiem był wybór stosu technologicznego, który faktycznie wykonałby zadanie.

W ciągu ostatnich 15 lat wieloplatformowy rozwój mobilny został zalany rozwiązaniami „uniwersalnymi”: HTML5, Xamarin, React Native, Flutter, Kotlin Multiplatform i wiele innych. Każde z nich przybyło z tą samą obietnicą — całkowitego zastąpienia rozwoju natywnego. W praktyce większość albo całkowicie zawiodła, albo odniosła sukces tylko w ściśle ograniczonych przestrzeniach problemowych. Nie ma uniwersalnej abstrakcji, która sprawiłaby, że różnice między platformami znikną: każdy, kto dostarczył i utrzymywał duże aplikacje mobilne, o tym wie. Jedynym niezawodnym sposobem naprzód jest praca wstecz od konkretnych wymagań, a nie do przodu od trendów narzędziowych.

Przełożyliśmy ten cel końcowy na zestaw nienegocjowalnych wymagań (1), które każde wybrane rozwiązanie musiało spełnić, i użyliśmy ich jako naszych ram przewodnich w procesie ewaluacji:

  1. Koszty i ramy czasowe: Stos musiał materialnie zmniejszyć koszt i czas wymagany do dostarczenia, utrzymania i rozwoju Proton Mail na iOS i Androida.
  2. Doświadczenie użytkownika: Musiał zachować wydajność i jakość interakcji bliską natywnej — cokolwiek mniej nie wchodziło w grę.
  3. Strategiczne zabezpieczenie na przyszłość: Rozwiązanie musiało być długowieczne. Byliśmy zdecydowani unikać frameworków stron trzecich, które uzależniłyby naszą mapę drogową od ciągłego wsparcia innego dostawcy.

Napięcie między dwoma pierwszymi ograniczeniami to branżowa wersja świętego graala: „Rozwiązanie wieloplatformowe, które zapewnia wydajność i doświadczenie użytkownika natywnych aplikacji”.

Od początku byliśmy sceptyczni, że React Native lub Flutter — dwa dominujące frameworki wieloplatformowe w tamtym czasie — mogłyby spełnić ten wymóg. Mimo to zweryfikowaliśmy ten sceptycyzm, budując implementacje proof-of-concept widoku listy wiadomości Mail.

React Native szybko ujawnił swoje ograniczenia. Przewijanie dużego zbioru danych sprawiło, że koszt jego interpretowanego modelu wykonywania stał się boleśnie oczywisty. Flutter wypadł lepiej, ale interfejs użytkownika pozostał widocznie nienatywny, zwłaszcza na iOS. Co ważniejsze, Flutter jest zastrzeżonym frameworkiem kontrolowanym przez Google, które ma historię(nowe okno) porzucania technologii wewnętrznych i niedawno zwolniło dużą część zespołu Fluttera. W przypadku produktu z długoterminowymi gwarancjami bezpieczeństwa i niezawodności taki poziom zewnętrznej zależności był nie do przyjęcia.

Kolejnym kandydatem był Kotlin Multiplatform. Jest to atrakcyjna opcja — szczególnie dla organizacji z głęboką wiedzą o Androidzie — ale ostatecznie nie sprawdziła się w naszym przypadku użycia. Brak udostępnianej warstwy UI, pytania dotyczące dojrzałości i dodatkowy narzut wprowadzony przez jego model wykonywania przeważyły nad korzyściami.

W tym momencie wniosek był jasny i zgodny z naszą początkową intuicją: jedyną architekturą, która konsekwentnie zbliża się do pożądanego rezultatu, jest celowo mieszany stos. Natywne UI na każdej platformie — Jetpack Compose na Androidzie, SwiftUI na iOS — wspierane przez udostępnianą warstwę logiki biznesowej napisaną w wysokowydajnym języku niskiego poziomu. Podejście to ma swoją historię: Dropbox słynął z używania C++ do udostępniania logiki biznesowej na platformach mobilnych, zanim porzucił go w 2019 roku ze względu na koszt operacyjny i poznawczy języka.

Do końca 2023 roku Rust wyraźnie wyłonił się jako następca w linii języków programowania systemowego.

Rust zajmuje tę samą przestrzeń wydajności co C++, ale bez wielu jego historycznych obciążeń. Zapewnia silne gwarancje bezpieczeństwa pamięci bez garbage collection, wymusza bezpieczną dla wątków współbieżność w czasie kompilacji i jest wspierany przez duży, wysoce kompetentny ekosystem open-source. Co równie ważne, Rust integruje się czysto z natywnymi językami mobilnymi — Swift i SwiftUI na iOS, Kotlin i Jetpack Compose na Androidzie — co czyni go pragmatycznym wyborem do udostępniania podstawowej logiki bez kompromisów w warstwie UI.

To nie była decyzja wolna od ryzyka. W tamtym czasie istniało niewiele przykładów wielkoskalowych aplikacji mobilnych skierowanych do konsumentów zbudowanych na architekturze skoncentrowanej na Rust, a doświadczenie z Rust w zespole było ograniczone.

Ale znaczące innowacje rzadko zdarzają się na terytorium niskiego ryzyka. Prawdziwym wyzwaniem nie był sam Rust, ale inercja organizacyjna — przejście od sprawdzonych, konserwatywnych podejść w kierunku celowego eksperymentowania, kierowanego jasnymi ograniczeniami i osądem inżynierskim.

Nowy Proton Mail: wynik i wnioski

Przenieśmy się do dnia dzisiejszego i zobaczmy, jak potoczył się ten zakład.

Poniższy diagram przedstawia architekturę mobilną Mail. Rdzeń Rust odpowiada za całość logiki biznesowej aplikacji. Rozszerzyliśmy wykorzystanie języka Rust poza jego typowe zastosowania (sieć, pamięć masowa, obliczenia algorytmiczne) aż do obsługi złożonej logiki nawigacji. Dobrym przykładem jest logika zarządzająca nieskończonym przewijaniem listy wiadomości. Chociaż jest to niekonwencjonalne, okazało się kluczowe dla osiągnięcia naszego celu, jakim jest maksymalizacja ponownego wykorzystania kodu. W rezultacie prawie 80% bazy kodu jest teraz udostępniane między iOS i Androidem.

Diagram Architektury dzięki uprzejmości Leandera Beernaerta, 2026
Diagram Architektury dzięki uprzejmości Leandera Beernaerta, 2026

Czy przełożyło się to na szybszy, wyższej jakości czas wprowadzenia na rynek? Chociaż jest jeszcze za wcześnie na ostateczny werdykt, wczesne sygnały są bardzo zachęcające:

  • W ciągu dwóch miesięcy po wydaniu zespół zdołał utrzymać tygodniowy rytm aktualizacji funkcji na obu platformach (łącznie 12 wydań funkcji).
  • Zamknęliśmy luki w funkcjach między platformami, wprowadzając długo oczekiwane funkcje na Androida, takie jak drzemka, RSVP kalendarza i przesunięcie, aby przejść do następnej wiadomości.
  • Nawet na tym wczesnym etapie nowa baza kodu okazała się bardziej stabilna niż poprzednie generacje na obu platformach: wskaźnik awarii iOS wynosi 0,05% (spadek z 0,12%), podczas gdy Androida wrócił do historycznego poziomu bazowego (0,19%). Jest to silne potwierdzenie stabilności środowiska uruchomieniowego Rust.

Wsparcie również skaluje się skuteczniej przy tym podejściu. Często szybciej jest zidentyfikować i rozwiązać pojedynczą, wspólną przyczynę źródłową niż ścigać powierzchownie podobne problemy wynikające z nieco innych błędów logicznych rozproszonych w dwóch niezależnych bazach kodu. Znaleźliśmy empiryczne potwierdzenie tego, co wcześniej było hipotezą roboczą, naprawiając klasę problemów synchronizacji kategorii wpływających na logikę leżącą u podstaw możliwości offline aplikacji: jedna przyczyna źródłowa, jedno rozwiązanie — reprezentowane na powyższym diagramie przez moduł Rebasing dostarczony z wersją 7.6.2.

Druga strona medalu?

  • Błędy i regresje prawdopodobnie będą miały szerszy wpływ i dotkną użytkowników na obu platformach. Nie można mieć wszystkiego — ale zdecydowanie można ograniczyć ryzyko poprzez nadmierne indeksowanie testów end-to-end (E2E).
  • Podobnie jak w przypadku każdego podziału rozwiązania skierowanego do użytkownika wzdłuż poziomej granicy technologicznej, istnieje ryzyko stworzenia silosów wiedzy i utraty części inżynieryjnego skupienia na doświadczeniach użytkownika end-to-end. Musisz być tego świadomy i celowo ograniczać ryzyko. Wśród najskuteczniejszych środków:
    • Dostosuj podzespoły do dostarczania funkcji, a nie warstw technologicznych.
    • Szkol inżynierów mobilnych, aby stali się „full stack”, tj. zdolni do debugowania, wsparcia i inżynierii zarówno w bazie kodu Rust, jak i na platformach natywnych.

Co dalej z Transformacją Inżynieryjną

Od samego początku tego projektu było jasne, że stawka wykracza daleko poza sam Proton Mail. Pomyślne zastosowanie tego stosu technologicznego do flagowej aplikacji Proton zawsze miało być pierwszym krokiem w dłuższej podróży — takiej, która ostatecznie doprowadzi do wdrożenia tego podejścia w pozostałej części naszego ekosystemu mobilnego.

Ten scenariusz właśnie się realizuje. W chwili pisania tego artykułu nasze SDK Konta i Płatności, a także kolejna generacja aplikacji mobilnych Proton Calendar, są przepisywane zgodnie z tym nowym kierunkiem technicznym.

To oznacza początek drugiej fali transformacji inżynieryjnej — ewolucji, która rozszerza plan technologiczny o ramy architektoniczne zaprojektowane tak, aby ułatwić ponowne wykorzystanie komponentów, nie tylko między platformami, ale także między produktami. Chociaż to przejście nie nastąpi z dnia na dzień, jest fundamentalne dla budowy płynnie zintegrowanego, stawiającego na prywatność ekosystemu, jakiego nasi klienci oczekują od Protona.

(1): Simon Lewis,“A strategy for application implementation on multiple platforms”, 2023.