niedziela, 23 września 2012

NDepend na straży jakości kodu źródłowego [PL]

W ostatnim czasie zapoznawałem się trochę bliżej z narzędziem o nazwie NDepend. Przez ten czas na pewno nie stałem się ekspertem w korzystaniu z NDepend, chciałbym jednak podzielić się moimi subiektywnymi odczuciami na jego temat. Nie będę tutaj wymieniał jego funkcji (można to znaleźć na stronie producenta lub w wielu innych wpisach), nie będę pisał jak zainstalować (bo to proste, choć mi się nie podobało - lubię instalować a nie rozpakowywać :) ), nie będę się zastanawiać czy warto tego narzędzia używać (niech każdy wyrobi sobie własne zdanie, licencja jest odpłatna). Skupię się natomiast na elementach, które ja bym wykorzystał, które mi się spodobały lub które nie wywarły najlepszego wrażenia. Zapraszam do lektury.
Na NDepend po raz pierwszy trafiłem już kilka lat temu, gdy szukałem narzędzia do zobrazowania diagramu zależności pomiędzy poszczególnymi assembly w projekcie, nad którym pracowałem. Nie skorzystałem wtedy jednak z NDepend, gdyż znalazłem jakieś darmowe narzędzie, które było wystarczające, a nie długo później pojawiło się Visual Studio 2010, w którym w jednej z wersji udostępniało menu „Architecture” i funcję „Generate Dependency Graph”. Piszę o tym, bo ukierunkowało to moje spojrzenie na NDepend i chyba nie do końca właściwe... Myślałem bowiem o NDepend jako o narzędziu do eksploracji kodu źródłowego, a tym raczej nie jest.
Ponieważ NDepend służy do analizy kodu źródłowego, trzeba mieć co co analizować. W niniejszym wpisie będę się odwoływał do projektu, który był już opisywany na łamach tego bloga, a mianowicie MeshDiagram3D.

Diagramy

Pierwszym elementem, z którego chciałem skorzystać w NDepend były diagramy, a w szczególności diagram zależności. Na pierwszy rzut oka efekt był bardzo udany, po wykonaniu analizy przez NDepend ujrzeć mogłem:
Otrzymany diagram okazał się nie statyczny, ale nazwałbym go interaktywnym, gdyż można było na nim wykonywać inne operacje. Nie mówię tu tylko o przesuwaniu elementów na ekranie, kolorowaniu ścieżek, ale też wyszukiwaniu poszczególnych elementów bezpośrednio z diagramu lub pokazywaniu dodatkowych statystyk. Ponadto, gdybyśmy poczuli się zdezorientowani, pomogą nam wyświetlane podpowiedzi.
Nie jest jednak tak zupełnie różowo, zaprezentowany powyżej graf zależności został wygenerowany dla bardzo prostego projektu lub raczej „Solution”. Jeżeli spróbujemy przeanalizować coś bardziej skomplikowanego możemy zobaczyć zaplątaną sieć podobną do poniższej:
W tym przypadku nie wiele pomogą funkcje zbliżania, oddalania, zaznaczania, czytelność jest mała. Jednak nie należy tego traktować jako zarzut wielkiego kalibru, sam nie bardzo mam pomysł jak można by to poprawić (no może jakieś "inteligentne" filtrowanie tego co chcemy wyświetlać). Z tego problemu zdaje sobie sprawę sam producent NDepend, i sugeruje w takim przypadku korzystanie z innego sposobu obrazowania, a mianowicie z matrycy zależności (Dependency Matrix):
Na tej matrycy łatwiej zauważyć zależności nawet w bardziej skomplikowanych projektach, a wyświetlane podpowiedzi pomagają w jej zrozumieniu nawet bez czytania dokumentacji.
W każdym razie po wspomnianych diagramach oczekiwałem pierwotnie czegoś trochę innego.

Analiza statyczna kodu w NDepend

Po krótkiej przygodzie z diagramami, zacząłem się bardziej przyglądać NDepend, a dokładniej całej masie uwag, które otrzymałem na temat mojego kodu źródłowego. W analizie mojego prostego projektu znalazłem kilka uwag krytycznych i kilkaset ostrzeżeń. Przyglądając się im dokładniej okazało mi się, że nie do końca wszystkie są uwagami, które powinny nas skłonić do wykonania jakichś akcji, ale pokazują one miejsca, na które warto zwrócić uwagę, mogą to być między innymi:
  • metody lub typy zbyt skomplikowane lub za długie,
  • metody o zbyt dużej ilości parametrów,
  • typy które korzystają z zasobów niezarządzalnych,
  • niewykorzystywany kod,
  • nieoptymalna widoczność metod lub innych elementów,
  • złe nazewnictwo lub organizacja kodu źródłowego,
  • i wiele innych (opartych o ponad 200 reguł)
Mocną stroną NDepend jest właściwość pozwalająca na dowolne modyfikowanie wspomnianych reguł, które zapisywane są w w specjalnym języku o nazwie CQLinq. Jest to znany nam wszystkim w C# LINQ, ale wzbogacony o pewne dodatkowe słowa kluczowe i o pewnym ustalonym źródle danych jakim jest nasz kod.
Zobaczmy przykład:
warnif count > 0 from m in JustMyCode.Methods where 
  m.NbParameters > 8
  orderby m.NbParameters descending
select new { m, m.NbParameters }
Powyższa reguła ostrzeże nas gdy w naszym kodzie będzie metoda, przyjmująca więcej niż 8 parametrów. W ramach informacji otrzymamy listę metod i liczbę parametrów przez nie przyjmujących. Jeżeli nam się ona nie podoba, np. nie chcemy metod z listą parametrów dłuższą niż 10, a w wyniku chcemy otrzymać jeszcze nazwę klasy i assembly, nic prostszego wystarczy że napiszemy:
warnif count > 0 from m in JustMyCode.Methods where 
  m.NbParameters > 10
  orderby m.NbParameters descending
select new { m, m.NbParameters, m.ParentType, m.ParentAssembly }
Nie martwcie się, nie trzeba się uczyć się potencjalnie skomplikowanego modelu informacyjnego kodu źródłowego w NDepend - edytor wykorzystywany podczas wpisywania kwerend jest wyposażony w podpowiedzi kontekstowe.
Warto wiedzieć, że w NDepend dostępnych jest ponad 80 różnych metryk pozwalających na ocenę naszego oprogramowania w różnych aspektach, a dzięki temu dbaniu o jego jakość!

NDepend zadba o ciągłą poprawność.

Kolejną wartościową cechą NDepend jest możliwość wykonywania nie tylko analiz na żądanie, ale też włączenie ich do właściwego build'a oraz uruchamianie analizy i weryfikacji w trybie ciągłej integracji. Mamy tutaj kilka możliwości, ale w najprostszym przypadku wystarczy dokonać edycji pliku .csproj w naszym Solution i zamienić <Target Name="AfterBuild"/> na coś podobnego do:
  <Target Name="AfterBuild">
    <PropertyGroup>
      <NDPath>c:\miejsce-instalacji-NDEPEND\NDepend\NDepend_4.0.2.6750\NDepend.Console.exe</NDPath>
      <NDProject>$(SolutionDir)MeshDiagram3D.ndproj</NDProject>
      <NDOut>$(TargetDir)NDepend</NDOut>
      <NDIn>$(TargetDir)</NDIn>
    </PropertyGroup>
    <Exec
      Command='"$(NDPath)" "$(NDProject)" /OutDir "$(NDOut)" /InDirs "$(NDIn)"'/>
  </Target>
Dzięki takiemu podejściu build nie zakończy się sukcesem gdy wystąpią uwagi o charakterze krytycznym i nie ma tutaj znaczenia, czy będziemy budować kod z poziomu Visual Studio, czy linii poleceń (np. przez MSBuild'a) lub w systemie ciągłej integracji.
 W tym momencie chciałbym jeszcze wspomnieć o możliwości porównywania wybranych build'ów kodu lub historycznych analiz wykonanych przez NDepend. Dla przykładu zwróćmy uwagę na błąd występujący podczas build'u, a widoczny na powyższym rysunku:
"API Breaking Changes: Methods"
Co ten błąd oznacza? Mianowicie NDepend wykrył że zmianie uległa jedna z publicznych metod (a w tym przypadku konstruktor). Więcej szczegółów można znaleźć w przygotowanym raporcie:
 Co w kodzie źródłowym prezentuje się następująco:

Podsumowanie

Niniejszy wpis nie jest kompletnym opisem funkcji NDepend, więcej informacji można znaleźć na stronie producenta, gdzie dostępne są nie tylko opisy, ale również filmy instruktażowe. Ja bym chciał jeszcze wspomnieć tutaj o jednej funkcjonalności, doskonale uzupełniającej opisane powyżej, a mianowicie możliwość sprawdzania pokrycia kodu testami. Niestety nie udało mi się tej funkcji jeszcze wypróbować, ale może kiedyś ją sprawdzę i opiszę na blogu.
Mam nadzieję że w tym wpisie udało mi się pokazać pewne ciekawe funkcje NDepend i przedstawić moje subiektywne odczucie, do czego się to narzędzie nadaje, a do czego raczej nie bardzo. Na pewno sprawdzi się jako strażnik nie pozwalający na dodawanie szkodliwego jakościowo kodu (chociaż trzeba rozważnie dobrać listę reguł), można NDepend wykorzystać również do generowania statystyk. Gorzej NDepend sprawdzi się w roli narzędzia do eksploracji kodu (np. w sytuacji, gdy chcemy zrozumieć zupełnie nie znany nam kod) lub narzędzia dokumentującego kod.

2 komentarze:

  1. Dochodzę do wniosku, że programista abap ma łatwiej :)

    OdpowiedzUsuń
  2. Bardzo wartościowy post, pozdrawiam:)

    OdpowiedzUsuń

Posty powiązane / Related posts