Ale to jest właśnie najlepsza metoda wychodzenia z legacy code'u.
- Masz legacy shit code i nic nie warte testy. Każdy refaktor niesie ryzyko, bo nie masz testów. Dopisanie testów jest trudne, bo kod jest nietestowalny.
- Dodajesz jeden prosty, dobry unit test. Czyli taki któy nie jest tightly-coupled do logiki, testuje to co ma testować, jest odseparowany od innych testów, działa szybko i rzetelnie na wielu systemach; nie failuje randomowo, i jest w stanie wygryć buga.
Jak chcesz napisać "jeden prosty" test gdy "dopisanie testów jest trudne, bo kod jest nietestowalny"?
Ja najcześciej wybieram najprostszy kawałek obecenie do przetestowania. Część kodu może być bardzo zła; część tylko trochę zła. Można zacząć od tej trochę złej.
Są też inne metody; np zamiast edytować jakąś funkcję w miejscu, możesz napisać jej kopię, przetestować, a potem zacząć stopniowo używać tej nowej w innych miejscach, aż starej już nie ma użyć. Dużo jest na to sposobów, zależy od tego jak w dużym szambie jesteś.
Mogę o tym opowiedzieć więcej jeśli jesteś zainteresowany, jak to dokładnie działa.
Także refaktor i pisanie testów to nie to samo; ale czasem jedno jest warunkiem drugiego. Czasem wejdziesz do projektu i znajdziesz nietestowalny kod. Autentycznie, kod którego się nie da przetestować, chyba że jakimiś niezdrowymi sposobami albo manualnie.
Więc wtedy masz do wyboru:
- Albo testować to już całkiem manualnie
- Albo używać hacków żeby napisać testy na siłę
- Albo zostawić nieprzetestowane
- Albo zrefaktorować kod, i napisać normalne testy
Spoiler alert: tylko jedna odpowiedź jest poprawna (zakładając że nadal chcesz rozwijać projekt).
A to powyższe stoi w sprzeczności z tym wcześniejszym. Albo najpierw piszesz testy i potem refaktoryzujesz, albo najpierw refaktoryzujesz bez testów, a potem piszesz normalne testy.
Okej, widzę czemu ktoś mógłby tak pomyśleć.
Ale tak po prostu jest. Im więcej refaktora robisz (innymi słowy, im bardziej odseparowane są elementy od siebie im lepiej kod jest zaprojektowany), tym lepiej się wprowadza testy. Oraz, zarówno; im masz więcej lepszych testów, tym lepszy refaktor możesz zrobić.
To są takie dwie machiny napędzające się nawzajem.
Jeśli obie stoją to będzie ciężko ruszyć którą kolwiek z nich; ale to właśnie musimy zrobić, po kawałku odpalać dwie na raz, na początku bardzo powoli, ale z czasem ruszą. I mówiąc "bardzo powoli" mam na myśli, że np jeden test, albo jeden prosty refaktor. Jednostkowo, te rzeczy można zrobić szybko, ale trzeba ich zrobić bardzo dużo na początku. Takiej średniej jakości projekt powinien mieć w mojej opinii około 2000-5000 testów jednostkowych, taki standardowy. Duże projekty, odpowiednio dużo więcej. Nie napiszesz tego w miesiąc czy dwa. Ale możesz zacząć od 1, potem od 5, 10, etc. i tak dalej.
Nie posądzam Cię o brak profesjonalizmu, ale czasami z Twoich wiadomości odnoszę wrażenie, że mówisz piękne rzeczy, ale utopijne i niezgodne z rzeczywistością. Coś jak "ludzie powinni się kochać" albo "scrum nie zajmuje dużo czasu".
No; ale co ja Ci poradzę. Ja tak robię w swoich projektach zarówno w pracy jak i prywatnie, i to jakoś działa. Także dla mnie to nie jest utopia tylko pewien konkretny (co prawda wysoki, ale konkretny) standard.
Osobiście, moim zdaniem największym problemem stojącym "rzeczywistości" na drodze jest przekonanie że się nie da. Gdyby tylko pokazać programistom: "Patrz, widzisz: tak mogę zrobić refaktor, tak mogę napisać test", to by to robili, tak mi się wydaje. Ale ponieważ mają bardzo mocno zakorzenione "Nie da się", albo mocno zakorzenione takie przekonanie "sprzątanie kodu jest po to żeby było ładnie, nie po to żeby działało", to ciężko walczyć z wiatrkami.
W Star Wars, jak się szkoliło Jedi to był taki etap nauki "Wyciszenie mąrości głupca". U programistów by się to przydało ;)
Jak masz dużo badziewnego kodu, to nie zrobisz refaktora w minutę,
Jak masz dużo badziewnego kodu, to nie zrobisz refaktora w minutę,
Zależy co masz na myśli mówiąc "nie zrobisz refaktora". Czy masz na myśli to żę zrefaktorujesz cały projekt, albo chociaż jeden modół? Oczywiście że nie.
Ale czy zrefaktorujesz jedną funkcję lub jedną zmienną? Już możesz to zrobić w minutę; nawet nie wiem czy nie krócej. Ja przynajmnej bym mógł jedną funkcję.
ani też nie napiszesz jednego prostego testu jednostkowego.
Hmm, zależy. W dobrym środowisku; np kiedy piszesz nową funkcję albo dopisujesz kod do już dobrze zaprojektowanego kodu jaknajbardziej. Mi się czasem udało zrobić kilka testów w minutę. Czasem się da jeden test dodać w kilka sekund, i nie zartuję teraz. I to dobry test, rzetelny, nie mający false-negative'ów, i false-positive'ów i nie couplujący się do kodu. Oczywiśce nie zawsze jest to możliwe, czasem trzeba usiąść.
Jeśli natomiatst dostajesz metodę pełną globalnego stanu, statycznych zmiennych, poukrywanych zależności, taką która miesza logikę z widokiem, albo bóg wie co jeszcze, to oczywiście że nie napiszesz go szybko; ale nie dlatego że nie da się napisać testu szybko, bo da się - ale trzeba najpierw odseparować testowaną funkcję od reszty syfu, w miarę możliwości, i to zajmuje najwięcej czasu. Samo napisanie testu jest szybkie i proste (zakładając oczywiście że wie co się robi).
Z radością zobaczyłbym, jak robisz takie rzeczy w ośmiotysięcznikach javascriptowych,
Możemy kiedyś znaleźć taki i zrobić eksperyment.
Ale opowiedz mi, co Ci się wydaje. Myślisz że to co mówię brzmi spoko, ale gdybyś faktycznie miał spróbować odnieść takie praktyki do rzeczywistości to by się nie udało?
No i dochodzi cały kontekst biznesowy, który Ty całkowicie ignorujesz. W końcu ważniejsze jest, żeby kod był dobry, a nie żeby firma przetrwała na rynku i mogła zarabiać (to była ironia).
No oczywiście że ważniejsze jest żeby produkt robił to co ma robić - i jeśli stworzono go z myślą o zarabianiu to powinien to robić. I faktycznie może tak być, że g**no kod zarabia na siebie. Jeśli robię cokolwiek, to właśnie po to żeby firma przetrwała jak najdłużej, poprzez nie zwiększanie kosztów utrzymania oprogramowania. Najlepszy przykład to legacy code: w wielu firmach znajdziesz bardzo stare produkty, które są absolutnie odrażające gotowe, najgorszy kod na świecie; ale działają. Tylko one mają prawo działać tylko dlatego że nie ma żadnych zmian w nich. Nikt nie przychodzi, i nie mówi żeby to zmienić, to zmienić, to usprawnić. Także taki program, mimo że okropny, to jest stabilny i działa. 0 bugów, 0 dodatkowych feature'ów, po prostu działa tak jak ma działać. No ale z przykrą konsekwencją tego że jak coś w nim zmienisz to się posypie. Trochę tak jak domek z kart, dopóki go nie dotkniesz (albo coś innego go nie dotknie) to będzie stał. I ja bym bardzo chętnie zostawił taki legacy code tak jak jest.
Ale jak widać, że nowe zmiany dochodzą co tydzień albo codziennie; to musisz zadbać o to żeby te zmiany się dało wprowadzać szybko i tanio.