Miroslav Holec
Premium

NDepend

Miroslav Holec   27. prosince 2014upd. 29. března 2016

Tento článek je již velmi zastaralý. Článek nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.

Ndepend je nástroj, který pomáhá na základě nejrůznějších pravidel analyzovat zdrojový kód, odhalovat potenciální problémy a v konečném důsledku tak zlepšovat kvalitu vašeho kódu. Po prvním spuštění může být vývojář zmaten poněkud komplikovanějším rozhraním, později však dává všechno smysl a dá se říct, že nástroj je to svým způsobem dost jednoduchý. Jeho síla je především ve schopnosti rychlé analýzy a výpočtech.

Metriky

Než napíšu první řádky o samotném nástroji NDepend, chtěl bych se zastavit u některých metrik, které souvisí s kvalitou a komplexností zdrojového kódu. NDepend pracuje z 82 metrikami. Pochopení některých z nich je celkem klíčové pro práci s analytickými výstupy. Samotné Visual Studio vlastně také umí měřit "kvalitu kódu", byť poskytuje jen málo ukazatelů.

Ca - Afferent Coupling at type level

Udává kolik jiných typů je přímo závislých na jiném typu. Hodí se pro rekurzi (re-engineering), hledání mrtvého kódu aj.

Ce - Efferent Coupling at type level

Udává to samé jako Ca ale zahrnuje i 3rd party assemblies. Obvykle typy s Ce > 50 jsou již uváděny jako příliš komplexní a často mají více než jednu odpovědnost (mohlou být tedy kandidáty pro refactoring).

LCOM - Lack of Cohesion Of Methods

Vychází ze SRP a taková třída by měla být soudržná. NDepend pracuje se dvěma ukazateli LCOM a LCOM HS (Henderson-Sellers), kde LCOM [0-1] a LCOM HS [0-2]. Platí, že vyšší číslo je horší (nedostatečná soudržnost). Mohl bych tu napsat vzorec, který bere v potaz počet metod ve třídě, instance, properties atd. ale nemyslím, že někdo je masochista nebo si tolik neváží času, aby z nudy počítal soudržnost svých tříd, když to umí právě NDepend.

Vyhnout bychom se měli třídám s (LCOMHS > 1 && počtem fields > 10 && počtem metod > 10).

CC - Cyclomatic Complexity

Metrika definovaná nad metodami. Jak název napovídá, vyjadřuje jak je třída komplexní. Její výpočet je primitivní, bere v potaz vyhrazená klíčová slova a jejich počet + 1 udává právě CC metody. Ve své podstatě tato klíčová slova dělají v kódu určité "vyhýbky", dělají jej komplexnějším (což lze znázornit snadno grafem), hůře udržovatelným a co je nejhorší - těžko testovatelným. Těžko pochopitelné jsou metody s CC > 15, CC > 30 jsou již extrémně komplexní.

ILCC - IL Cyclomatic Complexity

Zmíněná CC je závislá na jazyce, proto NDepend používá Independent Language CC. Výpočet jsem nezkoumal ale dle doporučení platí, že čísla oproti CC jsou následující 15~20 a 30~40. Tedy všechno nad 20 je už zajímavé, nad 40 celkem kritické.

DIT - Depth of Inheritance Tree

Poslední zajímavá metrika udává hloubku zanoření ve stromu dědičnosti (včetně System.Object). Dle NDepend je problém DIT >= 6, byť já jsem DIT našel v pravidlech obvykle v kombinaci s dalšími pravidly.

Princip NDepend a odvozování

NDepend je defacto expertní systém zaměřený na měření kvality zdrojového kódu. Jsou do něj navkládána určitá pravidla včetně těch odvozovacích a k oživení potřebuje pouze data v podobě .NET assemblies, která bude schopen procházet a analyzovat pomocí reflexe.

Například hledáte datové typy, které jsou kriticky velké. NDepend reflexivně pracuje s celým vaším kódem a snaží se pomocí předdefinovaných pravidel najít shody. Tato pravidla jsou realizována pomocí různých dotazů. Vše je dotaženo k dokonalosti, protože NDepend používá tzv. Code Query over LINQ (CQLinq), tedy dotazovací jazyk postavený na LINQu, který podporuje:

  • code completion a intellisense
  • live compilation včetně zobrazení a popisu chyb
  • tooltip dokumentaci

To znamená, že jakékoliv pravidlo, které vám přijde mírné, můžete zpřísnit vzhledem k vašemu kódu. Mnou zmíněné pravidlo vypadá například následovně:

warnif count > 0 from t in JustMyCode.Types where 
   t.NbLinesOfCode > 500 
   orderby t.NbLinesOfCode descending
select new { t, t.NbLinesOfCode, t.NbILInstructions,
                t.Methods, t.Fields }

Z kódu je patrné, že bych mohl pravidlo zpřísnit přepsáním počtu řádků. Nebo mohu udělat i něco odvážnějšího:

warnif count > 0 from t in JustMyCode.Types where 
   (t.NbLinesOfCode > 300 || t.Methods.Count() > 20)
   orderby t.NbLinesOfCode descending
select new { t, t.NbLinesOfCode, t.NbILInstructions,
                t.Methods, t.Fields }

Také bych si mohl napsat pravidlo zcela vlastní, které bude řešit úplně jiný problém. Syntaxe je naprosto intuitivní.

Principy NDepend

Na obrázku výše můžete vidět výsledek dotazu, který jsem si rozšířil o sloupec AvgCC, který mi počítá průměrou cyklomatickou komplexitu třídy (na základě celkové CC a počtu metod). Je to ukazatel skoro k ničemu, jen jsem chtěl ukázat, že i výstup je možné si různě přzpůsobit :)

Rule

O pravidlech jsem se tedy zmínil. NDepend umožňuje zobrazit seznam míst, která porušují kritická pravidla nebo je zkrátka možné zobrazovat kód, který porušuje pravidla konkrétní. Ta jsou rozdělena do několika skupin souvisejících s OOP, návrhem, jmennými konvencemi, potenciálně mrtvým kódem nebo přímo využitím .NET frameworku. Pravidla jsou často velmi spolehlivá a například odhalení mrtvého kódu je trochu spolehlivnější než v případě "vyšedlých" snippetů, jak to dělá ReSharper. Našel jsem i nějaká pravidla sporná, například v sekci jmenných konvencí (maďarská notace apod.). Trochu tápu i v pravidlech souvisejících s architekturou.

Pravidla příliš často nepracují s pokročilými metrikami ale spíše se snaží kontrolovat různé konvence. Zde je pár příkladů pro představu:

Pravidla ze sekce OOP v NDepend

Všechna pravidla si můžete prohlédnout přímo na webu včetně definice.

Graph, dependency matrix

NDepend nabízí celou řadu grafů, které umožňují lépe pochopit různé závislosti v projektu. Některé grafy mohou působit na první pohled nepřehledně ale je možné je dále přizpůsobit. Velmi užitečná jsou maticová zobrazení závislostí, která často neukazují pouhou závislost ale i míru závislosti. Závislosti je možné zobrazovat i nad komplexnějšími třídami, pokud například chystáte refactoring nějakého molocha. Na obrázku je například vidět maticový graf závislostí metod uvnitř třídy global.asax:

NDepend - matice závislostí

Pokud bychom odstranily metody, které u sebe mají jen zelené čtverečky, sice bychom omezily nějakou funkcionalitu aplikace ale teoreticky by měla být aplikace zkompilovatelná a závislosti by neměly být porušeny. V případě metod, které u sebe mají modrý čtvereček je tomu opačně. Jsou "used by" a jejich odstraněním by se aplikace rozbila. Můžeme pak dále vizuálně zjistit, jak moc jsou některé metody závislé na okolních (mají mnoho zelených čtverečků) nebo jak moc jsou metody systému závislé na některé jiné metodě (je u ní mnoho modrých čtverečků).

Trend

Další zajímavou funkcí je průběžné logování statistik souvisejících se stavem kódu. NDepend si ve výchozím nastavení denně ukládá data o počtu napsaných řádků, třídách, metodách ale i o všech specifických metrikách a jejich výsledcích nad kódem. Díky tomu se lze v čase dívat například na to, jak přibývá kód, jak se zlepšuje celková CC, LCOM nebo jaké je pokrytí kódu Unit Testama. Hezky vše vypadá na úvodním dashboardu:

NDepend - dashboard

Metric

Zatímco rule (pravidla výše) jsou spíše hotová doporučení, která vychází z logických návrhů, metriky jsou v NDepend již naměřené stavy kódu a reprezentují jeho kvalitu nebo komplexitu. Můžete s nimi naložit dle vlastního uvážení. Pokud si zobrazíte kód na základě metrik, obvykle budete ve výsledcích vidět právě čísla o kterých jsem psal na začátku článku (CC, ILCC, DIT...). Mnoho metrik je složených, tedy nepracují například jen s CC ale třeba pro zjištění komplexnosti metod si berou na pomoc i DIT.

Kromě zobrazení jednotlivých metrik lze zobrazit i tzv. code metrics view. Tento grafický pohled ve své podstatě umožňuje zvýraznit v kódu klíčové oblasti z různých hledisek. Umožňuje to pohled na systém jako takový, abstrahovat od čísel ale spíše se zaměřit na problematickou oblast a tu řešit v rámci kontextu.

Příklad: Vím, že mám problém se soudržností tříd a chci ji postupně napravit. Abych identifikoval problém v rámci celku, zobrazím si LCOM TOP 20 a následně TOP 20 míst v rámci aplikace nad code metrics view, abych viděl, kde problémy vznikají:

NDepend - code metrics view

Často se pak problémy řeší snadněji, když je zřejmé, že původcem zla je jeden autor :)

Coverage

Souvisí s pokrytím kódu unit testy. Vzhledem k tomu, že úzce používám pro tento účel dotCover, který mi zvýrazňuje přímo nepokrytý zdrojový kód barvou a procentuelně a používá stejný interface jako ReSharper, neměl jsem tuto sekci ani potřebu zkoumat.

Export to...

Celý NDepend ještě sám o sobě nabízí řadu dalších malých funkcí, které jsem nezmínil. Všechny výstupy dotazů je možné exportovat do souborů různého typu (txt, xls, html) nebo do různých matic, grafů reprezentujících závislosti nebo treemap. Lze tedy udělat code review pro klienta se zaměřením na kvalitu kódu a dle určitých metrik si udělat celkem bezpracné a přehledné výstupy.

Webmodelka (Temporary)  (analysis done Today 15:58 most recent)

(from t in Types orderby t.LCOM descending
select new { t, t.LCOM }).Take(5)

5 items

--------------------------------------------------+--------------+
                                                  |Lack of Cohesi|
types                                             |on Of Methods |
                                                  |(LCOM)        |
--------------------------------------------------+--------------+
Webmodelka.Lib.Subreg.Info_Object_Contact         |0.95          |
Webmodelka.Lib.Subreg.Make_Order_Params           |0.94          |
Webmodelka.Lib.Subreg.Users_List_User             |0.94          |
Webmodelka.Lib.Subreg.Info_Domain_Data            |0.94          |
Webmodelka.Lib.Subreg.Info_Domain_CZ_Data         |0.93          |
--------------------------------------------------+--------------+
Sum:                                              |4.69          |
Average:                                          |0.94          |
Minimum:                                          |0.93          |
Maximum:                                          |0.95          |
Standard deviation:                               |0.0053        |
Variance:                                         |2.792959E-05  |
--------------------------------------------------+--------------+

Report summary

Existují dva reporty. Ten první se prochází pomocí menu ve Visual Studiu a psal jsem o něm v podstatě až do této chvíle. Ten druhý se generuje do XML soborů (možná je sdílený a generují se z něj reporty pro VS - nezkoumal jsem to) a zobrazí se v prohlížeči jako HTML stránka. Uloží se kamsi do C:/Users/{{YOURNAME}}/AppData/Local/Temp/NDependTemporaryProject/ a je možné jej procházet, filtrovat, zobrazovat různé grafy aj. Je to ideální na první analýzu projektů, protože vše je graficky přehledné, výsledky se dají snadno filtrovat a není problém odhalit největší rizika.

NDepend - html report

Závěr

Ndepend stojí za to minimálně vyzkoušet. Nabízí celou řadu zajímavých funkcí, které pomohou velmi snadno získat ucelený pohled na vyvíjenou aplikaci. Z nejsilnějších funkcí nástroje vidím hlavně metriky, code quality analýzu a sledování závislostí. Nad vším tímto je velmi pozitivní přítomnost CQLinq, díky kterému lze v reálném čase sestavovat vlastní dotazy a zpřesňovat tak vyhledávání "potenciálně škodlivého" kódu. Pokud s nástrojem pracujete častěji, k zahození není ani úvodní dashboard a všechny trend charts. Nakonec jsem zcela vynechal funkci Diff, která je schopná porovnat v čase dvě assemblies. Vzhledem ke komplexnosti funkce si ji chci nechat na samostatný článek. Aktuální cena jedné licence pro vývojáře je € 299 s tím, že nástroj si můžete na 14 dní vyzkoušet.