What if you cannot see the changes on the website?

22 february 2024
Jakub Rojek Jakub Rojek
DALL-E-generated graphics showing a futuristic soldier in armor and with a rifle, who is fighting a knight with a triangular shield and sword, wearing armor and a helmet. The action takes place at some battlefield, where you can see the knights lying in the background, and in the distance a huge castle with a large gate and stairs. The sky is cloudy, even grayish. The symbolism is the fight between the new and the old, which defends the current order.
Categories: For clients, Deployments, Web, Guides

Niektórzy znają to uczucie aż za dobrze - istnieje aplikacja webowa, która działa produkcyjnie i jest cały czas rozwijana. Kończy się pewien kolejny etap prac (np. sprint), wszystko zostało przetestowane, wypolerowane i pozostaje tylko wdrożenie na serwer. Ten wiekopomny moment następuje, ktoś jeszcze kontrolnie "przeklikuje" nową wersję, klient jest informowany o przyroście, po czym następnego dnia... software house dostaje informację, że użytkownicy nie widzą zmian lub aplikacja dziwnie się zachowuje.

Wówczas programiści wypowiadają pod nosem klasyczny zwrot "dziwne, u mnie działa" i jeszcze raz sprawdzają sytuację. Wchodzą na serwer, do którego dostęp mają użytkownicy, logują się, wykonują te same akcje i... okazuje się, że widać wszystkie nowe elementy i działają one poprawnie. Zatem ta sama wersja aplikacji na tym samym serwerze działa u jednego użytkownika, a u drugiego nie. Kto w takiej sytuacji ma rację - klient zgłaszający błąd czy programista, który mimo wszelkich prób nie może tego błędu odtworzyć i klnie się na swojego laptopa, że wszystko jest poprawne?

Odpowiedź brzmi: nikt i oboje jednocześnie. Tego typu zachowanie systemu jest frustrujące, gdyż pojawia się tutaj kilka zmiennych - przeglądarka, środowisko (czyli np. znaczenie może mieć to, jakie ciasteczka są zgromadzone w aplikacji), użytkownik, jego uprawnienia itd. Nie da się ukryć, że dużą rolę odgrywa też cache, a więc pamięć podręczna, która w trosce o wydajność aplikacji potrafi zachować stary obraz strony lub jego części. Wówczas rzeczywiście mogą się dziać nieprzewidziane rzeczy w postaci niewidocznych nowych zmian lub wręcz dziwacznych wyników zwracanych przez system IT.

Mimo że nikt jednoznacznie nie ponosi tutaj winy i nie ma racji, to jakoś tę kwestię trzeba rozwiązać lub przynajmniej zbadać. Problem w tym, że jeśli programista nie może odtworzyć kłopotu u siebie, to musi zobaczyć to u klienta, a to bywa czasem utrudnione. W związku z tym, należy mieć procedurę działania, która nie tylko pozwoli rozjaśnić sytuację, ale również zapewnić, iż software house podchodzi do sprawy profesjonalnie.

Ten tekst jest poradnikiem na takie okazje i postaramy się opisać najczęstsze kroki, jakie wówczas powinno się wykonywać. Natomiast najpierw dokładniej wyjaśnijmy, na czym może (choć nie musi) polegać problem. Przypomnijmy, że będziemy obracać się w kręgu aplikacji webowych, choć w przypadku aplikacji desktopowych występują podobne problemy, aczkolwiek tam łatwiej zadbać o logi.

"Halucynacje" aplikacji

Specjalnie wykorzystałem słowo "halucynacje", mimo iż temat nie jest związany ze sztuczną inteligencją, gdzie ten termin opisuje oderwane od rzeczywistości wyniki działania modelu LLM (ang. Large Language Model). Powszechnie ten wyraz oznacza omamy, fałszywe doznania wzrokowe i poniekąd pasuje to do sytuacji, jaką mogą mieć użytkownicy, którym aplikacja nie wczyta się w odpowiedni sposób.

Powód zwykle mieści się w jednym z dwóch obszarów. Pierwszy to wspomniania już pamięć podręczna (cache), która działa jak magazyn przechowujący gotowe, już wcześniej obliczone, wyniki, aby w przyszłości szybciej serwować je użytkownikowi. Wynika to z tego, że nowoczesne systemy IT są dynamiczne i po otwarciu odpowiedniej strony przez użytkownika, oprogramowanie musi "przekalkulować" treść, którą pokaże, w tym np. pobrać informacje z bazy danych, przetworzyć je, a następnie wygenerować odpowiedni kod HTML. To wszystko zajmuje czas, a jeśli do tego uwzględnimy fakt, iż dana strona może być wyświetlana wiele razy i za każdym razem proces wygląda tak samo, to nietrudno dojść do wniosku, że drugie, trzecie i kolejne wywołanie może być szybsze, o ile zachowamy wyniki pośrednie. Tak działa właśnie cache, który może zachodzić na wielu poziomach, jednak w kontekście dzisiejszego tekstu najbardziej interesuje nas przeglądarka użytkownika. Otwarcie strony internetowej to tak naprawdę zlecenie przeglądarce pobrania nie tylko samego kodu HTML, ale też skryptów JavaScript, plików CSS, obrazków itd. W związku z tym przez pewien czas są one cache'owane, aby przy kolejnym wywołaniu użytkownik szybciej zobaczył stronę. Szczególnie dotyczy to plików statycznych, za jakie uważane są pliki JS czy CSS.

Te pierwsze są szczególnie istotne w kontekście naszego problemu, ponieważ nowoczesne frontendy są zazwyczaj (aczkolwiek nie zawsze) pisane przy pomocy frameworków JS, u których plik index.html jest tylko czymś w rodzaju punktu startowego, a całe działanie aplikacji mieści się w zbiorach javascriptowych. W związku z tym podmiana tych zasobów nie zawsze jest od razu rejestrowane przez przeglądarkę i odświeżane, to użytkownik tuż po wdrożeniu nadal może widzieć stare pliki, odnosząc wrażenie, że nic się nie zmieniło.

Drugi najczęstszy powód sytuacji typu "u ciebie działa, a u mnie nie" to uprawnienia użytkownika. Może być tak, iż aplikacja nie została dostatecznie dobrze przetestowana z perspektywy usera o określonej roli lub kombinacja jest na tyle unikalna, że oprogramowanie - mówiąc kolokwialnie - "zgłupiało". Czasem jest to z kolei poprawne zachowanie aplikacji, która odpowiednio dba o uprawnienia, natomiast w przedstawionym przypadku nie jest to klarowne dla klienta. W przeciwieństwie do problemów z cachem, które czasami występują tylko i wyłącznie u konkretnej osoby na jego komputerze, kwestię przywilejów danego użytkownika programiści mogą zbadać zdalnie lub "u siebie".

Pozostaje pytanie, jak zabrać się do rozwiązywania takiej kwestii, jeśli już wystąpi. Jest na to parę sposobów i rad, które kolejno można przekazać klientowi.

Wyczyszczenie cache'a przeglądarki

W momencie, kiedy programista słyszy o tym, iż po wdrożeniu u użytkownika coś niepoprawnie się załadowało, następuje zwykle rada "proszę w twardy sposób odświeżyć stronę". No dobrze, ale co to właściwie oznacza?

Jeśli jako użytkownicy chcemy jeszcze raz załadować stronę na której się znajdujemy, zazwyczaj zaznaczamy adres URL i klikamy ENTER lub - jeszcze szybciej - naciskamy klawisz F5, który w większości przeglądarek jest przypisany do funkcji odświeżenia. To jest coś, co można nazwać "miękką" lub normalną wersją tej operacji - przeładowuje ona tylko stronę, powodując np. pobranie danych na nowo, natomiast statyczne pliki są zazwyczaj nadal przywracane z cache'a. Aby zmusić przeglądarkę do tego, aby te zasoby również zostały pobrane na nowo, należy zamienić pojedynczy klawisz F5 na CTRL + F5 lub CTRL + SHIFT + R, w zależności od preferencji, przeglądarki i systemu operacyjnego (w przypadku macOS zazwyczaj CTRL należy czytać jako CMD, znany też z symbolu ). Listę dostosowaną do każdego środowiska można znaleźć np. w tym artykule.

Tutaj warto wspomnieć jeszcze o jednym fakcie, który zauważyliśmy u naszego klienta, gdzie usilne wciskanie kombinacji CTRL + F5 na Windowsie nie pomagało. Szybko okazało się, że niektóre modele laptopów mają domyślnie zmienione zachowanie klawiszy funkcyjnych (a więc od F1 do F12) i traktują je tak, jakby włączony był klawisz Fn znajdujący się zwykle pomiędzy lewym CTRL i ALT. Jest to przycisk aktywujący alternatywne znaczenie klawiszy, jak np. zwiększenie głośności czy przełączanie ekranu. Jak widać, niektórzy producenci notebooków uznali, iż "drugie" funkcje są ważniejsze niż pierwotne, w wyniku czego zamiast CTRL + F5 należy naciskać CTRL + Fn + F5, aby osiągnąć to, co normalnie byłoby możliwe za pomocą dwóch przycisków. Oczywiście, można to przełączyć systemowo, jednak autor tego artykułu przyznaje, że domyślne ustawianie klawiatury w taki sposób jest jedną z bardziej idiotycznych praktyk, jakie widział w ostatnim czasie.

Wracając do twardego odświeżania, w większości wypadków usuwa to problem. Czasem potrzebne jest kilkukrotne działanie w ten sposób, jednak w końcu powinno to sprawić, iż pliki strony internetowej załadują się na nowo i wówczas okazuje się, iż problemem faktycznie był cache. Na całe szczęście.

Zmiana przeglądarki

Czasem jednak tak się nie dzieje i nadal pojawiają się kłopoty. Wówczas, przed podjęciem innych kroków, warto jeszcze sprawdzić inną przeglądarkę lub przynajmniej tryb incognito, jeśli żadna inna aplikacja tego typu nie jest zainstalowana na komputerze. Pamiętajmy, że cache i lokalne pliki są zapisywane nie dla całego systemu, tylko dla konkretnego programu do wyświetlania stron WWW. Łatwo to zresztą zauważyć - gdy zalogujemy się na nasze konto w serwisie internetowym za pomocą Chrome, to po przejściu do Firefoxa nadal będziemy niezalogowani.

Oczywiście, twarde odświeżenie strony również powinno rozwiązać problem na tej samej przeglądarce, jednak sprawdzić kolejną nie zaszkodzi także z innych powodów. Pierwszy z nich to problemy samej strony internetowej na konkretnym programie. To się zdarza i o ile, oczywiście, nie jest to powód do chwały dla programistów, to jest to wątek, który warto zweryfikować. Druga sprawa to z kolei rozszerzenia, jakie użytkownicy mają zainstalowane w przeglądarce, a które w skrajnych przypadkach mogą utrudniać działanie strony. Do tej grupy zaliczają się też różne unikalne decyzje konfiguracyjne, jak np. ograniczenie ładowania JavaScript.

Wyczyszczenie aplikacji i ciasteczek (ang. cookies)

Zanim przejdziemy dalej, warto sprawdzić jeszcze jedną rzecz. Być może stara i nowa wersja aplikacji w różny sposób przechowują dane w ciasteczkach lub local storage - są to miejsca, do których trafiają różne informacje użytkownika, jak choćby token, z którego korzysta po zalogowaniu się do serwisu czy akceptacja profilowania na stronie. Widywaliśmy w przeszłości przypadki, w których zmiana sposobu gromadzenia takich danych potrafiła doprowadzić do dziwnych sytuacji, kiedy aplikacja nie umiała prawidłowo zinterpretować wartości, które użytkownik już miał w przeglądarce.

Dla pewności można je wyczyścić, przy czym tutaj należy już zapoznać się z czymś, co nazywa się narzędziami deweloperskimi (ang. Developer Tools) i w większości przeglądarek jest dostępne po naciśnięciu klawisza F12 lub kliknięciu prawym przyciskiem myszy w dowolnym miejscu na stronie oraz wybrania opcji w rodzaju "Zbadaj" czy "Inspect". Wówczas pojawi się panel składający się z kilku zakładek, który zapewnia ogromne możliwości i jeszcze dzisiaj na chwilę do niego wrócimy. Teraz jednak interesuje nas sekcja "Application" i to, co jest widoczne pod "Local storage" lub "Cookies" dla interesującej nas strony. Warto wyczyścić tamte dane poprzez kliknięcie prawym przyciskiem na danym elemencie i wybraniu "Clear", natomiast trzeba uważać - najczęściej oznacza to również, że zostaniemy wylogowani z danej witryny.

Uprawnienia

Jeśli nie pomogło czyszczenie przeglądarki oraz sprawdzenie innej na tym samym komputerze, to warto sprawdzić z innym użytkownikiem, czy on(a) również ma podobne problemy. Jest to sytuacja częsta w aplikacjach przeznaczonych dla konkretnego zespołu lub firmy i niejednokrotnie dostawaliśmy zgłoszenia "u kolegi działa, a u mnie nie". Zakładając, że nie jest to wina cache'a, warto poprosić "ofiarę" o test krzyżowy, czyli:

  • inny użytkownik sprawdza daną funkcję u siebie,
  • jeśli nie było problemów, to niech ten użytkownik w miarę możliwości zaloguje się na swoje konto na komputerze "ofiary".

W tym przypadku chodzi, oczywiście, o sprawdzenie uprawnień lub innych charakterystycznych danych konta - najczęściej na tym etapie okazuje się, że kłopot wynika z braku nierównych przywilejów i wówczas jest to bardzo ważna informacja dla programistów, którzy pod tym kątem będą sprawdzać zachowanie aplikacji. Nie zawsze musi być to tylko rola, a więc pakiet uprawnień - czasami jest to powiązanie użytkownika z konkretnymi obiektami w systemie, co oznacza, że zespół IT musi sprawdzić sytuację dokładniej korzystając z konta równoważnego osoby poszkodowanej. W jaki sposób to robią - zależy od wewnętrznych ustaleń pomiędzy software housem a klientem.

Natomiast taki test może też wykazać ewentualny przypadek, w którym to sam komputer osoby poszkodowanej jest w jakiś sposób winny. Rzadko się to zdarza, ale tutaj ponownie wysuwa się wpływ różnych rozszerzeń w przeglądarce, a także choćby połączenie z inną siecią internetową. Bywały już przypadki, w których problem wynikał nie tyle z samej aplikacji, co konfiguracji firewalla, choć trzeba przyznać, że to bardzo rzadkie okoliczności.

Dane przeglądarki

Wreszcie, gdy mimo tylu prób u poszkodowanego użytkownika nadal występuje problem i alternatywą pozostaje tylko pojawienie się u niego lub zdalne użyczenie ekranu, pozostaje zebranie odpowiednich danych, które być może rozjaśnią temat. I tutaj wracamy do Developer Tools, o których już wyżej wspomnieliśmy, a które tutaj ponownie nam się przydadzą.

Pierwsza sprawa to sprawdzenie, jak wygląda konsola JavaScript (zakładka "Console") - jest duża szansa, iż w tym miejscu będzie się znajdować dużo komunikatów w kolorze czerwonym, co - oczywiście - oznacza błędy. Często znajdują się tam ważne informacje, które należy skopiować i później przeanalizować.

Istotne rzeczy można też znaleźć w zakładce "Network" (lub "Sieć"), gdzie mogą na czerwono widnieć żądania zakończone błędem. Warto w takie pozycje kliknąć i skopiować dane z podzakładek "headers", "payload" oraz "response". Te informacje również są ważne dla programisty, choć trzeba wspomnieć o tym, że jeśli coś wyskoczyło w tym miejscu, to najczęściej można to też znaleźć w logach na serwerze. Z konsolą JS tak łatwo, niestety, nie jest.

W przypadku drugiej porady warto też wspomnieć o dodatkowych kwestiach. Po pierwsze, wpisy w tym miejscu pojawią się dopiero po odświeżeniu strony (w jakikolwiek sposób) i jeśli w "Network" niczego nie ma, to prawdopodobnie wystarczy nacisnąć F5. Po drugie, ta zakładka pozwala też zaznaczyć opcję "Disable cache", którą warto wypróbować przy szukaniu źródła błędu - działa ona podobnie, jak twarde odświeżenie omawiane wcześniej.

Cierpliwość jest cnotą

Tytuł tego rozdziału brzmi jak nieśmieszny żart, ale ma sens w przypadku, kiedy sytuacja dotyczy ogromnej aplikacji, która jest skalowana poziomo. Mowa o serwisach, które mieszczą się nie na jednym serwerze, tylko wielu, powielając swój kod w celu równoważenia obciążenia i wykorzystując do tego load balancera. Tego typu konstrukcja nie tylko pomaga w zachowaniu odpowiedniej przepustowości witryny, ale także w radzeniu sobie z awariami. W jaki sposób? Choćby taki, że wdrożenie nowej wersji może nastąpić tylko na część serwerów, inkrementacyjnie, w celu zweryfikowania, czy wszystko działa jak należy. Wówczas przez pewien czas użytkownicy, trafiający na "nowe" serwery, widzą już zmiany, podczas gdy nieszczęśnicy (albo i szczęśliwcy), którzy zostali przekierowani do "starych" jednostek jeszcze nie doświadczą modyfikacji. Muszą wtedy poczekać, aż wdrożenie dopełni się na wszystkich maszynach i również one zaczną serwować nową wersję usługi.

Ten mechanizm powinien być nam doskonale znany z sytuacji, kiedy pojawia się nowa zmiana w dużym serwisie (np. na Facebooku) i wszyscy o niej mówią, a my jej jeszcze nie widzimy, mimo usilnego odświeżania strony. Taka sytuacja oznacza, że prawdopodobnie odwołujemy się do serwera (tzn. tak zostaliśmy pokierowani przez wewnętrznego "dystrybutora" serwisu), który jeszcze nie ma wprowadzonej zmiany lub wręcz nie znajdujemy się w grupie kontrolnej użytkowników mogących doświadczyć modyfikacji. Jakkolwiek to źle nie brzmi, jest to zaplanowane działanie producenta oprogramowania i należy po prostu poczekać.

Jak do tego nie dopuścić?

Oczywiście, te wszystkie porady mają zastosowanie już po fakcie, a więc po wystąpieniu sytuacji, w której użytkownik nie jest zadowolony, a programiści zastanawiają się, co począć. Warto więc wiedzieć, w jaki sposób ograniczyć częstotliwość takich zdarzeń. Przede wszystkim należy mieć świadomość, że przeglądarka cache'uje pliki o takich samych nazwach, a mówiąc bardziej konkretnie - żądania odwołujące się do plików o tym samym URL. Aby to ominąć, częstą techniką jest dodawanie przyrostków po znakach zapytania, najczęściej losowych liczb. Wówczas przeglądarka "myśli", że to inny plik i go nie cache'uje. Jest to technika prosta, ale siłą rzeczy sprawi ona, że pamięć podręczna nie zadziała także w odpowiednich ku temu sytuacjach, a więc kiedy trzeba przyspieszyć stronę. Coś za coś.

Można też zwyczajnie poinformować przeglądarkę o tym, że dane witryny nie powinny być cache'owane poprzez odpowiednie opcje tagów meta, a więc Pragma oraz Expires. Generalnie warto zainteresować się hasłem "cache control" dotyczącym żądań HTTP, a w szczególności nagłówkiem o nazwie Cache-Control. Polecamy również przeczytać ten tekst, w którym znajduje się więcej informacji o cachingu w przeglądarce i przykłady, jak sobie z tym "radzą" duże witryny.

Frameworki JS-owe umożliwiają również inne nazywanie plików będących wynikiem budowania aplikacji - zarówno React, jak i Angular pozwalają uzupełnić nazwę poprzez losowy hash, jednak w sposób, który nie powinien zaburzyć prawidłowego cache'owania. Ten "dodatek" będzie stały na jedno wdrożenie - po zbudowaniu nowej wersji ulegnie zmianie, a więc zmusi przeglądarkę do ponownego pobrania pliku. A przynajmniej powinien, bo powiedzmy, że różnie z tym bywa, jako że sam index.html też ulega zapamiętywaniu w pamięci podręcznej.

W przypadku problemów wynikających z uprawnień nie ma tak naprawdę innego sposobu, jak dokładniejsze zarządzanie tym aspektem systemu, lepsza weryfikacja oraz przygotowanie przypadków testowych lub użytkowników testowych, za pomocą których będzie można w manualny lub automatyczny sposób weryfikować różne scenariusze.

Podsumowanie

Ten artykuł był przeznaczony zarówno dla programistów, jak i użytkowników, którzy narzekają na tego typu przypadłości. A że każdy z nas jest użytkownikiem takiej czy innej aplikacji, teoretycznie jest to tekst dla każdego odbiorcy stron internetowych. Warto przede wszystkim zapamiętać, czym jest twarde odświeżenie strony oraz fakt, że nie zawsze programiści są winowajcami sytuacji, która spotyka odbiorcę. Z naciskiem na "nie zawsze".

Pozdrawiam i dziękuję - Jakub Rojek.

We write not only blog articles, but also applications and documentation for our clients. See who we have worked with so far.

About author

Jakub Rojek

Lead programmer and co-owner of Wilda Software, with many years of experience in software creation and development, but also in writing texts for various blogs. A trained analyst and IT systems architect. At the same time he is a graduate of Poznan University of Technology and occasionally teaches at this university. In his free time, he enjoys playing video games (mainly card games), reading books, watching american football and e-sport, discovering heavier music, and pointing out other people's language mistakes.

Jakub Rojek