legacy code: jak przejąć stary projekt i go naprawić
[Read this post in english here]
Każdy projekt zaczynam od rewizji potrzeb użytkowników i rozwiązań w systemie (ficzery). Aby określić jakie są aktualne potrzeby użytkowników i w jaki sposób są one zaspokajane poprzez aplikację. Pomaga to podzielić aplikację na części i określić które części są używane i w jakim stopniu. Szczególnie interesują mnie elementy systemu które nie są używane. Funkcjonalności które zostały zapomniane, porzucone i które można bezpiecznie usunąć. Aby nie inwestować czasu i energii w kod który jest zbędny.
Rewizję tę opieram na trzech źródłach informacji: dokumentacja, ludzie i dane.
Staram się uzyskać odpowiedzi na pytania zebrane w tabeli poniżej:
| pytania | ludzie | dane |
|---|---|---|
| aplikacja | ||
| po co istnieje? | powiedzą dlaczego istnieje | z danych tego się nie dowiemy |
| jakie ma problemy? | powiedzą dlaczego mają problemy z aplikacją | wskażą problemy techniczne aplikacji |
| kod (ficzery) | ||
| jak można je podzielić? | powiedzą jak i dlaczego | wskażą kto, z czego i jak często korzysta |
| która są najważniejsze? | powiedzą dlaczego są najważniejsze | wskażą które są najczęściej używane |
| które są używane? | powiedzą dlaczego je używają | wskażą które są używane |
| które nie są używane? | powiedzą dlaczego ich nie używają | wskażą które nie są używane |
| czyje dany ficzer zaspokaja potrzeby? | powiedzą dla kogo jest dany ficzer | wskażą kto korzysta z danego ficzera |
| użytkownicy | ||
| kim są i jak można ich pogrupować? | powiedzą kim są i jak można ich pogrupować | wskażą którzy użytkownicy z czego korzystają |
| jakie mają potrzeby? | powiedzą jakie mają potrzeby | z danych tego się nie dowiemy |
| jakie mają problemy? | powiedzą o problemach jakościowych | wskażą problemy ilościowe |
| ficzery | powiedzą w jaki sposób z nich korzystają | wskażą którzy użytkownicy z czego korzystają |
| ilu jest 'martwych' użytkowników? | powiedzą dlaczego są martwi | wskażą którzy są martwi i ilu ich jest |
Wykonując taką rewizję, tworzę dokumentację w formacie tekstowym org, który jest podobny do markdown. Format tekstowy to jedyny naprawdę uniwersalny format który gwarantuje, że w przyszłości każdy będzie mógł łatwo edytować tak zapisaną dokumentacje. Nie zginię w czeluściach firmowego CRM, Jiry czy nieedytowanlnym pliku PDF.
1. źródła informacji
1.1. informacje zapisane - kod i dokumentacja
1.1.1. kod
Dane powstające podczas użytkowania aplikacji (np. dane aktywności użytkowników) powstające w skutek normalnego działania aplikacji (np. użytkownik składa zamówienie) bądź na skutek zaplanowanego wymuszenia powstania danych (np. logowanie aktywności) w celu sprawdzenia czy dany kod jest wywoływany.
To jedyne obiektywne źródło prawdy które da nam odpowiedzi na pytania: co jest używane, jak często i przez kogo.
- analiza statyczna
Czy dany kod jest wywoływany przez inny kod? Czy są do niego odniesienia, referencje?
- analiza dynamiczna
- kod którego wykonanie powoduje zapis danych
Dane powstającę w źródle danych, najczęściej baza danych.
- ile jest wierszy w danej tabeli?
Tutaj zależnie od wieku aplikacji. Jeśli świeża to wcale nie musi mieć dużo wierszy w każdej z tabel. Więc drugim ważniejszym pytaniem jest:
- kiedy powstały ostatnie wiersze w danej tabeli?
Zazwyczaj mamy jakieś informacje określające date powstania danego wiersza np. kolumnę created_at (timestamp) albo zawsze możemy taką utworzyć. Tu również sami musimy wywnioskować czy jeśli ostatnie wiersze powstały np. rok temu to czy istnienie tej funkcjonalności jest zasadne.
- ile jest wierszy w danej tabeli?
- kod którego wykonanie nie powoduje zapisu danych
Więc tutaj musimy doprowadzić aby jakieś dane powstały przy wywołaniu danego kodu. Powstawanie danych a raczej ich zapis przy dużej ilości zapytań może spowolnić działanie aplikacji i być odczuwalny przez użytkowników. Dlatego tego typu 'obserwacje' prowadzimy przez określony czas, starając się zebrać reprezentatywną próbkę danych. Tutaj możemy wyodrębnić dwa zakresy zbierania danych:
- całościowy
- na poziomie zapytań HTTP
Czyli logujemy prawie wszystkie requesty HTTP. Zależnie od potrzebnych informacji - warto mieć ścieżkę, nazwę klasy i funkcji, nazwę użytkownika i jego role.
- na poziomie PHP
Tutaj https://github.com/krakjoe/tombs ale przyznaje, że jeszcze nigdy z tego nie korzystałem produkcyjnie. Wymaga zainstalowania dodatkowego extension do PHP.
- na poziomie zapytań HTTP
- punktowy
Kiedy ze względu wydajnościowych nie chcemy odpalać logowania wszystkiego albo mamy mało podejrzanych miejsc to możemy powstawiać loggera w konkretne miejsca. Sprawdzić czy użytkownicy wchodzą na daną stronę czy wywołują daną akcję np. kupno produktu.
To podejście doczekało się własnego terminu tzw. tombstone. Logger ma zadanie logować miejsce w którym został wywołany i czas wywołania. Zazwyczaj logujemy do specjalnego pliku. Dla ułatwienia wstawiamy sobie date powstania nagrobka np. Log::('2024-05-26'). A w pliku z logami powstanie zapis. np. '2024-05-26 UserController::showAction()'. Tutaj fajna i krótka (5min) prezka na ten temat David Schnepper's Ignite talk, "Isn't That Code Dead?"
- całościowy
- kod którego wykonanie powoduje zapis danych
1.1.2. dokumentacja
Informacje które zostały spisane i są dostępne w jednym z takich źródeł jak:
- dokumenty Office np. Microsoft 365, Word i SharePoint
- firmowe serwisy wiki
- systemy zarządzania projektami np. Jira
- komunikatory np. Teams, Slack
Mnogość dostępnych opcji które często są używane jednocześnie powoduje dodatkowe rozproszenie informacji. Co pogłębia trudności w wyszukiwaniu a duży wysiłek potrzebny aby zapewnić aktualność powoduje, że informacje te są mocno zdezaktualizowane.
Zazwyczaj znajduje jednorazową ogólną specyfikacje projektową z początku projektu która dodatkowo została zapisana w formacie uniemożliwiającym jego dalszą modyfikacje (PDF).
Źródła te mają pewną wartość historyczną. Ciekawie na to spojrzeć. To pozwala zrozumieć genezę powstania projektu.
1.2. informacje niezapisane - ludzie
Informacje pozyskane z rozmów z ludźmi, którzy dysponują tzw. wiedza tubylczą przekazywaną z ust do ust. Rezultatem działań zbierania informacji niespisanych jest ich odpowiednia selekcja i spisanie.
1.2.1. podział ludzi
Zależnie od aplikacji można podzielić i pogrupować użytkowników w określone grupy które mają wspólne cechy i właściwości. Można zacząć od podziału na użytkowników wewnętrznych, czyli firmowych, oraz zewnętrznych czyli klientów danej firmy. Następnie stosowny podział na poszczególne grupy i role. Sprawdzenie czy podział ma odzwierciedlenie w aplikacji.
1.2.2. sposoby zbierania
Pozyskanie informacji od danego przedstawiciela grupy, lub roli. Tutaj dzielę to na część oficjalną i nieoficjalną:
- nieoficjalna
Rozmowy z ludźmi 'w realu' lub zdalnie.
- oficjalna
Dłuższy, zaplanowany wywiad 'w realu' albo zdalnie, przebieg jest rejestrowany. Po prostu pomagam przygotować dłuższą wypowiedź pisemną danemu użytkownikowi aplikacji np. użytkownik korzystający z firmowego CRM'a. Proszę go aby pokazał w jaki sposób korzysta z aplikacji i z poszczególnych funkcjonalności, jakie ma problemy i pomysły na ich rozwiązanie.
Zapis audio-video rozmowy jest nieoceniony aby później w spokoju opracować całą rozmowę. Po opracowaniu przedstawiam mu spisane pomysły, opinię i wnioski z prośbą o przejrzenie, poprawki, dopisanie rzeczy które mu się przypomniały, oraz wydanie zgody na publikację. Po wyrażeniu zgody niszczę materiał video, a wywiad dodaje do dokumentacji jako oficjalną wypowiedź użytkownika aplikacji.
2. dalsze kroki
Po zebraniu odpowiednich informacji staram się usunąć jak najwięcej martwego kodu. W rozmowie z biznesem używam bardziej precyzyjnego języka. Sądzę, że lepiej używać wyrazu 'archiwizacja' bo z systemem kontroli wersji to właśnie robimy. Nic nie usuwamy na zawsze, tylko usuwamy z 'aktualnej wersji' oprogramowania. Po to abyśmy nie tracili czasu i zasobów na 'utrzymanie' czyli przeglądanie, czytanie i modernizowanie kodu którego nikt nie używa.
Po takiej czystce zabieram się za postawienie pierwszych testów. Szybkim sposobem na zapewnienie sobie minimalnej siatki bezpieczeństwa jest podpięcie smoke testów. Jest do tego paczka której używam od lat i polecam https://github.com/shopsys/http-smoke-testing.
Uzyskanie danych o aktywności użytkowników pozwala też określić które elementy systemu są najważniejsze a więc określić w jakiej kolejności należy prowadzić dalszą pracę.