„Własny, uniwersalny framework” – historie z placu boju
„Własny framework” nigdy nie brzmi dobrze kiedy klient opowiada o swoich projektach. Dołóżmy do tego takie zwroty jak „spójny styl” i „uniwersalne komponenty” i tragedia gotowa.
Słowem wstępu
Pracujemy z klientem jako część jego działu IT więc trochę rzeczy robią nasi developerzy, trochę developerzy klienta, korzystamy z tego co oni itd.
I jedną z rzeczy, która jest obecna w ich firmie to framework do frontendu. Zbudowany w oparciu o jakieś gotowe rozwiązanie ale mocno zmodyfikowany. Zamysł był taki, ze każdy zespół może go po prostu wziąć i użyć gotowe komponenty angularowe/knockoutowe. Bez pisania własnych styli czy zachowań mieć gotowy layout, do którego tylko pcha dane albo jakieś customowe zachowania. Utrzymaniem tego frameworka zajmuje się dział UI/UX.
No i akurat w tym momencie robię typowy korpo-projekcik czyli „excel w przeglądarce”. Na początku, jakiś rok temu, sami wszystko musieliśmy naklepać, potem wróciliśmy do tego projektu z nowymi funkcjonalnościami i właśnie ten UIowy framework zaczął być portowany na Angulara. Zmienił się firmowy layout i osoby z działu odpowiedzialnego za frontendowy framework zgodziły się na przeportowanie również naszej aplikacji na nową wersję.
Pomijając jakość kodu wygenerowanego w trakcie tej migracji (nie byli częścią zespołu więc też dużego refactoru nie robili) to okazało się, że aplikacja, która działała całkiem nieźle (sporo czasu spędziliśmy na optymalizacji dużej rozwijanej tabeli) zaczęła mocno obciążać przeglądarkę.
Dla nabrania kontekstu opowiem pokrótce jak wygląda główna część aplikacji:
Na stronie głównej użytkownikowi wyświetlają się pewne pozycje zawierające ok. 10 kolumn, zebrane w grupy po kilka, kilkanaście sztuk. Grupy są albo automatycznie utworzone albo dodane przez userów. I dla grup tworzonych ręcznie można z poziomu tego ekranu dodawać albo usuwać pozycje. Gdzie usuwanie jest robione z menu kontekstowego przy każdym z wierszy.
Coś tu jest nie tak
I dla kilkunastu rekordów jest w miarę ok. Jednak jak dodaliśmy więcej funkcji dla grup i zacząłem je sobie dodawać i modyfikować to okazało się, że przy kilkudziesięciu wpisach strona staje się praktycznie nieużywalna.
No to zacząłem sprawdzanie czy na pewno wszystkie komponenty, które tylko coś wyświetlają mają ustawione ChangeDetectionStrategy.OnPush
itd.
I w końcu przeszedłem do przeglądania tego co jest wyrenderowane w przeglądarce – bo większość problemów z UI jest kiedy elementów na stronie mamy za dużo. A przy takich tabelach to nie trudno o taki problem.
Ukryjmy, nikt nie zauważy
Spodziewałem się wielu głupich problemów z wyświetlaniem niepotrzebnych elementów. Ale to co znalazłem przerosło moje granice pojmowania.
Otóż wyszło na to, że dla każdego wiersza w tabeli dodawane jest do DOMu osobne menu kontekstowe. Ale to jeszcze można zrozumieć jakoś. Zacząłem więc sprawdzać jakie style mają te menu. I tutaj zagadka się rozwiązała. Otóż każde z tych menu miało ustawione visibility: hidden
zamiast display: none
. Każdy kto coś tam robił we frontendzie pewnie już wie o co chodzi.
Mianowicie kiedy zmieniamy visibility
to przeglądarka nadal renderuje taki element na stronie, przydziela mu miejsce itd. Po prostu ostatecznie jest on niewidoczny. Jeżeli ustawimy display
to taki element nie jest brany pod uwagę przy renderowaniu.
Każde takie menu to ok. 10 elementów HTML. Dla 100 wpisów daje nam to 1000 dodatkowych elementów, ze swoimi eventami i właściwościami, które są dodatkowo stale sprawdzane przez Angulara. Poza tym te menu mają pododawane wiele globalnych eventów w dokumencie. A niezależnie od tego ile ich jest to widoczne dla użytkownika będzie zawsze max 10 z nich. Bo można mieć tylko jedno menu otwarte w tym samym momencie.
Zadałem więc pytanie osobie odpowiedzialnej za ten korporacyjny cud techniki – „WHY?!”. I okazuje się, że wszystko zostało w ten sposób zrobione…. żeby animacja otwierania ładnie wyglądała. Trwa ona 0,25sek i dopóki nie usłyszałem, że tam jest to nawet nie wiedziałem, że to się animuje.
Szukajmy dalej
Zacząłem dzisiaj też mocniej przeglądać stronę pod tym kątem – czy może jeszcze gdzieś się nie kryją takie cuda. I szybko odkryłem, że plan był zakrojony na szeroką skalę.
Bo mamy też na tej stronie kilka popupów (ot np. potwierdzenie czy chcesz usunąć coś albo pozwalające zmienić nazwę albo dodać nową pozycję).
Więc ogólnie jest ich powiedzmy 5-6 sztuk.
I okazuje się, że w przypadku popupów jest jeszcze weselej. Bo to już nie mamy visibility: hidden
. Tutaj już wjeżdża gruby kaliber w postaci opacity: 0
. Czyli wszystkie one są renderowane na stronie, po czym przeglądarka musi ustawić im przeźroczystość.
Zapytacie pewnie po co takie wynalazki? Otóż wszystko po to aby popup mógł mieć piękną, półsekundową animację pojawiania się.
Zrozumiałbym gdyby ten framework był robiony przez zewnętrzny zespół, który nie ma wizji tego jakie projekty się robi w firmie. Albo gdyby nasz projekt z dużym wolumenem danych był jedynym takim, a reszta to zwykłe appki z paroma polami i po prostu mamy „sytuację wyjątkową”.
Ale nie – większość projektów w firmie klienta to są własnie takie excele w przeglądarce. To co my mamy to chyba najlżejszy z nich bo u nas nie można np. edytować danych w wierszach. I zespół UX/UI siedzi razem z resztą developerów. I przygotowuje teoretycznie komponenty dla właśnie tych projektów…Na szczęście po tym jak powiedziałem na standupie z czym jest problem, jaka biblioteka jest tutaj źródłem, dlaczego i co konkretnie jest z nią nie tak to zdjąłem z siebie dalszą walkę z optymalizacją. Teraz lider ma iść do odpowiednich osób i pogadać co zepsuli.
Jednak nadal trzeba się męczyć ze stroną, która pożera tyle zasobów co Visual Studio.
Mimo, że CSS to nie programowanie to jednak programistyczne serce krwawi.
Skąd ten problem?
Co ciekawe nie mówimy tutaj o sytuacji kiedy biblioteka, o której mówimy została odziedziczona po jakimś zespole. Który w dodatku by ją odziedziczył po jeszcze kimś innym. Nie jest to też biblioteka, którą wykonała zewnętrzna firma.
Mówimy o kodzie napisanym przez pracowników klienta. W dodatku takich, którzy prawdopodobnie będą z tym kodem pracować jeszcze wiele lat bo mało kto się u nich zwalnia po mniej niż 10 latach. I siedzą razem z resztą działu. I widzą użytkowników przy kawie w kuchni.
Nie jest też problemem czas albo budżet. Nie ma u klienta problemu z tym, że komuś zejdzie z zadaniem tydzień dłużej. Albo, że trzeba na coś wydać dodatkowe pieniądze. Sądzę więc, że problemem jest albo problem kompetencji, albo co bardziej prawdopodobne – podejścia. Bo jeżeli nikt od nas nie wymaga maksimum to róbmy wszystko tak żeby nie musieć za dużo czasu nad tym siedzieć albo zbyt długo myśleć nad rozwiązaniem. Byle szybciej, byle kod był sformatowany i działał (na szczęście formatowanie kodu jest u nich na dobrym poziomie).
Myślę, że może to być kwestia priorytetów. Dla różnego rodzaju projektantów(designnerów) priorytetem nie jest to jak szybko coś działa, tylko czy wygląda dobrze. Programista patrzy i mówi „Ale to zjada 100% procesora”, a designer mówi „Ale tak wygląd lepiej”. Kwestia jest taka, że ktoś musi powiedzieć, że to ma wyglądać trochę gorzej i ma nie zjadać całego procka. I to jest odpowiedzialność lidera ;)