środa, grudnia 02, 2009

Inercja i koło wielokrotnego wynajdywania, czyli radosne macki piekieł w kodzie [alem pojechał w tytule ;-)]

Już parę lat pracuję ze wspólnym kodem, czyli takim który pisze wiele osób i którego niektóre fragmenty skrywają swoje pochodzenie w pomrokach dziejów i nikt nie jest w stanie powiedzieć kto je napisał i kiedy [znaczy to tyle, że napisano je jeszcze zanim zaczęto używać kontroli wersji, tudzież używano innego systemu kontroli wersji, a później dokonano przejścia na inny system ;-)], często także nikt nie wie po co ktoś to napisał ;-)

I dwie najbardziej denerwujące cechy takiego kodu to jego inercja i wielokrotne wynajdywanie koła.

Inercja uwidacznia się w taki sposób, że natrafiając na dany fragment kodu developer podświadomie przyjmuje styl pisania w tym fragmencie, przy jednoczesnym braku analizy tego kodu.
To drugie oblicze inercji objawia się tym, że często poprawka polega na dodaniu, zmianie lub usunięciu jednej linii do której doprowadził Cię debuger, ale tak naprawdę nie rozumiesz co zmieniasz.
Oznacza to, że kod z wiekiem obrasta w różnego rodzaju obejścia i tymczasowe rozwiązania, upodabnia się do klasycznych układów na pająka ;-) ale nie tych gdzie pająk jest wynikiem braku płytki, a tych gdzie robi się nim np. wspólną masę.

I w pewnym momencie w kodzie mamy tajemniczą linię:
st.setString(1,values[idx++]);
st.setString(2,values[idx++]);
idx++; // tajemnicza linia
st.setString(3,values[idx++]);
Która oznacza, że prawdopodobnie zmieniło się coś w strukturze danych, i najprostszym rozwiązaniem było zastosowanie takiego tricku ;-)

Pierwsze oblicze inercji wygląda tak, że ktoś kiedyś powtórzył dany kod kilka razy z drobnymi zmianami, np. coś takiego:
if (startValue==null) {
start.setValue(0);
} else {
start.setValue(startValue);
}
if (middleValue==null) {
middle.setValue(0);
} else {
middle.setValue(middleValue);
}
if (endValue==null) {
end.setValue(0);
} else {
end.setValue(endValue);
}

Wtedy niemal pewne jest, że gdy będzie to potrzebne dostaniemy do kodu takie coś:
if (stopValue==null) {
stop.setValue(0);
} else {
stop.setValue(stopValue);
}

Żeby kod był "konsystentny".
Tutaj zaś rozwiązaniem byłoby np. proste zamienienie wszystkich tych "idiomów" w użycie operatora warunkowego ?, a w większości innych przypadków wyrefaktorowanie metody i przekazywanie jej tych drobnych różnic.
Ale tu właśnie pojawia się inercja, bo gdy tego są 3 sztuki to mało kto coś robi, a jak jest ich paręset w całym kodzie w kilkudziesięciu klasach to już taka prosta operacja nie jest ;-)

Wielokrotne wynajdywanie koła też jest ciekawą przypadłością. Zapewne można by próbować wyznaczać ilość developerów pracujących na danym kodzie przez prostą analizę ilość powtórzeń pewnych fragmentów i częstości wyniesienia ich do klas utilowych ;-) O co chodzi?
Np. trzeba zapisać coś do pliku, jakiś tekst, kilka razy, wtedy developer może zrobić metodę saveToFile(String fName,String text) i umieścić ją w klasie Util i niby wszystko OK, ale jest prawie pewne, że za jakiś czas inny developer [a czasem nawet ten sam ;-)], będzie potrzebował metody robiącej dokładnie to samo, nie będzie miał jednak pojęcia o istnieniu tej pierwszej metody i stworzy nową... tym razem może przyjmować np. parametry w odwrotnej kolejności.
W jednej z poprzednich firm mieliśmy chyba 4 kompletnie różne metody pobierania informacji o sprzęcie do którego byliśmy podłączeni i co najlepsze każda z tych metod mogła zwrócić za każdym razem ciut inny obiekt, choć intencja była taka by zawsze zwracała to samo ;-)
Albo metody formatujące, często jest ich kilka zestawów choć każda z nich robi to samo ;-) Bo gdy developer dotarł do miejsca gdzie musiał sformatować dużo liczb czy dat to stwierdził, że dobrze zrobić sobie takie metody........... nie wiedząc, że już gdzieś takie są ;-)

Oczywiście tak inercja kodu jak i wielokrotne wynajdywanie koła nie są dobre. Bo gdy trzeba coś zmienić to okazuje się, że nie wiadomo gdzie to zrobić, że trzeba poprawkę z drobnymi modyfikacjami założyć w milionie miejsc i w końcu, że i tak się kilka tych miejsc przegapiło ;-)

Jak z tym walczyć? Czasem pomagają stand-up'y, bo np. pomagają szerzyć wiedzę o utilach, ktoś mówi "i dziś napisałem trochę kodu do formatowania daty i używajcie tego" i czasem to gdzieś w cudzej głowie utkwi i jest szansa, że wykorzysta. Pomagają czasem inspekcje bo wszyscy jako tako znają dzięki nim kod.
Na inercję pomaga refaktoring. Zwykle pomaga, choć oczywiście nie zawsze. Już byłem w takich miejscach, że przerefaktorowanie kodu powodowało, że musiałem się kilka razy bardziej nagimnastykować by coś osiągnąć bo akurat dany refaktoring był zbyt "ładny", a za mało praktyczny.

Btw. widzicie jak często używam słów niemierzalnych, takich jak "ładny", "czasem", "zwykle" i podobnych? Taki już urok kodowania, że mimo istnienia jakichś 3 milionów typów metryk i tak jedyną metodą na ocenę kodu jest popatrzenie nań przez człowieka, który może dostrzec tam piękno [rzadko ;-)] lub radośnie machające macki piekieł [częściej ;-)].

Ale, choć wspólny kod miewa problemy, to i tak zwykle mimo połączenia ignorancji wielu łączy też ich geniusz i efekt działa i bywa nawet w miarę przewidywalny ;-)
Czasem się tylko coś wysypie ;-)


Podobne postybeta
Przepływ sterowany danymi - A takie Java'owe coś ;-)
O tym czemu branche są złe...
Wyrocznia od siedmu boleści
Nieistniejący rynek
Projekty Informatyczne - Mity ;-)