Tworzenie produktu, prostej aplikacji czy też dużego systemu zawsze zaczyna się czynności które w efekcie mają zaowocować powstaniem czegoś na kształt projektu. Czy to będzie kompletny, szczegółowy projekt „od A do Z” czy tylko zbiór luźnych „kartek” – zależy to od wielu czynników. Dzisiaj – w czasach wszechobecnej zwinności – wiemy już, że o ile warto znać i rozumieć najważniejsze wymagania, to nie zawsze opłaca się już na starcie tworzyć planu z zaplanowaniem najdrobniejszych szczegółów i działań. Wiemy także, że iteracyjne metodyki w wielu przypadkach przynoszą bardzo dobre efekty. Zwinność i skuteczność iteracyjności polega na działaniu w zgodzie z tym jak my „umiemy” rozumieć świat. Nasz aparat poznawczy – oparty o mózg, złożoną sieć neuronową – powoduje, że poznajemy, uczymy się etapami. Najpierw poznajemy podstawy, potem kolejne bardziej zaawansowane zagadnienia i tak dochodząc do etapu specjalisty (uznaje się, że poziom specjalisty osiągamy po 10tyś godzin pracy nad czymś) a potem do poziomu eksperta, gdzie pojawia się pełna świadomość i mgliste – aczkolwiek ogromnie wartościowe – zjawisko intuicji.
Mając na uwadze powyższą drogę rozwoju, warto zastanowić się nad paradoksem, który towarzyszył nam przy realizacji projektów w IT przez ostatnie dekady. Występuje on w kontekście prac zespołów IT które mają realizować projekty których wiedzy dziedzinowej w znacznym stopniu nie znają, a w których dodatkowo zmienność wymagań ma istotną wartość w porównaniu do czasu realizacji projektu. Przykładowo, mamy zaprojektować i wykonać system bankowości mobilnej. Dla zespołu jest to pierwszy projekt z tej dziedziny, zakładany czas trwania projektu to 14 miesięcy. Notujemy rocznie kilkadziesiąt zmian przepisów prawnych mających potencjalnie wpływ na nasz projekt. Czy ktoś się pokusi o kaskadowy sposób organizacji takiego przedsięwzięcia? Czy ktoś przygotuje szczegółowy projekt i plan działania na 14 miesięcy? Czy ktoś zadecyduje aby zespołem złożonym z nie specjalistów i nie ekspertów przygotować projekt systemu? Skoro na początku mamy mało wiedzy, dopiero zaczynamy ją zdobywać (nie jesteśmy ekspertami), skoro wiemy, że wymagania będą się zmieniać w czasie, musimy inaczej podejść do procesu projektowania, planowania i realizacji.
Oczywiście możemy być na drugim biegunie, mamy zespół ekspertów i niską zmienność wymagań, tak też może być. W zależności od wielu czynników powinniśmy zawsze dobrać odpowiednią strategię, odpowiednie podejście do rozwiązania problemu. I tak:
Działający prototyp – podejście stosowane tam gdzie pomysły i produkty są nowe, nowatorskie, innowacyjne. Pomaga najpierw zbadać sensowność samego pomysłu, bo jak wiadomo „lepiej źle zrealizować dobry pomysł niż dobrze pomysł zły”. No i tu dochodzimy do pytania, skąd mamy pewność że pomysł jest dobry? Najlepiej to sprawdzić. Przygotowujemy coś na kształt prototypu, który realizuje koncepcje MVP (Minimum Viable Product), działający produkt z minimum funkcjonalności, które pozwolą zweryfikować jego potencjał. Podejście to stosowane jest dzisiaj w ogromnej ilości organizacji, uwielbiają je startup’y, dla których jest to główna metoda i sposób działania. Jeżeli pomysł okazał się dobry? Mamy go zrealizowanego na szybko, „jakoś tam”, prototypowo, wydawało by się, że sytuacja nie jest najlepsza. Jest wręcz przeciwnie. Mamy już zespół na całkiem innym etapie wiedzy i doświadczania, mamy już łączność z rynkiem, mamy na bieżąco dopływ świeżych, sensownych wymagań, inwestorzy pukają do drzwi. Mamy wszystko aby zrealizować teraz nasz pomysł jeszcze raz, tak, jeszcze raz, tym razem „dobrze”.
Zwinne iteracje – to podejście stosujemy w sytuacji gdzie prototyp to za mało, w tych segmentach rynkowych gdzie spotykamy się z dużą dojrzałością, wysokimi oczekiwaniami funkcjonalnymi i jakościowymi. W takim przypadku mamy zazwyczaj szansę na lepiej zdefiniowane wymagania, na dostęp do specjalistów dziedzinowych, czy wręcz dostęp do wiedzy w postaci materiałów czy szkoleń. W takim przypadku optymalne będzie działanie zwinne, z podziałem pracy na iteracje, z pozyskaniem wiedzy i kompetencji na wstępie, z aktywnym zaangażowaniem odbiorcy, z założeniem realizowania najbardziej wartościowych funkcjonalności przy zachowaniu założonej jakości. Iteracyjny charakter procesu broni nas przed zmianami wymagań (nie psują one nam procesu) i zapewnia, że odbiorca otrzymuje coś co jest dla niego mocno wartościowe. Tu zauważmy, że wcale nie koniecznie to co myślał, że chce otrzymać na początku. W trakcie projektu – prowadzonym przy jego aktywnym udziale – może okazać się że wiele oczekiwań straciło sens, uległo zmianie, powstały nowe, wiadomo, czas płynie otoczenie – także biznesowe – się zmienia. W efekcie mamy duże szanse na osiągnięcie sukcesu, choć na początku nie istniał skończony szczegółowy projekt całości.
Klasyczna kaskada – stosowana w przypadku gdy wymagania są wyraźnie określone, czy wręcz jednoznacznie zdefiniowane, ryzyko ich zmienności jest małe, posiadamy sile kompetencje, do tego jakość powstałego rozwiązania jest bardzo ważna. np. zależy od niego życie ludzkie (oprogramowanie samolotu czy czołgu). Wtedy tak „jak należy”, przygotowujemy pełny projekt „od A do Z” i realizujemy go od początku do końca w zgodzie z niezmiennymi założeniami. Zmiana założeń w tym podejściu także jest możliwa ale zawsze wymaga ustaleń, uzgodnień, analizy wpływu na inne wymagania, przeprojektowania podsystemów zależnych czy zmiany testów. W projektach z takim podejściem zazwyczaj stosujemy silne techniki QA (Quality Assurance) i QC (Quality Control).
Powyższe trzy podejścia nie wyczerpują tematu, pokazując raczej, że zawsze warto dostosować metodę do problemu. Czy da się jednak zdefiniować i zastosować takie podejście które niezależnie od specyfiki problemu poprawiło by osiągane rezultaty. Wydaje się, że tak, zobaczmy:
Dekompozycja
Dekompozycja to uznana metoda z stajni „dziel i zwyciężaj”. Polega na podziale całego problemu na mniejsze części składowe, i znalezienie dla każdej z nich optymalnego podejścia. Oczywiście co podzielone musi być potem złączone, determinuje to konieczność ustalenia – już w momencie podziału – interfejsów łączących elementy składowe i nieznacznie komplikuje ogólną architekturę. Jednak czy to jest wada? W pewnym sensie tak, trzeba to zrobić i oczywiście może się zdarzyć, że zrobimy to źle. Jednak powyższa „konieczność ustalenia”, jeżeli się ją zrealizuje przynajmniej poprawnie, może dać nam ogromne korzyści:
- komponenty i interfejsy – zdefiniowanie komponentów i interfejsów, wymusza na twórcach wykonanie intelektualnego wysiłku, pracy, która może okazać się najbardziej wartościową w całym przedsięwzięciu. Identyfikacja komponentów, nazwanie ich, określenie kompetencji, sposobów komunikacji i co bardzo ważne ich interfejsów zewnętrznych, to wszystko są działania wnoszące ogromną wartość, zwiększające szanse na powodzenie,
- dobór odpowiedniej techniki – dzieląc problem na mniejsze możemy łatwiej dobrać odpowiednią technikę realizacji do specyfiki części składowej, tam gdzie trzeba wykonamy prototyp, a gdzie indziej zastosujemy kaskadę, przy takim podejściu jesteśmy jeszcze bardziej zwinni,
- mniejsze ryzyko – mniejsze zakres daje może nie mniejsze ryzyko popełnienia błędu, ale mniejsze konsekwencje w przypadku jego popełnienia, przy dobrym podziale błędy nie wyjdą poza zakres wydzielonego komponentu,
- szanse na poprawę – skoro pomylimy się w jednym z komponentów to łatwiej możemy go wymienić na lepszy, a przy dobrze zdefiniowanym interfejsie wymiana ta będzie możliwa i stosunkowo łatwa
- doświadczenie – skoro nie „fiksujemy” się na jednej metodyce, z czasem zdobywany doświadczenie w wielu, a także wiedzę którą i kiedy stosować
Jak rozumieć powyższe zalecenia w kontekście konkretnych działań projektowych czy developerskich? Z punktu widzenia zarządzającego portfelem projektów, będzie to oznaczać wskazanie aby dobrać odpowiednia technikę do realizowanego projektu, lub podzielić projekt na mniejsze projekty jeżeli ich złożona specyfika powoduje, że są do tego wskazania. Z punktu widzenia kierownika projektu czy właściciela produktu, będzie to oznaczać możliwość realizacji wydzielonych podsystemów realizowanego produktu innymi metodami organizacyjnymi czy w oparciu o inne stosy technologiczne. Z punktu widzenia architekta systemowego będzie to oznaczać możliwość wyboru architektury makro czy mikro serwisowej zamiast monolitycznej i wskazania do realizacji poszczególnych usług w inny dopasowany do potrzeb sposób. Z punktu widzenia architekta kodu czy developera może to oznaczać świadome i celowe wydzielanie jednostek kodu (moduły, paczki JAR) z zastosowaniem w każdej z nich optymalnego podejścia do architektury, testowania czy dostarczania wersji.
Podsumowując, zwinne, iteracyjne podejście nie jest jedyną techniką działania jaką musimy przyjąć zawsze i w każdej sytuacji, wyższą zwinność osiągniemy dzieląc nasze problemy na mniejsze, zamknięte konteksty i dobierając (zwinnie) najlepsze rozwiązania dla specyficznych wymagań. Warto to zawsze rozważyć, mając na uwadze koszty związane z podziałem, oraz aktualne i przyszłe korzyści.
Krzysztof Olszewski
Dyrektor Technologii i Architektury Oprogramowania
Krzysztof Olszewski
Dyrektor Technologii i Architektury Oprogramowania