Bartłomiej Kołakowski

Project Manager

Jak TDD obniża koszty: case study

Oparta na testach automatycznych koncepcja TDD (Test-Driven Development) obiecuje wyższą jakość oprogramowania. Jednak czy konieczność pisania i utrzymywania testów jednostkowych według podejścia TDD nie podnosi znacząco kosztów projektów? Jak dowiedliśmy razem z ING Bankiem Śląskim, dobrze wdrożona metodyka TDD nie tylko znacząco zmniejszyła liczbę błędów, ale też obniżyła koszty o 20%. 

Weryfikacja zysków z TDD: porównanie 2 projektów

Razem z ING zbudowaliśmy aplikację ING Finansowanie Faktur. To narzędzie do szybkiego i wygodnego finansowania faktur sprzedażowych, z którego można korzystać bez konieczności opuszczania firmy.

Projekt okazał się świetną okazją, by zweryfikować korzyści płynące z TDD, a także generowane koszty. Mogliśmy porównać ING Finansowanie Faktur do innego projektu, w którym  podobny zespół pracował w podobnym środowisku – dalej zwany  „projektem kontrolnym”.

Dziś mamy już za sobą uruchomienie produkcyjne oraz pierwsze miesiące utrzymania i rozwojów ING Finansowania Faktur oraz możemy je porównać do rezultatów projektu kontrolnego. Pora na podsumowanie.

Metodyka TDD

Test-Driven Development zakłada, że kod aplikacji powinien powstać dopiero gdy istnieją testy pozwalające na weryfikację poprawności działania kodu. Takie podejście zapewnia bardzo wysokie pokrycie kodu testami jednostkowymi, a w efekcie znacznie niższą liczbę błędów.

Testy opisują funkcjonalność aplikacji i wymagania, jakie jej stawiamy. Pisząc testy jako pierwsze, mamy możliwość analizy wymagań i ich ulepszenia jeszcze przed rozpoczęciem pisania kodu. Poprawia to wartość, którą aplikacja zapewnia klientowi i użytkownikom.

W praktyce metodyka TDD polega na powtarzaniu następującego procesu:

  1. dodaj jeden test - gdy chcemy wdrożyć nową funkcjonalność lub poprawić istniejącą, dodajemy test, który ją opisuje;
  2. uruchom testy - uruchamiamy testy, by zweryfikować, że nowo dodany test nie daje pozytywnego wyniku;
  3. zmień aplikację - wdrażamy nową funkcjonalność lub poprawki w kodzie aplikacji;
  4. wykonaj refaktoryzację kodu - jeżeli uznamy, że jest to potrzebne, ulepszamy kod, cały czas zapewniając spełnianie wymagań stawianych przez wszystkie testy.

Projekt start-upowy

ING Finansowanie Faktur miało z założenia działać jak start-up: podczas prac nad MVP zakres prac ciągle się zmieniał. Z jednej strony chcieliśmy dodać jak najwięcej funkcji, z drugiej - szybko wprowadzić aplikację na rynek. Dlatego byliśmy gotowi na zmiany koncepcji, a zatem i zmiany w kodzie. Musiały być szybkie oraz odporne na występowanie błędów regresji.

Założyliśmy, że będziemy w stanie usunąć w dowolnym momencie wszystkie błędy z aplikacji w przeciągu maksymalnie 1 dnia, czyli 8 godzin pracy zespołu. Innymi słowy, w przeciągu 1 dnia roboczego:

  • oddawaliśmy klientowi aktualną wersję aplikacji po raz pierwszy do testów;
  • klient testował ją i zgłaszał poprawki;
  • naprawialiśmy błędy.

Aktualizacja wersji produkcyjnej była gotowa w ciągu 8 godzin od oddania poprzedniej wersji do testów.

Cel: jakość

Naszym celem była redukcja liczby błędów w systemie oraz skrócenie cyklu dostarczania zmian. Z pewnością osiągnęliśmy ten cel: pierwszy poważny błąd pojawił się dopiero podczas siódmego sprintu i zaskoczył klienta, który w tym projekcie nie napotkał dotychczas istotnych niedociągnięć. Oczywiście, zgłoszony błąd usunęliśmy w ciągu jednego dnia.

Kolejne wersje aplikacji, oddawane na koniec sprintu, nie zawierały znaczących błędów. W wielu wypadkach były one instalowane na produkcji bez poprawek, w ciągu kilku godzin od oddania.

Niska liczba błędów utrzymywała się niezależnie od złożoności zmian w systemie, a błędy regresji nie występowały, mimo, że rdzeń aplikacji był kilkukrotnie gruntownie przebudowany. Wynika to z bardzo wysokiego, 95% pokrycia kodu testami.

"ING Finansowanie Faktur to zupełnie nowy, internetowy serwis, za pomocą którego firmy i przedsiębiorcy mogą zamienić swoje faktury sprzedażowe na gotówkę. Startując praktycznie  od zera weszliśmy na rynek z całkowicie nową propozycją w ok. 9 miesięcy. Pracowaliśmy w metodyce Agile i bardzo dynamicznie definiowaliśmy kolejne funkcjonalności aplikacji. Jednocześnie byliśmy w stanie utrzymywać wysoką jakość potwierdzoną przez bardzo małą liczbę błędów, co pozwalało uzyskiwać stały progres bez konieczności poświęcania czasu na naprawy."

Maciej Bukowiec
ING Bank Śląski, Product Owner

Koszt wykonania zadań

Koszty obu projektów szacowali kierownik techniczny i biznesowy projektu ING Finansowanie Faktur, kierownik biznesowy projektu kontrolnego oraz architekt, który dzielił swój czas po równo na oba projekty. Szacowanie wykonaliśmy rok po rozpoczęciu prac nad projektem, już po jego uruchomieniu produkcyjnym.

Aby uzyskać szczegółowe informacje, oszacowaliśmy koszt wykonania tych samych zadań w obu projektach. Wybraliśmy kilka standardowych modułów i funkcjonalności. Wielkość zadań odpowiadała takim, które były standardowo realizowane w ramach sprintów. W każdym wypadku koszty wykonania tej samej funkcjonalności w ING Finansowanie Faktur wynosiły 80% - 125% kosztów wykonania w projekcie kontrolnym. Uśredniając przyjmijmy, że zespół pracujący w TDD w każdym sprincie dostarczał ok. 10% pracy mniej.

Brak UAT w TDD - niższe koszty

Jednak w projekcie ING Finansowanie Faktur, prowadzonym metodą TDD, nie było faz UAT. Natomiast w projekcie kontrolnym, w którym sprinty trwały również 3 tygodnie, dodatkowo co 2-3 sprinty przeprowadzano sesję UAT, która trwała średnio 10 -12 dni roboczych i dopiero po niej nowa wersja aplikacji była gotowa do uruchomienia produkcyjnego. Faza UAT nie tylko opóźniała wdrożenie aplikacji, ale też generowała dodatkowe koszty. Średnio każdy sprint był przez to droższy o 30%.  

Brak UAT był możliwy jedynie dzięki prowadzeniu projektu metodą TDD i uzyskanej w ten sposób jakości kodu. Następny sprint mógł zacząć się w kolejnym dniu roboczym po zakończeniu poprzedniego. Nie udało nam się tego osiągnąć w żadnym innym projekcie, który nie byłby prowadzony metodą TDD.

Dlaczego TDD obniża koszty?

Obniżenie kosztów nie wynikało jedynie z braku fazy UAT. Ważne były jeszcze inne czynniki:

  • brak potrzeby debugowania systemu: testy poszczególnych klas (jednostkowe) pozwalają uniknąć sytuacji, w której programista musi ręcznie, niskopoziomowo analizować system;
  • brak ręcznej weryfikacji: nie trzeba regularnie sprawdzać, czy cały napisany kod działa - testy realizują to automatycznie. To też oszczędność dla pozostałych członków zespołu - znaleziony błąd ktoś musiałby opisać i zgłosić;
  • brak ręcznego poszukiwania błędów regresji: testy napisane uprzednio, do wcześniej dewelopowanych modułów, automatycznie sprawdzają, czy nowe zmiany nie zepsuły istniejącego systemu - nie trzeba tego robić ręcznie;
  • błyskawiczne wykrycie problemu i feedback: automatyczny test pozwala szybko wychwycić błąd (np. wynikający ze sprzeczności w założeniach), którego konsekwencje mogłyby eskalować i w przyszłości wygenerować dużo wyższe koszty;
  • naprawianie błędów od razu: gdyby błąd został wykryty na długo po tym, jak powstał, wrócenie do kontekstu wymagałoby od dewelopera więcej czasu - musiałby on przypomnieć sobie założenia danego modułu po przejściu  do nowych zadań. Pracując w TDD, identyfikuje i naprawia błąd od razu, gdy jest na bieżąco z danym tematem;
  • lepsze zrozumienie działania systemu i lepsza architektura: zaczynając od pisania testów, skupiamy się na tym, CO system powinien robić, a nie JAK. Koncentracja na działaniu systemu, a nie na sposobie implementacji, otwiera inna perspektywę, co przekłada się na różne aspekty programowania: architekturę, wykrywanie przypadków brzegowych, wyszukiwanie błędów koncepcji...
  • szybkość i bezpieczeństwo wprowadzania skomplikowanych zmian: TDD gwarantuje przestrzeń, w której programista nie waha się wprowadzać skomplikowanych zmian, nie zwleka z nimi, a także poświęca na to mniej czasu. To pozwala też na większą elastyczność.

Powyższe oszczędności wypływają bezpośrednio z zastosowania metodyki TDD. Nie byłyby możliwe, gdyby wybiorczo zastosowano jedynie elementy tej koncepcji.  

Podsumowanie: koszty w TDD niższe o 20%

Podsumujmy.
Zespół ING Finansowanie Faktur wykonuje ok. 10% mniej pracy per sprint, ale nie uwzględnia dodatkowych faz UAT, które występują w projekcie kontrolnym i podnoszą koszt każdego sprintu o ok. 30%. Przy takiej samej wielkości zespołów tę samą funkcjonalność metodą TDD wykonaliśmy o 20% taniej.

Przystępując do projektu ING Finansowanie Faktur, dopuszczaliśmy możliwość wzrostu kosztów w ujęciu ogólnym z uwagi na konieczność bardzo wysokiego pokrycia kodu testami. Spodziewaliśmy się też, że brak UAT-ów obniży koszty, częściowo rekompensując koszty TDD. Jednak końcowy rezultat - obniżenie kosztów o 20% - przerósł nasze oczekiwania.

Nie zapominajmy, że brak błędów i fazy UAT pozwolił obniżyć też koszty po stronie ING. W testowanie i odbiór aplikacji było zaangażowanych mniej osób. Znaczące ograniczenie cykli wykrywania, poprawiania i ponownej weryfikacji błędów zmniejszyło liczbę prac menedżerskich. Tych oszczędności nie ujęliśmy w naszym wyliczeniu, ale stanowią kolejny argument na rzecz wartości TDD.   

Obniżkę kosztów osiągnęliśmy jednocześnie dostarczając system o bardzo wysokiej jakości oraz tysiące testów, które pozwalają w dowolnej chwili przetestować każdy aspekt działania aplikacji. Ponadto, w projekcie ING Finansowanie Faktur, dostarczyliśmy finalnie nie tylko aplikację SPA (Single Page Application), działającą w całości w przeglądarce użytkownika, ale też aplikację po stronie serwera, z wystawionym publicznie interfejsem w postaci RESTful API. Zatem, w ramach prac dostarczyliśmy nie tylko aplikację, ale też API.

Metodyka i role w projekcie

Jak udało się to osiągnąć? Ważny był wybór metodyki. Dla projektu start-upowego, gdzie potrzebna była zmienność i elastyczne dostosowywanie się do zmian koncepcji, naturalnym wyborem była metodyka Scrum. Zdecydowaliśmy się na 3-tygodniowe sprinty.

W procesie TDD ogromne znaczenie ma odpowiedni skład zespołu i podział ról: programista jest jednocześnie testerem swojego kodu, a na etapie pisania testów musi przeanalizować przypadki użycia. Dzięki temu koncentruje się na wynikach działania (a nie na tym, jak będzie wyglądać realizacja).

W projekcie ING Finansowanie Faktur każdy członek zespołu zostawał menedżerem wytworzonej przez siebie funkcjonalności i był odpowiedzialny za rezultat końcowy. Oprócz kodowania, zarządzał również całym cyklem życia swojego zadania na tablicy Kanban, aż do odebrania przez klienta. Oznacza to, że do jego zadań należało też mobilizowanie w odpowiednich momentach członków zespołu oraz osoby odpowiedzialne za tę funkcjonalność po stronie ING.

Większość członków zespołu łączyła więc role dewelopera, testera i menedżera. 85% osób w zespole posiadało kompetencje deweloperskie, a 100% kompetencje dewelopera lub architekta. Dzięki temu:

  • zoptymalizowaliśmy pracę wszystkich członków w zespole: w każdym sprincie wykorzystywaliśmy na 100% czas pracy każdej osoby;
  • mogliśmy pracować wydajnie nawet w wypadku braku części zespołu;
  • usprawniliśmy komunikację między zespołami e-point i ING.

U poszczególnych członków zespołu wzrosło zaangażowanie (przez jednoznaczne przekazanie odpowiedzialności właścicielom konkretnych zadań) oraz zwiększyła się świadomość celu i wartości całego projektu, a także wykonywanego przez daną osobę modułu.

W ten sposób zespół stał się samowystarczalnym organizmem o wysokiej skuteczności i organizacji. To zaś zmniejszyło koszty związane z procesem czy zarządzaniem.

Wnioski

Projekt ING Finansowanie Faktur pokazał, że dzięki zastosowaniu metodyki TDD można dostarczyć kod wyższej jakości, elastycznie dopasowując się do zmieniających się oczekiwań biznesowym w projekcie o start-upowym charakterze i jednocześnie obniżyć jego koszty o 20%.

Stworzyliśmy rozwiązanie innowacyjne: produkt został zaprezentowany na Finovate Fall w Nowym Jorku 24 września 2018.