The art of programming is the art of organizing complexity;
Edsger W. Dijkstra
mastering multitude & avoiding its chaos as effectively as possible.
Modularyzacja w projektach stanowi jedną z kluczowych technik, które pozwalają na zachowanie czytelności, skalowalności oraz łatwości w utrzymaniu kodu. Wraz z rozwojem projektów, zwłaszcza tych o większym zakresie, staje się ona ważnym elementem zapewniającym porządek i przejrzystość w strukturze kodu. Jednakże, wraz z rozwojem projektu, gdy wprowadzane są kolejne zmiany, a programiści przychodzą i odchodzą, łatwo zapomnieć o początkowych założeniach architektury. Z pomocą przychodzą testy automatyczne. Tylko co testować i jak je zrobić? Bez solidnych testów nie jesteśmy w stanie upewnić się, że nasza struktura modułowa spełnia założenia, które sobie postawiliśmy.
Co testować
Pewne aspekty modułów, takie jak zależności międzymodułowe oraz cykle w języku java możemy testować za pomoc archunit. Biblioteka ta nie rozwiązuje jednak wszystkich problemów. Przyjrzyjmy się dwóm innym szczególnie istotnym przypadkom:
1. Równomierna dystrybucja kodu
Gdy mamy wiele modułów, ale jeden z nich zawiera znaczną większość kodu, modularyzacja traci swoje znaczenie. Przykładowo, jeśli mamy 15 modułów, a jeden z nich zawiera aż 80% kodu, to nie możemy mówić o rzeczywistej modularyzacji, ponieważ niemal całość kodu jest zawarta w jednym module. Jest to sytuacja, którą należy wyeliminować poprzez automatyczne testowanie rozmiaru modułów.
2. Niektóre moduły powinny być szczególne małe
Ważne jest również zapewnienie, że poszczególne moduły są niewielkie i stabilne pod kątem wprowadzanych zmian (jak często wprowadzamy zmiany). Na przykład, moduł typu „commons” powinien być niewielki i zawierać ogólną funkcjonalność, która jest często używana w różnych częściach projektu. Jeśli ten moduł staje się zbyt duży, może to prowadzić do problemów z zarządzaniem kodem i jego modyfikacjami. Oczywiście jest to przybliżenie, ponieważ ważniejsze jest minimalizowanie interfejsu modułu i zachowanie jego stabilności pod kątem zmian, co nie musi wiązać się z minimalizacją rozmiaru modułu.
Jak testować
Ponieważ opisane powyżej zagadnienia są dla mnie bardzo istotne postanowiłem je weryfikować w testach. Implementacja może być bardzo prosta i polegać na zliczaniu linii kodu w pakietach. Ku mojemu zaskoczeniu nie znalazłem gotowego narzędzia, które w szybki sposób pozwoliłoby sprawdzać rozmiar modułów.
Przygotowałem więc bibliotekę java, która pozwala na testowanie rozmiaru modułów – module-size-calculator. Biblioteka ta umożliwia analizę rozmiaru modułów w projekcie na podstawie liczby linii kodu (LOC).
Wystarczy zdefiniować zależność
<dependency>
<groupId>pl.tfij</groupId>
<artifactId>module-size-calculator</artifactId>
<version>1.0.0</version>
</dependency>
Od teraz możemy pisać testy w stylu
ProjectSummary projectSummary = ModuleSizeCalculator.project("src/main/java")
.withModule("com.example.module1")
.withModule("com.example.module2")
.analyze()
.verifyEachModuleRelativeSizeIsSmallerThan(0.3)
.verifyModuleRelativeSizeIsSmallerThan("com.example.commons", 0.1);
Biblioteka pozwala na różnego rodzaju asercje rozmiaru modułów, a jeśli coś nie zostało przewidziane można, na podstawie wygenerowanego raportu napisać klasyczną asercję w JUnit. Biblioteka pozwala także wygenerować raport w postacie wykresu Mermaid, który może następnie być dołączony np. do dokumentacji.
Po więcej szczegółów i przykładów zapraszam na github.
Przekaz na dziś
Modularyzacja jest kluczowym aspektem projektu. Testuj ją aby mieć pewność, że nie zanika z czasem.
Bardzo ciekawe to co piszesz. Często można trafić na dyskusje o tym jaki rozmiar modułu jest optymalny, ale zazwyczaj są to opinie mocno subiektywne. Ja trafiłem tylko na jedne wyniki badań w tym temacie, które opisał Eric Raymond w swojej książce „The Art of Unix Programming” (wersję online rozdziału „Modularity: Encapsulation and Optimal Module Size” można znaleźć pod adresem http://www.catb.org/esr/writings/taoup/html/ch04s01.html). Niestety są to dość stare badania, z końca poprzedniego wieku. Trafiłeś może na coś nowszego?
Dziękuję za Twój komentarz! Jestem wdzięczny, że dzielisz się swoimi spostrzeżeniami.
Zgadzam się z Tobą, że dyskusje na temat optymalnego rozmiaru modułu często opierają się na subiektywnych opiniach. Aby prowadzić tę dyskusję, istotne jest ustalenie spójnej definicji modułu, co nie jest łatwe.
W moim poście używam bardzo ogólnej definicji, gdzie moduł to po prostu katalog zawierający pliki. Ta elastyczność sprawia, że biblioteka do testowania może być bardziej wszechstronna i użyteczna w różnych kontekstach. Propozycja testów opartych na względnym rozmiarze wydaje mi się sensownym podejściem, które może być stosowane zarówno w małych, jak i dużych projektach. Uniknąłem też problemu definiowania jaki rozmiar jest właściwy.
W ogólności uważam, że modularyzacja powinna być wielopoziomowa, co jeszcze bardziej utrudnia definicję modułu. Myślę, że to świetny materiał na kolejny post, który chętnie napiszę, gdy znajdę tylko chwilę.