Nowy framework. Nowa architektura. Czysty kod. Zaczynamy od początku.
To jedna z najbardziej kuszących decyzji po przejęciu starego albo źle prowadzonego projektu IT.
Czasami jest właściwa.
Ale bardzo często jest po prostu najdroższym sposobem na rozwiązanie problemu, którego jeszcze dobrze nie zrozumieliśmy.
Przekonałem się o tym przy przejmowaniu jednego z produktów MedTech — historię całego projektu IKZMED opisałem w osobnym artykule.
Mieliśmy dwie wersje tego samego produktu
Pierwsza aplikacja była rozbudowana funkcjonalnie, ale przez kolejne etapy developmentu narosło w niej wiele problemów. Dodawano kolejne funkcje, zmieniały się założenia, a poszczególne elementy nie zawsze tworzyły spójną całość. Kod i architektura wymagały pracy.
Kolejny wykonawca podjął więc decyzję, która na pierwszy rzut oka wydawała się logiczna: wyrzucić istniejący produkt i napisać go od początku.
Problem? Nowy produkt również trzeba dowieźć.
Rewrite nie usuwa problemów. Zamienia je na inne
Przepisując aplikację od początku, pozbywamy się długu technologicznego poprzedniej wersji. Jednocześnie wyrzucamy rzeczy, które już działały.
Tracimy istniejące funkcjonalności, miesiące developmentu i wiedzę zaszytą w poprzednim rozwiązaniu. Musimy ponownie rozwiązać problemy, które ktoś wcześniej już rozwiązał.
W przypadku projektu, który przejąłem, druga wersja była technologicznie prostsza, ale funkcjonalnie znacznie uboższa. Z produktu posiadającego wiele potrzebnych elementów powstało słabe MVP.
A termin zakończenia projektu się zbliżał.
Trzecia wersja?
Kiedy wszedłem do projektu, mogłem spojrzeć na obie aplikacje i stwierdzić: „Zrobimy to porządnie. Napiszmy trzecią."
Z punktu widzenia programisty takie rozwiązanie jest bardzo atrakcyjne. Z punktu widzenia osoby odpowiedzialnej za dowiezienie produktu — niekoniecznie.
Najpierw przeanalizowałem obie wersje. Nie szukałem tej z najładniejszym kodem. Interesowało mnie:
- ile produktu rzeczywiście już istnieje,
- które funkcjonalności działają,
- co można naprawić,
- czego brakuje,
- ile czasu zajmie doprowadzenie każdej wersji do celu,
- jakie ryzyko niesie każda z dostępnych dróg.
Po tej analizie podjąłem decyzję o powrocie do pierwszej wersji produktu. Nie dlatego, że była idealna. Dlatego, że dawała największą szansę na dowiezienie projektu.
Refactoring zamiast rewolucji
Najpierw trzeba było zrozumieć odziedziczony system. Przeanalizowałem projekt i przygotowałem dokumentację. Dopiero później można było bezpiecznie zacząć go zmieniać.
Poprawiliśmy problematyczne elementy kodu, uzupełniliśmy brakujące funkcjonalności i uporządkowaliśmy produkt. Nie próbowaliśmy przy okazji stworzyć idealnej architektury na następne 20 lat.
Kiedy więc warto przepisać system?
Są sytuacje, w których rewrite jest uzasadniony. Jeżeli architektura uniemożliwia dalszy rozwój, wykorzystana technologia nie może być już bezpiecznie utrzymywana albo koszt naprawy rzeczywiście przekracza koszt stworzenia nowego rozwiązania — rozpoczęcie od początku może być najlepszą decyzją.
Problem pojawia się wtedy, gdy rewrite staje się reakcją na frustrację. „Kod jest słaby, więc napiszmy wszystko jeszcze raz." To za mało.
Przed podjęciem takiej decyzji chcę znać odpowiedź przynajmniej na trzy pytania:
- Co dokładnie uniemożliwia nam dalszy rozwój obecnego produktu?
- Ile rzeczy, które już działają, będziemy musieli stworzyć ponownie?
- Czy biznes ma czas i pieniądze, żeby czekać na nową wersję?
Jeżeli nie potrafimy na nie odpowiedzieć, prawdopodobnie jest za wcześnie na wyrzucenie istniejącego systemu.
CTO nie wybiera najładniejszego kodu
Rolą CTO nie jest stworzenie projektu, którym najbardziej będzie chciał pochwalić się zespół developerski. Rolą CTO jest podejmowanie decyzji technologicznych, które mają sens dla produktu i biznesu.
Czasami oznacza to nową architekturę. Czasami migrację. A czasami wejście w nieidealny, odziedziczony system i powiedzenie: „Tego nie wyrzucamy. To naprawiamy."
W przypadku IKZMED właśnie ta decyzja pozwoliła nam doprowadzić produkt do celu bez rozpoczynania kolejnego wielomiesięcznego rewrite'u.