W tym roku w projekcie mam na co dzień styczność z aplikacją Sonarqube do badania jakości kodu. Zlicza ona m.in. dług technologiczny, pokrycie testami i przeprowadza automatyczny Code Review na merge requestach. Jedną z reguł analizatora kodu, która ostatnio często wyłapuje mi code smells, jest przekroczenie tzw limitu Cognitive Complexity dla metod. Twórcy Sonarqube chcieli w jakiś sposób badać stopień skomplikowania kodowanych funkcji i myślę, że wpadli na przejrzyste rozwiązanie. W celu wyjaśnienia jak działa zliczający algorytm przygotowali krótki dokument, do którego link podaję niżej.
https://www.sonarsource.com/docs/CognitiveComplexity.pdf
Cognitive Complexity jest sposobem na policzenie za pomocą punktów poziomu trudności zrozumienia działania i przeznaczenia metody lub klasy. Krótko mówiąc, wyższy wynik CC oznacza, że istnieje więcej abstrakcji, które musimy przeanalizować, by zrozumieć jak ta funkcja działa.
W konfiguracji Sonarqube należy ustawić maksymalną dopuszczalną ilość punktów dla metody - załóżmy, że 15 jest wystarczające na początek. Choć wg Roberta Martina w "Czystym Kodzie" każda funkcja powinna mieć jedną abstrakcję. Sumę punktów w funkcji jest dosyć łatwo podbić powyżej tej wartości, gdy nie dbamy o przejrzystość kodu. Dla przykładu każdy blok if, każdy else, zawartość pętli for, foreach, while, a także blok catch jest punktowany +1. Równiez zagnieżdżanie ich w sobie znacznie podbija licznik.
Dla przykładu kod poniżej zostanie oznaczony do poprawy jako code smell "Cognitive Complexity of methods should not be too high".
function cognitiveComplexity() {
// +1 punkt za abstrakcję pętli 'for'
for (let i = 0; i < 10; i++) {
// +2 punkty (+1 za 'if' i +1 za zagnieżdżenie)
if (i => 5) {
// +3 (+1 za pętlę 'for' i +2 za drugi poziom zagnieżdżenia)
for (let j = 0; j < 10; j++) {
// +4 (+1 za 'if' i +3 za trzeci poziom zagnieżdżenia)
if (j === i) {
alert('j === i')
} else if (j < i) {
// +4 jak wyżej
alert('j < i')
} else if (j > i) {
// +4 jak wyżej
alert('j > i')
}
}
}
}
if (true) {
// +1 za 'if'
return true
}
return false
}
// Suma punktów === 19
// z pewnością zostanie zgłoszona przez SonarAnalyzer do poprawki
Dokument ten jest świetnym uzupełnieniem książki "Czysty Kod" Roberta Martina, gdzie autor zwraca szczególną uwagę na pisanie jak najprostszych metod, z pojedyńczą abstrakcją i z jak najłatwiej rozpoznawalnymi nazwami. Przede wszystkim Sonar dzięki tym założeniom odpowiednio wcześniej ostrzega przed nowymi "zapachami kodu" i ogranicza w ten sposób rozrastanie się długu technologicznego.
Warto przeanalizować sobie ten dokument, by zacząc zwracać uwagę na to, jakie aspekty pisanego przez nas kodu sprawiają trudność w czytaniu go przez innych programistów.
Cognitive Complexity jest ulepszeniem dawno temu opracowanej metryki, jaką jest złożoność cyklomatyczna (Cyclomatic Complexity). Obecnie to nowe rozwiązanie lepiej odzwierciedla skomplikowanie kodu, a starsze np. wskazuje rozsądną liczbę test cases do opracowania. Jeśli działamy na Visual Studio Code, możemy zainstalować sobie dodatek CodeMetrics, który Cyclomatic Complexity naszych metod policzy automatycznie już podczas ich pisania, jak linter.