Convention Over Configuration

Convention Over Configuration (COC) moim zdaniem jest nie tylko jedną z najważniejszych, ale też najbardziej niedocenianych dobrych praktyk. Można ją traktować jako odmiana najważniejszej zasady – KISS. KISS mówi o prostocie w najbardziej ogólnym sensie, natomiast COC dotyczy bardzo specyficznego tematu – konfiguracji narzędzi, które używamy.

COC jako odpowiedź na chaos konfiguracyjny

Kiedy przyjrzymy się, gdzie kryje się najwięcej grzechów dotyczących zbyt dużego skomplikowania sytemu, widzimy dwie główne sfery:

  • przerośnięty, źle napisany, zaśmiecony kod
  • dżunglę plików, adnotacji i kodu konfigurującego używane narzędzia

Poprzez konfigurację rozumiemy każdą modyfikację domyślnego zachowania narzędzia, dostosowującą go do naszych potrzeb. Każda taka modyfikacja wprowadza dodatkowe skomplikowanie i co gorsza – niestandardowe zachowania. Dlatego też sformułowane zostało COC – paradygmat, który dąży do minimalizacji konfiguracji poprzez preferowanie ustawień domyślnych. Dzięki temu uzyskujemy spadek skomplikowania i bardziej intuicyjne zachowanie narzędzi. Obie te cechy są oczywiście bardzo pożądane.

No, ale jak mamy unikać konfiguracji? Przecież tego się nie da zrobić!

Jest to częściowo prawda. Niektóre konfigurację są wymagane (np. adres bazy danych) ale występują też takie, które są kompletnie oczywiste, nadmiarowe, niekonieczne, czy nadmiernie optymalizujące. COC nalega by przyglądać się każdej wartości konfiguracyjnej i promuje ich pominięcie/minimalizację, chyba że ich użycie jest absolutnie niezbędne. Pomijając konfigurację polegamy na zachowaniu domyślnym. Aby COC miało sens, potrzebna jest nie tylko świadomość użytkowników narzędzia, ale też racjonalne podejście twórców tychże narzędzi.

Najpiękniejszy przykład rozwoju COC w historii technologi JAVA

By docenić, jak duży wpływ COC miało na rozwój oprogramowania, potrzebna jest powtórka z historii. Jeśli ktoś miał do czynienia z początkiem JEE, to dokładnie wie o czym mówię. Niestety pierwsze wersje tego frameworka miały potworne rozwiązania konfiguracyjne. Była wymagana masa plików na samym starcie, a każdy obiekt EJB musiał mieć zestaw interfejsów i bardzo sztywne wymagania. Kolejne wersje znosiły po kolei wszystkie te elementy, głównie z tego powodu, że pojawił się potężny konkurent – Spring.

Spring wprowadził powiew świeżości, pakując konfigurację do intuicyjnego pliku .xml, który był znacznie prostszy, niż jego odpowiednik w JEE. Kolejne wersje agresywnie redukowały wymagania, aż doszło do maksymalnego zastosowania COC – powstał Spring Boot.

Spring Boot jako szczyt myśli COC

Wracając do dzisiejszych czasów, Spring Boot może być wystartowany bez ŻADNEJ konfiguracji. Przy domyślnych ustawieniach Spring Boot uruchamia najpopularniejszy serwer HTTP, konfiguruje testową bazę typu in-memory i automatycznie szuka serwisów oraz innych obiektów. Poziom pominięcia konfiguracji w tym frameworku jest nieziemski, co moim zdaniem jest najważniejszym powodem jego sukcesu. Po prostu uruchamiasz standardową paczkę, po czym wymieniasz tylko, to co potrzebujesz. Wynikiem tego procesu jest ekstremalnie prosta konfiguracja.

Przerost konfiguracji jest zły – pytanie dlaczego to się dzieje?

Zanim zacznę wymieniać konkretne powody, muszę wspomnieć o podstawowej różnicy pomiędzy kodem i konfiguracją. Procesy, które czuwają nad jakością kodu takie jak code review, zwykle są mniej skuteczne dla konfiguracji. Znajomość konkretnych narzędzi jest mniej powszechna niż znajomość języka programowania. Nie możliwe jest by członkowie zespołu znali tak samo dobrze zawiłości ustawień wszystkich narzędzi. Powoduje to bardzo często ulgowe traktowanie konfiguracji przy code review, nawet jeśli w teorii powinniśmy dbać o nią tak samo jak o kod. Do innych powodów możemy zaliczyć:

  • idealistyczne dostosowywanie każdego szczegółu narzędzia, nawet jeśli nie jest to konieczne,
  • copypasting zestawów wartości konfiguracyjnych, bez ich zrozumienia,
  • brak przeglądu konfiguracji przy aktualizacji narzędzia lub zmianę jego użycia w systemie,
  • przedwczesna optymalizacja bez zmierzenia, czy rzeczywiście optymalizacje są potrzebne,
  • brak użycia prawidłowej procedury wprowadzania nowego narzędzia do systemu, o czym piszę poniżej.

W jaki sposób wprowadzać nowe narzędzia w duchu COC?

Załóżmy, że dostajemy nowe wymaganie, które musimy zaimplementować w systemie. Proces powinien wyglądać mniej więcej tak:

Etap poszukiwania czegoś gotowego

Pierwszym etapem powinno być zawsze poszukiwanie gotowego rozwiązania, które rozwiąże nasz problem. Jeśli tego nie zrobimy to popełnimy standardowe odkrywanie koła na nowo. Zwykle społeczność programistów pisała i optymalizowała wykonanie danej czynności.

Kiedy już mamy na radarze daną bibliotekę/narzędzie/framework, to przede wszystkim zbadajmy jego powszechność, czy inni stosują go z powodzeniem i czy mamy odpowiednią ilość dokumentacji, by go skutecznie używać. Popatrzmy też na możliwych konkurentów, może któryś z nich będzie pasował lepiej?

Etap Proof of concept

Po ściągnięciu narzędzia staramy się użyć go stosując minimum kodu i konfiguracji. Po czym upewniamy się, że w rzeczywistości potrafi spełnić wszystkie nasze wymagania na kilku reprezentacyjnych przykładach.

Etap rozwoju

Po kolei implementujemy wszystkie wymagane funkcjonalności i co najważniejsze dla COC – NIGDY nie dodajemy konfiguracji jeśli nie jest to konieczne. Po tym etapie powinniśmy skończyć z gotową funkcjonalnością i minimalną konfiguracją.

Dalsze utrzymanie

Jeśli nie popełniliśmy żadnego błędu na poprzednich etapach powinniśmy pracować na konfiguracji jak najbliższej domyślnej. Podczas rozwoju i zmian powinniśmy dbać by konfiguracja nie rosła bez wyraźnego powodu. Powinniśmy też przeglądać ją jeśli dojdzie do aktualizacji narzędzia.

Słowo na koniec

Convention over configuration jest jedyną z tych mało intuicyjnych zasad, które mają ogromne znaczenie dla utrzymywalności systemów IT. Sukces frameworków opartych na COC jest niezaprzeczalny. Dzięki nim przemysł IT osiągnął nowe poziomy prostoty i prędkości rozwoju nowych aplikacji.

Leave A Comment