Rzeczy, których nigdy nie powinieneś robić, część I

Oryginalny post: Things You Should Never Do, Part I

Autor: Joel Spolsky

Netscape 6.0 wreszcie dotarł do swojej pierwszej publicznej wersji beta. Wersja 5.0 nigdy nie istniała. Ostatnie istotne wydanie, 4.0, zostało opublikowanie prawie 3 lata temu. Trzy lata to bardzo długi okres w świecie Internetu. Przez cały ten czas Netscape bezsilnie patrzył na to, jak jego udział w rynku spada na łeb, na szyję.

To odrobinę lizusowskie z mojej strony, by krytykować ich za tak długą zwłokę w publikacjach. Nie zrobili tego celowo, nieprawdaż?

W rzeczy samej - zrobili. Popełnili ten największy błąd strategiczny, jaki może popełnić firma:

Zdecydowali się przepisać program od zera.

Netscape nie było pierwszą firmą, która popełniła taki błąd. Borland zrobił to samo, gdy kupił Arago i próbował go przerobić na dBase dla Windows - projekt skazany na zagładę, który trwał tak długo, że Microsoft Access zdążył zjeść ich kawałek tortu. Później zrobili dokładnie to samo, przepisując Quattro Pro od zera i zaskakując swoich klientów tym, jak uboga była ich aplikacja po publikacji. Microsoft także otarł się o ten sam błąd, próbując przepisać Word dla Windows w projekcie skazanym na porażkę - Pyramid. Pyramid został przerwany i zamieciony pod dywan. Szczęśliwie dla Microsoftu, ich programiści nie przestali rozwijać starej wersji, więc mieli co wysłać w świat - obracając wydanie jedynie w finansową, a nie strategiczną, katastrofę.

Jesteśmy programistami. Programiści są w głębi serca architektami, a pierwsze co chcą zrobić, gdy dostaną się na plac budowy, to zrównać wszystko z ziemią i zbudować coś wspaniałego i wzniosłego. Nie czerpiemy radości z przyrostowej odbudowy: majsterkowania, poprawiania, sadzenia klombów.

Jest pewien subtelny powód, dla którego programiści zawsze wolą wyrzucić kod do śmieci i spróbować od początku. Powód ten jest mianowicie taki, że programiści myślą, że stary kod to bajzel. I tu ciekawa obserwacja: prawdopodobnie są w błędzie. Powód, dla którego myślą, że stary kod jest stertą śmieci, wynika z jednego fundamentalnego prawa związanego z programowaniem:

Trudniej jest czytać kod niż go pisać.

To dlatego ponowne wykorzystywanie kodu jest takie trudne. To dlatego każdy członek Twojego zespołu ma inną funkcję do dzielenia łańcucha znaków na tablicę łańcuchów. Piszą własną funkcję, ponieważ to jest łatwiejsze i ciekawsze niż główkowanie nad tym, jak działa stara funkcja.

W następstwie tego, gdy zapytasz niemalże dowolnego programistę o kod, nad którym pracuje, ten odpowie Ci, że “To jeden wielki bajzel”. “Niczego innego nie pragnę, jak tylko wyrzucić to wszystko i zacząć od początku.”

Dlaczego ten kod to taki bałagan?

“Wiesz,” mówią, “spójrz na tę funkcję. Ma dwa ekrany długości! Żadna z tych rzeczy nie powinna tu być! Nie mam pojęcia, do czego służy połowa z wywołań tych funkcji z API.”

Zanim Borland wypuścił swój arkusz kalkulacyjny dla Windows, Philippe Kahn, barwny założycieł Borlanda, był wielokrotnie cytowany w prasie, przechwalając się, jak bardzo Quattro Pro będzie lepsze od Microsoft Excel - jako, że miało być przepisane od zera. Cały kod miał być nowiutki! Tak jakby kod źródłowy kiedykolwiek rdzewiał.

Pomysł, że nowy kod jest lepszy od starego jest oczywiście absurdalny. Stary kod był wykorzystywany. Był przetestowany. Znaleziono mnóstwo błędów, które naprawiono. Wszystko jest z nim w porządku. Nowe błędy nie pojawiają się przez to, że ten kod mieszka sobie na Twoim dysku twardym. Wprost przeciwnie, mój drogi! Czy oprogramowanie ma być starym Dodge Dartem, który rdzewieje w Twoim garażu tylko dlatego, że w nim stoi? Czy oprogramowanie to pluszowy miś, który jest jakby brzydszy, jeśli nie jest wykonany całkowicie z nowych materiałów?

Wróćmy do dwustronicowej funkcji. Tak, wiem, to jest zwyczajna funkcja do wyświetlania okna, ale urosła odrobinę i nikt nie wie dlaczego. Powiem Ci dlaczego: to wszystko to poprawki. Jedna z nich naprawia błąd, na który natknęła się Nancy, gdy próbowała zainstalować aplikację na komputerze, który nie miał Internet Explorera. Inna naprawia błąd, pojawiający się, gdy brakuje nam pamięci. Jeszcze inny naprawia błąd, który pojawia się, gdy użytkownik uruchomi program z dyskietki i wyjmie ją w trakcie pracy. To odwołanie LoadLibrary jest paskudne, ale dzięki niemu kod działa na starszych wersjach Windows 95.

Każdy z tych błędów potrzebował tygodni działania programu w prawdziwym świecie, zanim został znaleziony. Programista mógł spędzić kilka dni, próbując odtworzyć ten błąd i go naprawić. Jeśli jest tam mnóstwo błędów a naprawienie każdego z nich kosztowało jedynie linijkę kodu - lub nawet kilka znaków - to w te kilka znaków włożono ogrom czasu i pracy.

Gdy wyrzucasz kod i zaczynasz od zera, wyrzucasz całą tę wiedzę. I zebrane poprawki. Lata programistycznej pracy.

Wyrzucasz pozycję lidera na rynku. Dajesz swoim konkurentom prezent w postaci dwóch lub trzech lat, i wierz mi, to bardzo długo w tym świecie.

Stawiasz się w wyjątkowo niebezpiecznej sytuacji, w której będziesz sprzedawał starą wersję programu przez kilka lat, kompletnie niezdolny do wprowadzania strategicznych zmian i reagowania na nowe żądania rynku - dlatego, że nie masz kodu, który mógłby być wydany. Równie dobrze mógłbyś zamknąć biznes na ten czas.

Marnujesz niebiańską sumę pieniędzy na pisanie kodu, który już istnieje.

Czy istnieje alternatywa? Konsensusem wydaje się być to, że kod starego Netscape’a był naprawdę kiepski. Cóż, może i był kiepski, ale wiesz co? Działał wcale nieźle na ogromnie dużej ilości prawdziwych komputerów.

Kiedy programiści mówią, że ich kod to koszmar (jak zwykle), to są ku temu trzy powody:

Po pierwsze, problemy związane z architekturą. Kod nie jest produkowany należycie. Kod związany z siecią pokazuje okna dialogowe znikąd; to powinno być zadaniem kodu związanego z interfejsem. Te problemy mogą zostać rozwiązane, jeden po drugim, za pomocą ostrożnego przemieszczenia kodu, refaktoryzacji, zmian w interfejsach. Mogą zostać dokonane przez jednego programistę, pracującego ostrożnie i wrzucającego swoje zmiany na raz, kiedy nikomu to nie przeszkodzi. Nawet dość poważnie zmiany w architekturze mogą zostać przeprowadzone bez pozbywania się kodu. W projekcie Juno spędziliśmy kilka miesięcy przebudowując jeden fragment: po prostu przemieszczając kod, czyszcząc go, tworząc klasy bazowe, które mają sens, wyznaczając jasne interfejsy między modułami. Zrobiliśmy to jednak ostrożnie, z istniejącą bazą, i nie wprowadziliśmy nowych błędów - nie wyrzucając kodu.

Drugim powodem, dla którego programiści myślą, że ich kod jest do bani, to jego niewydajność. Chodziła plotka, że kod związany z odrysowywaniem w Netscape jest wolny. To jednak dotyczy tylko małej części projektu, którą możesz zoptymalizować czy nawet przepisać. Nie musisz jednak przepisywać całego projektu. Gdy optymalizujesz wydajność, 1% pracy przekłada się na 99% efektu.

Trzecia rzecz: kod może być paskudnie brzydki. Jeden projekt, nad którym pracowałem, miał nawet typ danych nazwany “PieprzonyString”. Inny projekt zaczynał z konwencją, w której nazwy pól zaczynały się od podkreślenia. Później zmieniono to na bardziej standardowe “m_”. Tym samym połowa funkcji zaczynała się od “_” a druga od “m_”, co wyglądało paskudnie. Szczerze, jest to jedna z tych rzeczy, które załatwiasz w 5 minut jednym makrem w Emacsie, nie zaczynając niczego do początku.

Ważnym jest, żeby pamiętać, że gdy zaczynasz pisać coś od początku, nie ma absolutnie żadnego powodu, dla którego można myśleć, że odwalisz lepszą robotę niż za pierwszym razem. Przede wszystkim, najprawdopodobniej nie masz takiego samego zespołu jak ten, który pracował nad pierwszą wersją - a tym samym nie masz “większego doświadczenia”. Popełnisz większość tych samych błędów ponownie i wprowadzisz nowe, które nie istniały w oryginalnej wersji.

Stara mantra “pierwsza wersja jest do wyrzucenia” jest niebezpieczna, gdy zastosuje się ją do produktów komercyjnych wielkiej skali. Jeśli piszesz eksperymentalny kod, możesz chcieć usunąć funkcję, którą napisałeś tydzień temu, gdy wpadniesz na lepszy algorytm. To jest w porządku. Możesz chcieć zrefaktoryzować klasę, by dało się z niej łatwiej korzystać. To też jest w porządku. Wyrzucanie całego programu to jednak głupota i jeśli tylko Netscape miałby jakiś dojrzały nadzór i doświadczenie w branży, to mógły uniknąć tak bolesnego strzału w stopę.

Data publikacji oryginału: kwiecień 6, 2000

10 komentarze:

sebas86 pisze...

Ostatni rok to w większości czytanie i poprawianie kodu po kimś, cieszę się, że to nie tylko doskonalenie własnych umiejętności.

Konradzik pisze...

Fajny artykuł, daje do myślenia.

Trochę paskudnego kodu w życiu widziałem i:jeżeli projekt był mały - przepisywany był od zera, jeżeli projekt był duży - przepisywany był po kawałku, w miarę potrzeb, w miarę prac w podobnym regionie kodu (warunikiem jest ta sama technologia). I ten system nigdy mnie nie zawiódł.

Kod może nie rdzewieje, ale z czasem często odkrywamy wąskie gardła (zwiększone obciążenie), architekturę która uniemożliwia rozbudowę itd. Przepisywanie kodu jest rzeczywistością. Trzeba tylko, jak ze wszystkim, zachować umiar i mieć plan.

MDW pisze...

Dobry tekst. :-)
Poruszony w tym tekście problem naprawdę istnieje. Dopiero teraz to sobie uświadomiłem. Rzeczywiście zawsze mam wielką ochotę przepisać od zera wszystko co kiedykolwiek stworzyłem. Tak jest zawsze! Nie wiedziałem, że to jest problem większości programistów. Myślałem, że to tylko moja przypadłość. Ja jednak też zdałem sobie sprawę z tego, że tylko przepisywanie kawałków ma sens. Przepisywanie wszystkiego od zera rzeczywiście jest zabójcze.

Gerard pisze...

Jest tu w serwisie przetlumaczony artykul o ostrzeniu pily (http://www.devblogi.pl/2009/12/ostrzenie-piy.html). Pomyslalem o kolejnych moich programach jak o scietych drzewach.

Poprzednie drzewo cialem tepa pila, ale scialem i drzewo lezy. Teraz mam ostrzejsza pile (albo tak mi sie wydaje), ale drzewo, ktore lezy, nie bedzie bardziej lezalo. Lepiej nastepne drzewo sciac efektywniej, a to uznac juz za sciete.

Anonimowy pisze...

Artykul opisuje prawdziwe zjawisko... Jak zasiadam do czyjegos kodu, to mi sie noz otwiera w kieszeni, jak mam cos po kims naprawiac albo zmieniac, to naprawde wole pisac od zera....

Michał Środek pisze...

Netscape upadł z całkowicie innej przyczyny. Microsoft "zmusił" producentów sprzętu(np. Compaq) do dodawania do sprzedawanych przez nich komputerów IE zamiast Netscape. Inaczej cofnąłby im licencję na sprzedaż Windowsa co skończyło by się brakiem zainteresowania przez klientów tymi komputerami.

Jeśli chodzi o przepisywanie programów od nowa to miejscami piszesz głupoty. Owszem czasami bardziej opłaca się zostawić kod i go nie ruszać jednak to nie jest tak, że przepisywanie to czyste zło. Często aktualna budowa aplikacji nie pozwala na jej dalszy rozwój w zadowalającym tempie. Podajesz przykłady kilku programów, które się pogorszyły. Czemu nie podasz kilku przykładów aplikacji, które po przepisaniu działały lepiej i wydajniej?
Firefox, KDE, silniki gier(w zasadzie po 10 latach każdy jest przepisywany od nowa), GMail oraz w zasadzie każdy większy portal internetowy.

Anonimowy pisze...

kiedy FF, KDE, silniki gier, GMail zostały przepisane? Od zera?

godlark pisze...

A myślałem, że tylko ja mówię WTF?, gdy patrze na kod innego programisty. Tak było jak popatrzyłem na kod Wordpressa.
No, ale tutaj to była dobra decyzja, zacząłem pisać kod na frameworku Kohana ;) No i przynajmniej się czegoś uczę.

Te silniki to przepisywane nie są w całości, w większości to refaktoryzacja.

gilthanas pisze...

Jak w którymś momencie stwierdzasz: "z tego gówna już nic się nie da wyrzeźbić ... " to wtedy jest czas na zaoranie i napisanie czegoś od nowa.

Część argumentów które tu podano są jak najbardziej słuszne, ale są też i takie które przemawiają w drugą stronę. Jeżeli ktoś załatał jakąś dziurę, to nie wiadomo czy załatał ją całkowicie, czy była to dziura koncepcyjna i pomimo załatania w jednym przypadku, taka dziura ujawni się w kolejnym. Jeżeli ktoś na etapie planowania popełnił błąd lub nie posiadał wiedzy, którą posiadł później, a było już za późno na przebudowę połowy systemu, to bez zaorania takiego problemu się nie naprawi.

Projektuje obecnie systemy, kiedyś byłem także programistą i wiem, że czasami pierwsza, a nawet druga wersja systemu trafia w pewnym momencie na ścianę, którą da się przebić tylko i wyłącznie robiąc nowy system lub przepisując silnik systemu. Ważne jest, aby system był tak zbudowany, żeby można było zaorać i przepisać część funkcjonalności lub silnik, a nie ruszać 90% funkcjonalności.

Anonimowy pisze...

ja muszę przyznać, że przez ostatnie 1,5 roku mój zespół nie tyle nawet, że przepisywał, ale zupełnie tworzył na nowo kod, który de facto istniał, ale był dziurawy. w naszym przypadku wyszło to zdecydowanie na dobre. fakt, że przez 1,5 roku nie stworzyliśmy nic zupełnie nowego. ale to co mamy teraz jest o niebo lepsze od starej wersji. projekt zyskał na wydajności, łatwości utrzymania, przyjemności użycia (user-friendly) a i również na funkcjonalności. nie zawsze robienie czegoś od zera jest złe.

Prześlij komentarz

Related Posts with Thumbnails