Wytwarzanie oprogramowania jest nieustannym zmaganiem się ze złożonością: zmienia się technologia, potykamy się o dług techniczny pozostawiony we wcześniej wytworzonym kodzie, ludzie mają gorszy lub lepszy dzień, różne umiejętności, zdarza im się niespodziewanie odejść z pracy. O ile z tym można sobie jeszcze jakoś radzić, o tyle nie sposób uniknąć ani przewidzieć ciągłej ewolucji potrzeb, a co za tym idzie zmiany wymagań opisujących funkcjonalności i cechy oprogramowania. Właściwie jedyną rzeczą pewną przy tworzeniu oprogramowania jest to, że wymagania będą się nieustannie zmieniać.

Konfigurowalność na ratunek

Interesariusze mają świadomość tej zmienności potrzeb i oczekuje, że oprogramowanie, jakie otrzymuje, będzie sobie z nią radzić. Sytuacja, w której modyfikacja sposobu działania tej czy innej funkcjonalności wymaga wprowadzenia dużych zmian w kodzie, a co za tym idzie dużo czasu, jest niepożądana. W tradycyjnych metodach prowadzenia projektów konieczność uwzględnienia takich zmian powodowała spory problem i – najczęściej – opóźnienia w realizacji wcześniej poczynionych planów. Metody zwinne (na przykład Scrum) pozwalają dokonywać takich zmian co iterację, ułatwiając dopasowywanie produktu do bieżących potrzeb.

Konieczności radzenia sobie ze zmiennością wymagań od lat towarzyszy pokusa, by tworzyć konfigurowalne rozwiązania. Koncepcja jest prosta: zamiast ciągle zmieniać kod, możemy regulować jego działanie poprzez różne parametry, które tym działaniem sterują. Wymaga to być może bardziej rozbudowanych rozwiązań, ale gdy już zostaną one wytworzone, dalszy development wydaje się niepotrzebny lub powinien być ograniczony. Są miejsca, gdzie interesariusze wprost domagają się wbudowania takich mechanizmów adaptacyjnych w oprogramowanie, gdzie indziej Zespoły z własnej inicjatywy tworzą konfigurowalne rozwiązania, aby poradzić sobie z koniecznością dokonywania szybkich zmian.

Nie twierdzę, że takie podejście nigdy nie ma sensu – o ile parametrów konfiguracyjnych jest kilka, może kilkanaście, i o ile da się realnie sprawdzić zachowanie oprogramowania przy różnych sposobach jego skonfigurowania, to może być dobry pomysł. Realnie oznacza to stosunkowo małą aplikację lub prosty system, w którym jakieś funkcjonalności lub cechy można modyfikować bez naruszenia, lub zmienienia podstawowych zasad działania. Co zaskakujące, prawie nigdy twórcy takich rozwiązań nie wpadają jednak na pomysł uczynienia ich „konfigurowalnymi”, bo dochodzą do wniosku, że szybciej dokonywać po prostu zmian w kodzie…

W jaki sposób zbudować centrum sterowania wszechświatem

Niestety, dążenie do konfigurowalności jest powodem tworzenia rozwiązań ekstremalnie skomplikowanych wtedy, kiedy nie ma to za grosz sensu. Przede wszystkim w firmach tworzących dla siebie lub dla klientów duże i złożone systemy informatyczne. Poprzez dodanie do nich całej masy (nierzadko setek, czasem tysięcy) parametrów sterujących działaniem oprogramowania, złożoność rośnie wykładniczo. Wytworzenie takich rozwiązań staje się ekstremalnie drogie i czasochłonne, ponieważ każdy dodany przełącznik lub parametr wymaga sprawdzenia, w jaki sposób wchodzi w interakcję z tymi już istniejącymi.

Jeśli dodać do tego różnorakie wariacje danych, jakie systemy informatyczne muszą przetwarzać, konieczność radzenia sobie z niespójnościami tych danych, rozłożone w czasie procesy ich przetwarzania, w żadnym akceptowalnym czasie nie da się takich rozwiązań ani zbudować, ani tym bardziej zweryfikować poprawności ich działania. Zaczyna się wtedy powolne ograniczenie zakresu testów do „tego, co najważniejsze” – czego skutkiem jest powolna kumulacja długu technicznego. Ba, dochodzi się do etapu, w którym nie do końca już wiadomo, które przełączniki są używane, a które nigdy nie zmieniają wartości. Widziałem też takie systemy, gdzie nie pamiętano, co dokładnie konfigurował ten czy ów parametr.

Duże firmy mają tendencję do budowania centrów sterowania wszechświatem: systemów (pozornie) odpornych na zmienność potrzeb biznesowych, bo (znów pozornie) gotowych na dokonanie zmian działania w sposób (raz jeszcze pozornie) bezpieczny i szybki.

Warto podkreślić, że metody zwinne nie wyrugowały tego problemu, a czasami mam wrażenie, że go wręcz nasiliły. Duże firmy „transformując się do Agile”, bardzo często oczekują, że dzięki temu wszystko będzie „bigger, better, faster, cheaper” (to powiedzonko jednej ze znanych mi osób), czyli że interesariusze dostaną rozwiązania taniej i szybciej. Konfigurowalność dla wielu Zespołów jest jedyną odpowiedzią na wywieraną na nich presję, by dostarczały rozwiązania szybciej i w większej ilości. Jakość i realna wartość spada na drugi plan.

Agile to zdolność do adaptacji

Czy owa konfigurowalność rzeczywiście umożliwia zwinne działanie? Mam wątpliwości. Dużym wysiłkiem wbudowujemy w rozwiązanie mechanizmy, które są nam zbędne dziś, ale „na pewno” przydadzą się w przyszłości (skąd niby możemy to wiedzieć?). Te wszystkie parametry są niczym oblepianie plastrami kikuta urwanej nogi w nadziei, że ona jakimś cudem odrośnie. Dlaczego? Ponieważ zamiast ułatwiać nam dokonywanie zmian w oprogramowaniu w przyszłości, od razu (już teraz) powiększają jego złożoność, a nierzadko i wagę liczoną w tysiącach linii kodu, efektywnie utrudniając modyfikacje.

Ktoś zakrzyknie: zaraz, chwila, moment, przecież właśnie o to chodzi, by nie musieć kodu zmieniać!

Warto zadać sobie pytanie, czy osiągnięcie takiego stanu jest w ogóle możliwe. Odpowiedź, w sumie oczywista, jest taka: nie. Aby mieć parametry gotowe do zmiany każdego możliwego aspektu działania oprogramowania, musielibyśmy ich mieć tysiące (lub miliony dla dużych systemów). Co więcej, aby określić, co i w jakim zakresie ma się dać skonfigurować, konieczne byłoby pełne przeanalizowanie nie tylko bieżących potrzeb (jak w starym, włochatym Waterfallu), ale też wszystkich przyszłych potrzeb (sic!). Jeśli tego nie zrobimy, ta nasza pełna konfigurowalność jest w rzeczywistości ułudą i nieuniknionym stanie się zmienianie kodu, które będzie… piekielnie trudnie i powolne ze względu na złożoność rozwiązania najeżonego mechanizmami konfiguracyjnymi.

Często na szkoleniach mówię, że prawdziwa zwinność wymaga budowania takich rozwiązań, które da się łatwo modyfikować. Zamiast przygotowywać się na nieuniknione przyszłe zmiany i próbować przewidywać ich charakter, twórzmy oprogramowanie tak lekkie i proste w modyfikacjach, jak to możliwe. Pozbywajmy się wszystkiego, co nie jest niezbędne, upraszczajmy sposób funkcjonowania produktu. Im mniej kodu i funkcjonalności, tym łatwiej dokonywać zmian i w pełni przetestować wpływ tego, co się zmieniło, na pozostałą funkcjonalność. Dużo też łatwiej określić, w jaki sposób rozwiązanie działa dziś, nie wymaga to konieczności analizowania ogromnego zestawu parametrów konfiguracyjnych…

Co na to interesariusze?

W szaleństwie konfigurowalności bardzo często wychodzi brak profesjonalnego podejścia ze strony Developerów, którzy zamiast uświadomić interesariuszy o konsekwencjach przesadnej parametryzacji, godzą się na budowę wspomnianego wcześniej centrum sterowania wszechświatem.

Natomiast sami interesariusze często też nie są bez winy. Bez żadnej dającej się ekonomicznie uzasadnić potrzeby nadmiernie komplikują funkcjonalności, zamiast dążyć do ich maksymalnego ich uproszczenia. Wynika to albo z niewiedzy, jakie są realne koszty wytworzenia i utrzymania w działaniu oprogramowania, które na to pozwoli, albo – co może zapowiadać ekonomiczną katastrofę – z niezrozumienia, co warto w produkcie mieć, a czego można się pozbyć.

Osoby biorące na siebie odpowiedzialność Agile coacha, Scrum Mastera czy Product Ownera, są niejako z definicji odpowiedzialne za edukowanie interesariuszy w tym obszarze. Koniecznym jest wyjaśnienie, że zdolność do szybkiej adaptacji na potrzeby rynku oznacza konieczność ograniczania wielkości rozwiązań i stopnia ich złożoności tak, by móc dokonywać szybkich zmian. Konfigurowalność nie jest rzeczywistym mechanizmem adaptacyjnym, bo powstaje jako promesa braku konieczności przyszłych adaptacji. Fałszywa promesa, ponieważ w rzeczywistości nie eliminuje konieczności dokonywania dalszych zmian, tylko je utrudnia, generując niepotrzebne koszty.