Metodologia Depth-First Development
Poznaj metodologię zakładającą podejście “głębiej, nie szerzej” i zobacz, jak może Ci się przydać w codziennej pracy.
Istnieje szereg metodologii, których można użyć podczas rozwoju oprogramowania, a wiele z nich ma w nazwie “first” i “driven”. Tak więc mamy na przykład test-driven development, mobile-first development, API-first development oraz YOLO-driven development.
Każda metodologia określa jakiś główny priorytet - coś co kształtuje wszystkie inne priorytety w pewnym momencie rozwoju.
Test-driven development może być aplikowany na wszystkich poziomach procesu (jeśli chodzi o kodowanie), podczas gdy API-first development jest metodologią makro, opisującą kolejność, w której zespół rozwijający oprogramowanie powinien zająć się zadaniami. Każda z tych metodologii ma jednak swój własny, specyficzny cel.
Metodologie mikro = jak pisać oprogramowanie
Metodologie makro = jak wyznaczyć kolejność zadań przy pisaniu oprogramowania
Chciałbym omówić pewną metodologię makro. Nazwę ją depth-first development i przedstawię jako coś, co spowalnia proces na początku, ale potem znacznie go przyśpiesza.
Brzmi jak bardzo konkretny przykład użycia, prawda? Być może, ale robienie czegoś po raz pierwszy to bardzo powszechny scenariusz, zwłaszcza w startupach rozwijających oprogramowanie podczas pierwszych lat działalności.
Pomysł, który zaprezentuję nie jest nowy. Istnieje szansa, że Twój zespół już z niego korzysta! Nazwanie go może go ożywić i pozwoli teamom na korzystanie z niego w codziennej pracy.
Przykład budowania domów
Przyjmijmy scenariusz, że wszyscy budowniczowie właśnie strajkują, a trzeba wybudować dużo domów. Zakładam, że nikt z Was nigdy domu własnoręcznie nie wybudował. Zadanie to przypada więc niestety Tobie, a pewnie się na tym za bardzo nie znasz (chyba, że tak, to wtedy kudos).
Zamiast wylewać fundamenty, stawiać ściany i pokryć to wszystko dachem, skorzystaj z metody depth-first, która prezentuje się tutaj następująco:
- Wylej kawałek fundamentu
- Postaw jedną ścianę i zmodyfikuj fundament, jeśli to konieczne
- Postaw następną ścianę w taki sam sposób
- Oceń proces stawiania ścian i zanotuj, co udało Ci się w nim poprawić
- Postaw resztę ścian
- Pokryj to wszystko dachem
- Wylej fundament pod następny dom
- Oceń proces wylewania fundamentu i zanotuj, co udało Ci się poprawić
- Postaw ściany w drugim domu
- Pokryj drugi dom dachem
- Oceń proces pokrywania dachem i zanotuj, co udało Ci się poprawić
- Wybuduj następny dom w taki sam sposób
- Oceń cały proces budowy trzeciego domu i zanotuj, co udało się poprawić
- Wybuduj wszystkie następne domy w taki sposób
Jest w tym kilka kluczowych punktów:
- Nie spędzaj za dużo czasu na jednym etapie- o wiele więcej nauczysz się o jakimkolwiek pierwszym kroku, jeśli spojrzysz na niego z perspektywy drugiego kroku. To o wiele lepsze niż powtarzanie pierwszego kroku, aż dobrze się go nauczysz i przechodzenie do drugiego dopiero kiedy już coś pojmiesz. I potem okazuje się, że pierwszy krok został wykonany źle.
- Bądź bardzo krytyczny, robiąc drugą iterację. Jak robisz coś po raz pierwszy, zawsze wkraczasz na nieznane terytorium. Kiedy już coś powtarzasz, to masz więcej danych, których możesz użyć, żeby nakreślić wymagania dla przyszłych powtórzeń.
- Pierwsze i drugie powtórzenie zazwyczaj idzie wolno, ale wszystko powyżej trzeciego już mocno przyspiesza. Zwolnij trochę przy dwóch pierwszych powtórzeniach i zbierz tego plony przy następnych.
Oto podejście generalizujące:
- Zbuduj pierwszą część komponentu
- Zbuduj jego zależności
- Oceń interakcje między nimi i wprowadź poprawki tam, gdzie trzeba
- Stwórz drugą część komponentu w taki sam sposób, w jaki stworzyłeś pierwszy
- Oceń doświadczenie tworzenia komponentu w pierwszym przypadku i wprowadź poprawki tam gdzie trzeba
- Stwórz wszystkie części danego komponentu w taki sposób.
Pierwsze, drugie i dalsze iteracje z punktu widzenia programisty
Jeśli zajmujecie się czymś szerokim używając schematu, który przyjęliście po raz pierwszy (na przykład, pisząc drugi punkt końcowy, albo drugi komponent), oceń swoje doświadczenie z programowania:
- Czy było ciężko?
- Czy kod jest niechlujny albo zbyt rozwleczony?
- Czy musiałem coś powtórzyć z pierwszej iteracji?
- Jeśli ten schemat miałby być powtarzany w nieskończoność, czy byłbym z niego niezadowolony?
Jeżeli odpowiedzieliście twierdząco na którekolwiek z powyższych pytań, będzie Wam łatwiej się teraz zatrzymać i dokonać małych zmian niż kiedy baza kodu jest już pokaźnych rozmiarów i restrukturyzacja może wiele kosztować.
Jest to szczególnie ważne dla mniejszych zespołów! Im mniejszy zespół, tym większy koszt wprowadzania poprawek, kiedy można to było zrobić wcześniej.
Kiedy to zastosujecie, dalsza praca pójdzie bardzo szybko.
Wady
Żadna metoda nie pozostaje bez wad. Główna krytyka takiego podejścia opiera się na spowolnieniu procesu na początku, a w tym momencie startupy muszą poruszać się niezwykle szybko, a nie robić coś kosztem czegoś innego. Nie ma również na początku zbyt dużo materiału do przeanalizowania, bo nowe treści pojawiają się kiedy powtórzenia etapów 1 i 2 są ukończone. Ok, rozumiem.
Jednak polemizowałbym ze stwierdzeniem, że spowolnienie procesu polega na poruszaniu się wolno - wszystko zależy od kontekstu.
Ktoś, kto jest w stanie przebiec 100 metrów w 9 sekund jest niesamowicie szybki, ale jeśli wyścig ma 10000 metrów, ten wynik niewiele znaczy, jeśli biegacz straci szybko siły i będzie biegł resztę wyścigu w bardzo wolnym tempie.
Takie podejście zdecydowanie dużo kosztuje na początku, ale procentuje w środku szybkich projektów i na dłuższą metę.
Krótkie przykłady
- Masz do napisania dużo komponentów w React? Zastanów się nad tym, jak chcesz zrobić animacje dla jednego z nich przed napisaniem następnego.
- Piszesz aplikację front-endową? Zastanów się nad internacjonalizacją i skrótami klawiszowymi na jednej stronie, zanim zaczniesz pisać następną.
- Piszesz
GET-LIST
RESTful API? Zastanów się, jak chcesz poradzić sobie z paginacją i sortowaniem na jednym punkcie końcowym zanim zaczniesz pisać następny endpoint.
Podsumowanie
To bardzo efektywna strategia na usprawnianie złożonych projektów i jest ona szczególnie istotna dla mniejszych zespołów, które nie mogą sobie pozwolić na poważne błędy.
Nie zawsze jest to jednak dobre podejście, szczególnie, gdy potrzeba na początku dużych postępów. Jeśli jesteś jednak w stanie pogodzić się z tą metodologią, Twój zespół będzie w stanie wykonać fundamentalną pracę od razu.
Poniżej znajduje się pełniejszy przykład, w którym przedstawione jest działanie metody na przykładzie startupu. Przykład ten dokładniej omawia powyższe zasady.
Metoda: przykład rozwoju oprogramowania
Załóżmy, że pracujesz w małym startupie w pierwszym dniu pisania kodu. Twój startup rozwija platformę do dostarczania hummusu - coś na zasadzie Uber Eats, czy SkipTheDishes, ale bardziej sfokusowane.
Żeby zachować zwięzłość, powiedzmy, że obiektem w systemie są użytkownicy, zamówienia i restauracje, a poniżej znajduje się lista zadań do wykonania:
- back-end: konfiguracja bazy danych, usług (uwierzytelnienie, trwałość i tak dalej), RESTful API (bez GraphQL)
- front-end: aplikacja webowa React
- deployment: CI/CD
Istnieje szereg rozwiązań na przejście z dnia zero do skończonego produktu:
- Oddzielne zespoły zajmują się back-endem i front-endem równolegle, a wdrożenie odbywa się po skończeniu przydzielonych zadań
- Wszystkie zespoły pracują na back-endzie, potem nad front-endem i deploymentem równolegle
- Wszystkie zespoły pracują równolegle
- …
Metoda depth-first development działałaby tutaj w następujący sposób:
- Spraw by back-end zaczął działać
- Zaimplementuj ciągłą integrację
- Zaimplementuj ciągłe wdrażanie
- Wprowadź do systemu jeden obiekt (na przykład, restauracje)
- Napisz endpoint
GET-LIST
, włączając walidację - Odpowiednio wyeksponuje API w artefakcie, który zdeploy'ujesz
- Napisz testy API i uruchom je podczas ciągłej integracji
- Uruchom front-end
- Napisz komponent, który wyświetla dane restauracji przy pomocy endpointu
GET-LIST
- Napisz testy integracyjne dla tego komponentu i uruchom je podczas ciągłej integracji
- Powtórz kroki 5-10 dla
POST
,PUT
iDELETE
i nanieś poprawki, jeśli takowe są potrzebne - Powtarzaj kroki 4-11 dla innych obiektów w systemie i wprowadź poprawki tam gdzie trzeba
Zapewne widzisz, że moglibyśmy po prostu przejść na przełaj (np. zaczynając pisać POST
endpoint po punkcie końcowym GET
w kroku 5), zamiast tego, szliśmy jednak głębiej.
Użyteczność takiej metody polega na tym, że solidny schemat na radzenie sobie z taskami pojawi się, gdy podejdziemy do projektu “pionowo”, a nie “na szerokość”.
Na przykład, pisanie komponentu, który używa API w kroku 9 mogło spowodować, że uświadomiliście sobie potrzebę mechanizmu podczas walidacji, którego nie potrzebowaliście około kroku 5. W sytuacji gdy napisalibyście wszystkie punkty końcowe API podczas kroku 5 to w momencie pisania komponentów w kroku 9, zamiast poprawiać tylko jeden z nich, musielibyście poprawić je wszystkie.