Blog
Przyblokowana praca w kanbanie to stan “błędu krytycznego” a nie “oczekiwania”
Tagi: kanban
Podczas dyskusji na temat limitu WiP (work in progress – pracy w toku) dla kolumny “development” naszej kanbanowej tablicy, pojawiło się pytanie: co powinniśmy zrobić, jeśli historyjka zostaje z jakiegoś powodu zablokowana w trakcie developmentu?
Zaproponowałem, by oznaczyć przyblokowaną historyjkę jakimś rzucającym się w oczy znacznikiem (np. żetonem z dużym czerwonym wykrzyknikiem), “zatrzymać linię produkcyjną” i skupić się na jak najszybszym odblokowaniu historyjki.
To wzbudziło wiele wątpliwości. Dlaczego w takim przypadku powinniśmy zatrzymywać linię produkcyjną? Czemu nie możemy przez chwilę popracować wielozadaniowo i rozpocząć kolejnej historyjki, pozwalając przyblokowanej historyjce “powisieć” bezczynnie na tablicy, dopóki nie zostanie odblokowana?
Odpowiedziałem, że nie możemy wziąć kolejnej historyjki, ponieważ kolumna “development” jest już pełna i złamalibyśmy w ten sposób limit WiP. Była to, przyznaję, głupia odpowiedź, skupiająca się na mechanice tablicy zamiast na leżących u jej podłoża powodach. I, co było nieuniknione, zaowocowała dyskusją nad propozycjami rozwiązań również skupionymi wokół mechaniki tablicy.
Z powodu pewnego poślizgu pomiędzy wyspecyfikowaniem historyjki a jej developmentem, na naszej kanbanowej tablicy kolumnę “development” poprzedza kolumna “oczekuje na development”. Jednym z zaproponowanych rozwiązań było przerzucenie zablokowanej historyjki z powrotem do kolumny “oczekuje”, zamiast oznaczania jej żetonem wykrzyknika. Po pierwsze, projekt tablicy by się wtedy uprościł, niepotrzebne byłyby dodatkowe żetony. Po drugie, w elegancki sposób rozwiązałoby to problem “zatrzymania linii produkcyjnej” – historyjka przerzucona do kolumny “oczekuje” przestałaby łamać limit WiP na kolumnie “development”.
Oczywiście szybko wyjaśniłem, że ten pomysł jest zły i że oczekiwanie na development oraz przyblokowanie w trakcie developmentu to dwa całkowicie różne stany. Nie miałem jednak czasu, by we właściwy sposób wyjaśnić dlaczego. Postaram się więc zrobić to teraz:
Wyobraźmy sobie fabrykę samochodów. Po wytłoczeniu w odpowiednie kształty, części są lakierowane a następnie suszone. Ale nie dzieje się to natychmiast – pomiędzy wytłaczaniem a lakierowaniem występuje jeszcze dodatkowy krok “oczekuje”, podczas którego części są przechowywane w magazynie.
Wyobraźmy sobie teraz, że jakaś część została pobrana z magazynu do lakierowania, jednakże nastąpiła awaria i została ona polakierowana jedynie w połowie.
Czy musimy zatrzymać linię i naprawić problem? Może moglibyśmy pracować “wielozadaniowo” i po prostu zacząć lakierować kolejną część? Oczywiście nie możemy tak zrobić. Polakierowana do połowy część będzie zajmować miejsce na linii lakierniczej, utrudniając czy wręcz uniemożliwiając polakierowanie kolejnej części. Nie możemy jej też po prostu zrzucić na podłogę, bo poprzykleja się do niej kurz i świeży lakier zostanie zniszczony.
Czy możemy w takim razie przenieść polakierowaną do połowy część z powrotem do magazynu? Moglibyśmy udawać, że ta część oczekuje na lakierowanie, tak jakby była nowa. Tak również nie możemy zrobić. Nie możemy przenieść mokrej części do magazynu – uszkodzilibyśmy lakier i przy okazji narobili niezłego bałaganu na półce. Co więcej, nawet gdybyśmy jakimś cudem dali radę umieścić część bezpiecznie na półce, po kilku godzinach lakier wyschnie, utrudniając dokończenie lakierowania bez zostawienia widocznej linii na styku nowego i starego lakieru.
Jedyną opcją jest naprawienie awarii i albo jak najszybsze dokończenie lakierowania części, albo po prostu jej wyrzucenie.
Proces rozwoju oprogramowania jest, choć może się to wydawać nieintuicyjne, całkiem podobny. Bardzo trudno jest zatrzymać historyjkę w połowie developmentu i powrócić do niej później.
Najbardziej oczywistym powodem jest niedokończony, niestabilny kod. Taki kod nie może być zmergowany do głównej bazy kodu. By móc pracować nad kolejnym zadaniem, developer musi go “zestashować” lub utrzymywać na osobnej gałęzi. Większość systemów kontroli wersji to ułatwia, nie jest to więc samo w sobie wielkim problemem. Prawdziwym problemem jest dokończenie i zmergowanie kodu później. Ponieważ pozwalamy, by inne historyjki były rozwijane, podczas gdy praca nad przyblokowanym kodem jest wstrzymana, istnieje duża szansa na to, że jakieś wspólne części systemu się zmienią, w wyniku czego merge zakończy się konfliktami.
Kolejnym powodem jest umysłowa praca, jaką developer już wykonał. Jeżeli jest on w połowie kodowania, prawdopodobnie przeanalizował już wymagania, skonstruował w swojej głowie model mentalny problemu i ma wypracowaną wizję rozwiązania oraz tego, jak zaprojektować kod. Gdy przełączy się on na pracę nad inną historyjką, większość tego kontekstu przepadnie. A zbudowanie takiego kontekstu – co ponownie może nie być intuicyjne – to tak na prawdę najtrudniejsza i najbardziej czasochłonna część developmentu.
Co więcej, jeśli pozwolimy developerowi rozpocząć kolejną historyjkę, nie możemy zagwarantować, że w momencie gdy początkowa historyjka się odblokuje, będzie ona kontynuowana przez tego samego developera. A dla innego developera przejęcie historyjki będzie jeszcze trudniejsze, gdyż nie tylko będzie on musiał skonstruować swój model mentalny od zera, ale jeszcze będzie musiał dopasować ten model do innego, często konfliktującego, modelu, już obecnego w niedokończonym kodzie.
Jak widać, nie można bagatelizować pracy która już jest w toku. Zablokowana praca nie jest normalną fazą naszego workflow – to awaria, problem który powinien być obsłużony niemal z takim samym priorytetem jak krytyczne błędy na produkcji.
Jak to wygląda u Ciebie? W jaki sposób wizualizujesz i radzisz sobie z przyblokowanymi zadaniami na Twojej kanbanowej tablicy? Podziel się swoimi uwagami w komentarzach poniżej!