Kwadrant Feathers’a, czyli gdzie refaktoryzować?

Parę ładnych lat temu przygotowałem wpis na temat Złożoności Cyklomatycznej McCabe’a. Pod koniec artykułu wspomniałem o narzędziu, które opiera się o tą metrykę i które pozwala nam określić w które rejony kodu powinniśmy celować planując refaktoryzację. Narzędziem tym jest tzw. Kwadrant Feathers’a. W poprzednim artykule odgrażałem się, że o kwadrancie jeszcze wspomnę, ale jakoś nie było okazji. Aż do niedawna, gdy jeden z czytelników wprost zapytał pod tamtym wpisem, kiedy napiszę coś na ten temat. No to właśnie nadszedł ten moment.

Michael Feathers jest całkiem znanym autorytetem w dziedzinie pracy z legacy kodem – napisał m.in. książkę “Working Effectively with Legacy Code”. Ale dziś skupimy się na artykule z 2011 roku: “Getting Empirical about Refactoring”, w którym rozważał klasyczne podejście do kwestii tego w jaki sposób wybieramy kod do refaktoryzacji.

Jeśli jesteśmy osobą, która refaktoruje kod, to zwykle nie mamy konkretnej strategii wyboru miejsca, w którym przeprowadzimy refaktoryzację. Robimy to w miejscach, które nam wpadną w oko. Zazwyczaj będzie to kod nad którym aktualnie pracujemy. Podejście takie nie jest złe samo w sobie. Napewno jest dużo lepsze niż brak refaktoryzacji. Feathers jednak proponuje świadome posiłkowanie się metrykami zamiast działania na chybił-trafił.

Bazę do dalszych rozważań stanowi ilość commitów przypadających na daną jednostkę kodu (np. plik) – Feathers nazywa tą metrykę “code churn”. Idea jest taka abyśmy szukali kodu, który jest najczęściej zmieniany. Dlaczego akurat taki kod ? Ano dlatego, że dług technologiczny spowalnia naszą pracę za każdym razem gdy się z nim zetkniemy, tak więc kod który dotykamy szczególnie często powinien być w miarę możliwości jak najtańszy w utrzymaniu. Takie spojrzenie może jednak trywializować zagadnienie, ponieważ często klasy które zmienia się często nie są klasami, które chcemy refaktorować. Wyobraźmy sobie kawałek kodu, który jest jakąś wariacją na temat fabryki. Tego typu klasy często są “naturalnymi strefami zgniotu”, czyli miejscem które “przyjmie na siebie” koszt zmiany, tak aby klasy dookoła mogły pozostać niezmienione. Fabryki co do zasady nie są klasami w których mieszka dług technologiczny, no bo umówmy się – nie na codzień da się napisać tragiczną i trudną w utrzymaniu implementację fabryki.

Ale skoro samo code churn nie wystarczy, to powinniśmy znaleźć jakiś inny drogowskaz, który nas pokieruje. Tym drogowskazem wg. Feathersa jest metryka złożoności kodu. Jeżeli szukamy kandydata do refaktoryzacji tylko po złożoności to kod, który jest najbardziej złożony zwykle jest pierwszym podejrzanym. Takie spojrzenie również jest ryzykowne. Często zdarza się tak, że pod względem complexity przodują klasy należące do “core’owej” części naszej aplikacji. Taki kod często był pisany w czasach kiedy projekt mógłbyć rozwijany nieco “po partyzancku”, kiedy zespół nie był jeszcze doświadczony, wymgania się często zmieniały, itd. Z drugiej jednak strony “core” ma to do siebie że na jego barkach opiera się wszystko to co zbudowano później, więc ingerencja w niego jest mocno ryzykowna. Z trzeciej strony grzebanie w najstarszych bebechach aplikacji zdarza się zwykle bardzo rzadko, więc nie cierpimy przez to aż tak bardzo.

Skoro ani code churn ani complexity nie są w pojedynkę wystarczające, to może spróbujmy użyć obu? Feathers zaproponował dwuwymiarowy wykres. Na osi X umieścił ilość zmian w plikach a na osi Y umieścił złożoność. Wykres ten nazwał “churn-vs-complexity” i pod taką nazwą można go spotkać najczęściej.

Nie wiem czy to jest pojęcie jest autorstwa Michała Bartyzela i Mariusza Sieraczkiewicza, ale to oni szczególnie często mówią o Kwadrancie Feathersa. Kwadrant Feathersa jest właśnie inną nazwą na diagram churn-vs-complexity. Poza tymi dwoma spotkałem się jeszcze z “Code Turbulence”, które wzięło się prawdopodobnie od nazwy narzędzia “Turbulence” rysującego taki wykres dla kodu napisanego w języku Ruby.

Mając taki wykres, powinniśmy podzielić wykres “na pół w pionie” i “na pół w poziomie” otrzymując cztery obszary:

  1. Kod który ma wysoką złożoność a zmiany w nim występują często (prawa, górna, część wykresu).
  2. Kod który ma niską złożoność a zmiany w nim występują często (prawa, dolna, część wykresu).
  3. Kod który ma niską złożoność a zmiany w nim występują rzadko (lewa, dolna część wykresu).
  4. Kod który ma wysoką złożość a zmiany w nim występują rzadko (lewa, górna, część wykresu).

Z naszymi refaktoryzacjami powiniśmy celować przede wszystkim w prawą, górną część kwadrantu – tam gdzie kod jest skomplikowany oraz często zmieniany.

Na sam koniec wspomnę jeszczę, że generalnie Feathers poleca dodawanie do codziennie używanych metryk, informacji z repozytorium. Umożliwi nam to spojrzenie na nasz kod pod innym kątem. I to właśnie dzięki temu innemu spojrzeniu, Kwadrant Feathersa pozwala nam w bardziej przemyślany sposób planować refaktoryzacje.

Referencje

  1. M. Feathers – Getting Empirical about Refactoring
  2. M. Sieraczkiewicz – Natural Course of Refactoring – a Refactoring Workflow
  3. Turbulence (github)
  4. M. Feathers – Working Effectively with Legacy Code

Powiązane

Cyclomatic Complexity