Dług techniczny – przykład z życia wzięty

Można się spierać co jest długiem technicznym, a co nie jest. Chciałbym Wam pokazać przykład kodu, który świetnie nadaje się do zobrazowania problemu.

Problem:
Na jeden ze stron jest wyszukiwarka- formularz, w którym po wpisaniu danych w inputach i selectach przesyła te dane jako parametry “named”, np.:
example.com/controller/action/field1:value1/field2:value2
Potrzebny jest kawałek kodu javascript, który wygeneruje odpowiedni link i wywoła ten adres

Rozwiązanie 1:

function submitform()
{
    form = document.getElementById('car_form');
    name = form.elements["CarName"].value;
    registration_number = form.elements["CarRegistrationNumber"].value;
    entry = document.getElementById('CarEntry').value
    driver = document.getElementById('CarDriver').value
    linkForm = 'url('/cars/find/')?>';
    location = linkForm+'name:'+name+'/registration_number:'+registration_number+'/entry:'+entry+'/driver:'+driver;
    document.forms["car_form"].action = location;
}

Jest ono poprawne. Warto się zastanowić, czy został w tym przypadku zaciągnięty dług techniczny? Na pewno będą tacy, którzy od razu będą wiedzieć co z tym kodem jest nie tak. Inni będą to czuli w kościach. Resztę pocieszę mówiąc, że ta umiejętność przychodzi z czasem pod warunkiem, że chcesz się uczyć jak pisać dobry kod.

Ten formularz wisi sobie gdzieś w jakimś panelu admina w listingu samochodów. Jednak taka wyszukiwarka jest potrzebna też przy listingu kierowców. Oczywiście kopiujesz rozwiązanie i poprawiasz je do nowych warunków. Nie ma w tym nic złego pod warunkiem, że robisz to z odpowiednim nastawieniem: “kopiuję kod, żeby móc zauważyć części wspólne i przeprowadzić refactoring” (czasem łatwiej mieć dwa dublujące się elementy i na ich podstawie tworzyć uniwersalny element/funkcje niż wymyślać ją od zera).

No i zabierając się za refactoring – w tym wypadku chciałbym zbudować wspólny element (dla tych co raczej siedzą w RoR – partial), który wywołam sobie tam, gdzie potrzebuję – zaczynam widzieć, że teraz oto przyjdzie mi spłacić ów mistyczny dług techniczny. Na czym on polega? Otóż w moim przykładzie funkcja budująca url jest strasznie “sztywna”. Działa w tym i tylko w tym przypadku. Nie ma najmniejszych znamion uniwersalności. Dlatego teraz, zanim zacznę myśleć o elemencie, muszę tą funkcję przebudować na bardziej uniwersalną. Co to znaczy?

Powinna być w stanie złapać wszystkie inputy i selecty, które są istotne przy budowaniu url’a i iterując po nich sprytnie go zbudować. Mogła by wyglądać na przykład tak:

Rowziązanie 2:

function submitform(){
    var params = "";

    $("#car_form input[type!=submit][name!=_method], #car_form select").each(
    	    function(index,element) {
        	    params +=
        	    		  element.name.substr(
        	    				  element.name.lastIndexOf("[")
        	    		  ).slice(1, -1)+
        	    		  ":"+
        	    		  element.value+
        	    		  "/";
    	    }
    );
    window.location.href = 'url('/cars/find/')?>' + params;
    return false;
}

Teraz inputy i selecty są zbierane bardziej automatycznie (“#car_form input[type!=submit][name!=_method], #car_form select”), a nazwy parametrów są brane z parametru name tych inputów i selectów. Dzięki temu powinna działać dla każdego formularza. Mniejsza o szczegóły.

Jednak rozwiązanie 1 nie było złe. Było wręcz idealne (bo proste i zrozumiałe) tak długo, jak było potrzebne w jednym miejscu. Rozwiązanie 2 mogło zająć przynajmniej 4 razy tyle czasu co pierwsze. Dlatego dopóki nie było powtórzeń kodu – było dobre. Dług techniczny nie istniał. Jednak pojawił się w momencie, kiedy zacząłem refaktoring kodu.

Jak to możliwe, że dług techniczny pojawił się w momencie, kiedy zacząłem go “spłacać”?
Czy istniał cały czas i był ukryty, a ja go nagle odkryłem?

Odpowiem na to pytanie nie wprost: Dług techniczny jest tylko pojęciem umożliwiającym zrozumienie ludziom, kŧórzy nie są programistami, dlaczego nie wolno im zachęcać programistów do chodzenia na skróty. Uzmysławia też (mam nadzieję) niedoświadczonym programistom jakie są niebezpieczeństwa związane z chodzeniem na skróty.

Leniwy Dobry, doświadczony programista w ogóle nie musi zagłębiać się w szczegóły czy dług techniczny istnieje, czy nie. Po prostu tworzy dobry kod.

Share Button

3 thoughts on “Dług techniczny – przykład z życia wzięty

  1. Pingback: Credigy Receivables | Dług (The Debt) 1/10

  2. Moim zdaniem to nie jest przykład długu technicznego. Dług techniczny (czy też technologiczny, to chyba lepsze określenie) powstaje wtedy kiedy idziemy na skróty, czyli: olewamy testy, wprowadzamy świadomie duplikację kodu, budujemy beznadziejną architekturę. Dług technologiczny zaciągnąłbyś wtedy, gdybyś skopiował tę funkcję popoprawiał nazwy i odcinkami pozmieniał wybrane linijki.

    Jeśli przed istniejącym kodem realizującym zadanie A, ktoś stawia nowe zadanie B, czytaj oczekuje nowej funkcjonalności możemy ją zrealizować na dwa sposoby: zaciągając dług, czyli kopiujemy poprawiamy nazwy i wybrane linijki. Drugi sposób to refactoring kodu tak, żeby utrzymać ładną architekturę. A do nowej funkcji powinniśmy napisać testy. I wtedy ten dług będzie mniejszy albo zerowy. Z tym, że to kosztuje czas. A, że czas to pieniądz ludzie od pieniędzy zaraz będą chcieli, żeby olać dług. Wtedy z czasem programista się zaczyna męczyć przy takim zadłużonym oprogramowaniu. A jak się zmęczy to odchodzi i ten od pieniędzy zostaje z kłopotem do rozwiązania. Ale na szczęście zawsze są studenci. Tani, więc zwykle dług zwiększają. I tak zmierzamy ku “chińskiej” tragedii.

  3. Ok, to jest kwestia tego jak rozumiesz problem długu technicznego. W moim przykładzie zaciągasz mały dług techniczny pisząc naiwną (sztywną) funkcję. Gdy nadchodzi moment napisania kolejnej funkcjonalności to stajemy przed wyborem: spłacić część długu, odsetkami jest dodatkowy wysiłek; czy go nie spłacać i zrobić drugi raz to samo (zaciągnąć kolejny, już nieco większy).

    Można zapytać dlaczego większy, skoro rozwiązanie identyczne? Dlatego, że gdy z jakichś powodów musimy zmienić generowanie tego url’a (ot choćby zamienić dwukropki na podkreślniki, bo okazało się, że są lepsze z punktu widzenia SEO), wtedy mamy dwa punkty, w których musimy wprowadzać poprawki.

    Zgadzam się z Tobą, kiedy piszesz, że dług powstaje przy kopiowaniu kodu, olewaniu testów itd. Jednocześnie obstaję przy tym, że powstaje również w sytuacji, którą opisałem powyżej.

    Mogę pójść jeszcze dalej – dług techniczy powstaje przy napisaniu każdej litery kodu. Nie możemy go nie zaciągać. Możemy go minimalizować tak bardzo jak się da (życzę wszystkim, żeby w ich projektach dążył do zera). Intuicyjnie czuję, że nawet w najlepszym projekcie, pokrytym w 100% testami, poddawanym agresywnemu refaktoringowi koszty utrzymania i rozwijania kodu rosną. Jednak rosną nieporównywalnie wolniej niż w projekcie, gdzie dług jest zaciągany na potęgę.

    Ale to przecież kwestia terminologii, której rolą jest pomoc w zrozumieniu pewnych mechanizmów, prawda?

Leave a Reply

Your email address will not be published. Required fields are marked *