Co robisz, jeśli osiągniesz ślepy zaułek metodami ewolucyjnymi, takimi jak Agile lub XP?

11

Kiedy czytałem słynny post Martina Fowlera na blogu Is Design Dead? , jednym z uderzających wrażeń, jakie mam, jest to, że biorąc pod uwagę fakt, że w metodologii zwinnej i programowaniu ekstremalnym zarówno projektowanie, jak i programowanie są ewolucyjne, zawsze są punkty, w których trzeba coś zmienić.

Może się zdarzyć, że gdy poziom programisty jest dobry i rozumieją implikacje projektowe i nie popełniają krytycznych błędów, kod ewoluuje. Jednak w normalnym kontekście, jaka jest podstawowa rzeczywistość w tym kontekście?

W normalnym dniu, biorąc pod uwagę znaczny rozwój produktu, a kiedy pojawia się krytyczna zmiana wymagań, czyż nie jest to ograniczenie, że o ile kiedykolwiek chcemy, podstawowych aspektów projektu nie można modyfikować? (bez wyrzucania dużej części kodu). Czy nie jest całkiem prawdopodobne, że ktoś osiągnie ślepy zaułek w przypadku jakiejkolwiek dalszej poprawy projektu i wymagań?

Nie opowiadam się tutaj za żadną nie-zwinną praktyką, ale chcę wiedzieć od ludzi, którzy praktykują zwinne, iteracyjne lub ewolucyjne metody rozwoju, jeśli chodzi o ich prawdziwe doświadczenia.

Czy kiedykolwiek osiągnąłeś takie ślepe zaułki ? Jak udało ci się tego uniknąć lub uciec? Czy też istnieją środki zapewniające, że projekt pozostaje czysty i elastyczny w miarę ewolucji?

Dipan Mehta
źródło

Odpowiedzi:

17

Właśnie przeczytałem opublikowany link do artykułu, muszę powiedzieć, że Fowler napisał kilka bardzo dobrych punktów i wiele rzeczy, które powiedział, opowiadam się z naszym zespołem od lat.

IMO, jeśli wykonasz jakiś porządny projekt, nie powinieneś wchodzić w sytuację, która byłaby uważana za ślepą uliczkę. Zawsze uważałem oprogramowanie za zbudowane z elementów . Nadal wierzę w pewne wstępne projekty, ale głównym celem nie jest zaprojektowanie całego produktu, ale zapewnienie ogólnej architektury / kierunku, aby Twój zespół mógł wyobrazić sobie wspólny obraz, nad którym wszyscy pracujemy. Jeśli masz kilka kostek i trójkątów, warto naszkicować, w jaki sposób zamek zostałby złożony, zanim zaczniesz po prostu uderzać klockami.

Ponieważ pochodzę z ziemi OO, dla mnie każdy blok jest klasą, a powierzchnia tego bloku jest interfejsem publicznym (co jest widoczne dla klas zewnętrznych lub pochodnych). Jeśli będziesz przestrzegać dobrych zasad SOLID, upewnisz się, że każdy blok jest wyjątkowo prosty i ma intuicyjny interfejs publiczny. Wracając do mojej analogii, chcesz mieć pewność, że kod tworzy tylko proste kształty. Za każdym razem, gdy tworzysz zbyt złożone klasy (wiele funkcji, wiele zmiennych), tworzysz kształty, które trudno wykorzystać ponownie, gdy zmieniają się wymagania.

Zgadzam się z Fowlerem, że największym ryzykiem / wyzwaniem związanym z projektowaniem ewolucyjnym jest pozostawienie decyzji projektowych czasowi kodowania i oczekiwanie od każdego dewelopera podjęcia takich decyzji. W tym miejscu system może się zepsuć, jeśli nie masz odpowiednich mechanizmów sprzężenia zwrotnego. Ilekroć pojawia się prośba o nową funkcję, niezwykle kuszące jest po prostu znalezienie funkcji, którą należy rozszerzyć, umieszczenie w niej jakiegoś warunku i dodanie do niej całej wiązki kodu. Czasami może to być wszystko, czego potrzeba, ale jest to również (IMO) najczęstsza praktyka prowadząca do ślepych zaułków. Nie ma to nic wspólnego z projektowaniem ewolucyjnym. To się nazywa „brak projektu”.

Tak długo, jak poświęcisz czas, aby się cofnąć i powiedzieć: poczekaj chwilę, ta klasa ma już 15 zmiennych składowych, pozwól mi wyodrębnić 6 z nich i umieścić je w swojej własnej, zamkniętej klasie, twoje oprogramowanie będzie się składać z bardzo lekkiego - lekkie, elastyczne i wielokrotnego użytku bloki konstrukcyjne. Jasne, że jeśli pojawią się premierzy i zmienią o połowę wymagania dotyczące produktów, być może będziesz musiał usunąć niektóre swoje klocki, odłożyć je z powrotem na półkę i dobrać nowe (tak jak podczas budowania zamku, nie możesz użyć wszystkich twoje cylindry). Ale w tym momencie to tylko część robienia interesów. Wymagania się zmieniły i zachowując elastyczność i modułowość kodu, powinieneś być w stanie zmienić swój produkt, aby dostosować go do nowego kierunku działalności.

Wierzę, że to ewolucyjne podejście do projektowania działa na każdym poziomie umiejętności inżyniera. Osobiście tworzyłem oprogramowanie przez bardzo długi czas i zanim nasz zespół przeszedł na zwinną metodologię, byłem odpowiedzialny za wysyłkę kilku głównych komponentów z mojego komputera deweloperskiego prawie bezpośrednio do klienta przy prawie żadnej kontroli jakości. Jednocześnie elementy te zawsze były elastyczne i łatwe w utrzymaniu.

Próbuję tylko powiedzieć, że uważam się za względnie przyzwoitego w projektowaniu oprogramowania. Jednocześnie, jeśli poprosiłeś mnie o napisanie 100-stronicowego dokumentu projektowego, przekazanie go programistowi i oczekiwanie, że zadziała, prawdopodobnie nie mógłbym zaprojektować się z papierowej torby. Kiedy zaczynałem pracę, czasami szkicowałem kilka diagramów podobnych do UML (bardzo uproszczonych, ale nie w pełnym języku), ale kiedy zaczynam kodować, refaktoryzowałem według potrzeb, a mój końcowy kod nigdy nie wyglądałby tak, jak pierwotnie narysowałem. Nawet jeśli spędzam miesiąc lub dwa, zastanawiając się nad każdym najmniejszym szczegółem, nie mogę sobie wyobrazić, że ktoś inny może wziąć moje diagramy i wymyślić solidne oprogramowanie bez zmiany projektu podczas kodowania.

Na drugim końcu spektrum, obecnie w moim zespole (teraz zwinny i w pełni to popieram) mamy kilku facetów, którzy dołączyli do nas z zagnieżdżonej ziemi, gdzie zajmowali C tylko przez ostatnie 15 lat. Oczywiście pomogłem przy wstępnym planowaniu i układaniu klas, ale zadbałem również o regularne przeglądy kodu i sesje burzy mózgów, podczas których omawiamy zastosowania SOLID i zasad projektowania. Stworzyli kod spaghetti, który sprawił, że trochę się skrzywiłem, ale po lekkim trąbieniu zacząłem refaktoryzować to, co już zostało wyprodukowane, a zabawne jest to, że jeden z nich wrócił do mnie kilka dni później i powiedział: Nienawidzę żeby to powiedzieć, ale po przeniesieniu tego kodu wygląda to o wiele bardziej czytelnie i zrozumiale. Ślepy zaułek odwrócony. Punkt I Staram się, aby nawet ktoś, kto jest zupełnie nowy w OO, mógł stworzyć całkiem przyzwoity kod, o ile ma mentora z większym doświadczeniem, aby przypomnieć mu, że „projekt ewolucyjny” to nie to samo, co „brak projektu”. I nawet niektóre z jego „bardziej złożonych” klas nie są aż tak przerażające, ponieważ każda klasa nie ma tak dużej odpowiedzialności (tj. Niezbyt dużego kodu), więc najgorsze staje się gorzej, jeśli ta klasa „ślepych zaułków”, my rzuć to i napisz klasę zastępczą, która ma ten sam publiczny interfejs (do tej pory nigdy nie widziałem potrzeby takiej nieprzewidzianej treści w żadnym napisanym artykule i robiłem recenzje kodu dwa razy w tygodniu).

Na koniec jestem również zwolennikiem dokumentów projektowych (przynajmniej w warunkach biznesowych mojego obecnego zespołu), ale głównym celem naszych dokumentów projektowych jest Pamięć Organizacyjna , więc rzeczywiste dokumenty są pisane po wygenerowaniu kodu i odnowione. Przed kodowaniem generalnie mamy szybką (czasem nie tak szybką) fazę projektowania, w której szkicujemy klasy na serwetkach / mspaint / visio i zawsze przypominam ludziom, że ta faza tworzy ścieżkę do naśladowania, a nie plan, a kiedy zaczynają kodować, wszystko, co nie ma sensu, powinno zostać zmienione. Nawet z tymi przypomnieniami nowi faceci starają się przywrócić kod do oryginalnego projektu, bez względu na to, jak nienaturalnie wydaje się im nawet. Zwykle pojawia się to w recenzjach kodu.

Dang, dużo pisałem. Przepraszam za to.

DXM
źródło
1
+1 Było warto za każde słowo. Zetknąłem się z tymi sytuacjami, tak jak to opisałeś, a po upływie terminu poproś ich o oczyszczenie (przeczytaj refaktoryzację) z tego, co moim zdaniem jest powszechne w projektowaniu. Ale często ludzie powtarzają to samo pytanie - dlaczego robię to samo ponownie? Wydaje mi się, że teraz mam odpowiedź - jeśli potrzebujesz czasu na wprowadzenie na rynek i pozwolenie na ewolucję projektu, refaktoryzacja nie jest rekompensatą za twoje stare grzechy, ale faktycznie jest uzasadniona rzeczą do zrobienia.
Dipan Mehta
Tak, dużo napisałeś, ale to była dobra rzecz. Naprawdę podobało mi się czytanie :).
Radu Murzea,
11

Powiedziałbym, że zjawisko „projektowania ślepego zaułka” jest ortogonalne w stosunku do metod zwinnych. Mam na myśli to, że można zrobić wodospad, spędzić dużo czasu z góry na (złym) projekcie. Następnie poświęć dużo czasu na jego wdrożenie, aby znaleźć się w ślepym zaułku.

W ogóle metody zwinne powinny pomóc ci wcześniej odkryć, że dokonałeś złych wyborów projektowych. Powodem tego jest to, że twoje zaległości powinny najpierw wykonać elementy o najwyższej wartości dla klienta i powinieneś skupić się na dostarczaniu użytecznych przyrostów oprogramowania. Jeśli twój projekt pozwala na dostarczenie wysokiej wartości i użyteczności, jest już na coś dobry :-) W przeciwieństwie do tego, możesz mieć zły projekt w wodospadzie, w którym przez wiele lat możesz się nie dowiedzieć, że ten projekt nie może dostarczyć dowolna wartość i użyteczność - wszystko, co masz, to złudzenie, że jest to dobry projekt. Jak mówią, dowodem jest pudding.

Drugą stroną jest to, że nawet w metodach zwinnych ważne jest, aby mieć realną wizję projektu systemu, który kieruje decyzjami od iteracji do iteracji. Myślę, że Ken Schwabber powiedział coś takiego: jeśli masz zespół okropnych programistów, będą oni produkować złe oprogramowanie konsekwentnie iteracyjnie przez iterację. Zwinność oznacza po prostu, że nie spędzasz dużo czasu z góry, ponieważ masz ograniczoną wiedzę, którą możesz się nauczyć lub sobie wyobrazić przed rozpoczęciem wdrażania ( a wymagania również się zmieniają). Są jednak sytuacje, w których musisz wykonywać pracę z góry (np. Badania), a następnie musisz to zrobić.

Jak uniknąć ślepych uliczek?

Powiedziałbym głównie, przewidując przyszłe wymagania. Jest to coś, co zyskujesz dzięki doświadczeniu i znajomości podobnych projektów / produktów. To oczekiwanie jest częściowo tym, co pomaga wdrożyć dobry projekt, ponieważ zadajesz sobie wiele pytań „a co jeśli” na temat twojego obecnego systemu. Dla mnie jest to kluczowy element. Techniki takie jak OO pomagają ci, gdy już wiesz, co robisz.

Co robisz, jeśli masz ślepy zaułek?

„Ślepy zaułek” nie różni się niczym od jakiegokolwiek innego bloku technicznego, który trafisz podczas opracowywania czegoś nowego. Pierwszą rzeczą do zrozumienia jest to, że tak naprawdę nie ma prawdziwych „ślepych zaułków”, które zmusiłyby cię do całkowitego wycofania się. Przynajmniej nauka do tego momentu pozwala ci iść naprzód, więc wysiłek nie został zmarnowany. Kiedy trafisz w ślepy zaułek, masz problem . Problemem jest to, co należy zmienić, aby spełnić nowe (lub stare) wymagania i jak zoptymalizować dokonanie tej zmiany. Wszystko, co musisz teraz zrobić, to rozwiązać ten problem. Bądź wdzięczny, że jest to oprogramowanie, a nie np. Projekt samolotu, ponieważ zmiana jest znacznie łatwiejsza. Zidentyfikuj problemy, napraw je == refactor == inżynieria oprogramowania. Czasami wymaga to dużo pracy ...

Jeśli używasz Scruma, zmiana ta powinna oczywiście wynikać z historii użytkowników (co użytkownik zyskuje na tej zmianie?). Proces zaczynałby się od jednej historii, która nie może być łatwo zaakceptowana przez obecny projekt (ups) i odbyłaby się dyskusja z właścicielem produktu na temat tego, jak rozbić tę historię. Przez tę zmianę nadal stosujesz zwinne zasady.

Kilka znanych wielkich zmian wymagań ze świata OS, które przychodzą mi do głowy:

Niezależnie od tego, jak na nie spojrzysz, są to dużo pracy. Oryginalny projekt prawie na pewno nie uwzględniał możliwości takiego zdarzenia (tj. Poręczność nie była dużym wymogiem). To, czy projekt był OO, czy też nie, prawdopodobnie nie jest dużym czynnikiem. W dobrym projekcie części specyficzne dla platformy byłyby nieco odizolowane, a praca byłaby łatwiejsza.

Guy Sirton
źródło
W rzeczywistości wczesne wersje systemu Windows NT zaimplementowały „warstwę wyodrębniania sprzętu” i wspierały DEC Apha oraz x86. Ponieważ nikt nigdy nie kupował maszyn opartych na DEC alfa, zostało to po cichu odrzucone. Wyobrażam sobie, że ta „niezależność od maszyny” nadal istnieje w szczątkowym formacie w bieżącym wydaniu, więc port ARM może nie być tak trudny.
James Anderson
3

Stale zmieniam projekty i korzystam z diagramów klas UML. Mam na myśli to, że tworzę jeden lub więcej diagramów klas według pakietów. Każdy diagram jest zapisywany w katalogu głównym pakietu. Każdy klasyfikator UML ma własny identyfikator, który jest odwzorowany na powiązany identyfikator Java. Oznacza to, że kiedy otwieram mój diagram, jest on automatycznie aktualizowany do najnowszych zmian refaktoryzacji kodu. Mogę również bezpośrednio zmieniać diagramy klas na poziomie graficznym, a cały mój projekt jest natychmiast refaktoryzowany. Działa całkiem dobrze, ale nigdy nie zastąpi człowieka. Mój diagram klas UML jest również tylko graficznym widokiem mojego kodu. Bardzo ważne jest, aby nie mieszać kodu i modelu, takiego jak zaćmienie EMF, ponieważ po dokonaniu refaktoryzacji tracone są również informacje o modelu. Nigdy nie używam generatora kodu Model Driven Development, ponieważ jest to bezużyteczne. Ja nie

Powiedziawszy, że posiadanie ponad 100 klasowych diagramów reprezentujących wszystkie szczegóły struktury mojego projektu i pełne notatek wszędzie jest naprawdę pomocne. Tworzę tylko diagramy klas dla projektów, ponieważ zwykle programiści nie mają czasu na naukę lub używanie innych diagramów. Diagramy klas są również bardzo dobre, ponieważ są automatycznie aktualizowane. Diagramy klas można tworzyć po kodzie, po prostu odwracając pakiet i dodając notatki. Jest szybki i zawsze dokładny oraz w 100% iteracyjny.

Proszę nie mylić rozwoju opartego na modelu, który jest kodem generującym model, i zwykle używa UML jako prezentacji graficznej z diagramami klas UML zaktualizowanymi z kodu. Tylko zsynchronizowany kod UML ma dla mnie prawdziwą wartość, jeśli jest wiele iteracji.

Przepraszam, że tak długo, ale myślę, że powinniśmy dać drugą szansę diagramom klas UML, jeśli byłyby użyte tylko jako graficzny widok naszego projektu. Oznacza to, że UML obejmuje pełny projekt i ma jeden model złożony z diagramów dużych klas reprezentujących pełny projekt. Byłoby absurdalnie mieć setki małych widoków i model dla każdego widoku w projekcie mającym setki widoków :-)

UML_GURU
źródło
1
+1, aby pokazać pomysł kodu pocztowego UML! Co ciekawe, po kodzie nigdy nie wracamy do dokumentów ze schematami !
Dipan Mehta
Tak, jest to dokładnie problem związany z programowaniem opartym na UML. Generujesz dokumentację, a następnie nigdy jej nie używasz w przypadku modyfikacji projektu. Scalanie modelu i kodu pozwala nam używać UML i zmieniać go tyle razy, ile potrzebujemy.
UML_GURU
3

Osiągnąłem ślepy zaułek mój kod i inne kody z powodu złego projektu, zmiany kierunku itp. Widziałem również wiele innych osób napotykających ten problem. Wielkim błędem (przynajmniej wydaje mi się, że to pomyłka) jest natychmiastowe pragnienie wyrzucenia działającego kodu i ponownego wdrożenia wszystkiego od podstaw.

Do każdej sprawy podszedłem w ten sam sposób, który wydawał się działać dobrze:

  • określić, dlaczego obecny projekt nie działa
  • wymyślić nowy projekt i plan przejścia
  • oznacz kod, który nie będzie używany jako przestarzały
  • wdrożyć tylko to, czego potrzebuję do nowego projektu, aby zaspokoić natychmiastową potrzebę
  • usuń kod nowy kod staje się przestarzały

Koszty:

  • większa złożoność dzięki 2 implementacjom w bazie kodu jednocześnie
  • większy całkowity koszt na funkcję / naprawę błędu niż pełne przeprojektowanie / ponowna implementacja przy założeniu dobrych przypadków użycia i testów

Korzyści:

  • co najmniej o rząd wielkości mniejsze ryzyko, ponieważ nadal masz stary działający projekt / kod
  • szybciej wprowadzać na rynek dowolne funkcje od 1 do n-1, które nie wymagają pełnego przeprojektowania
  • jeśli produkt / kod skończy się śmiercią przed zakończeniem pełnego przeprojektowania, zaoszczędzisz różnicę czasu rozwoju
  • iteracyjne podejście i wszystkie wynikające z niego korzyści
dietbuddha
źródło
2

Około miesiąc lub dwa lata temu nasz obecny projekt utknął w martwym punkcie z powodu złych decyzji projektowych (i braku dużej ilości projektu w jednym miejscu) w stylu rozwoju SCRUM.

Naszym rozwiązaniem (i moim zdaniem standardowym dla SCRUM) było poświęcenie całego sprintu (~ 2 tygodnie) wyłącznie na refaktoryzację. W tym czasie nie dodano żadnej nowej funkcji, ale mogliśmy pomyśleć o obecnej bazie kodu i zaprojektować znacznie lepszy system do tego, co robiliśmy.

Przeszliśmy już tę przeszkodę i ponownie dodaliśmy nowe funkcje.

Izkata
źródło
To kolejne duże tarcie do znalezienia. Powiedzieć klientowi - że budujemy teraz to samo - o funkcjach, które nie są niczym nowym, po przekazaniu pierwszej wersji! Jak często i często (jeśli w ogóle) możesz sobie pozwolić na przekazanie tego klientowi? albo jak w ogóle wyjaśnić?
Dipan Mehta
Masz dwie podstawowe opcje, albo najpierw po prostu powiesz prostą prawdę - że chociaż to, co masz, to bałagan i trzeba go uporządkować, aby przejść do przodu, lub po drugie, że budujesz infrastrukturę w celu wspierania ciągłego rozwoju ( co jest nie mniej prawdziwe, ale obraca się, aby być nieco bardziej pozytywnym). Oba można podsumować, mówiąc, że w celu zapewnienia funkcjonalności zaciągnęliśmy dług techniczny, który teraz należy spłacić.
Murph,
@Dipan Mehta: Załóżmy, że klient chce dwupiętrowego domu. Projektujesz i dostarczasz. Następnie chcą mieć cztery dodatkowe kondygnacje. Mówicie, no cóż, muszę spędzić ten czas tylko po to, aby obecny budynek był solidniejszy, aby mógł pomieścić cztery dodatkowe kondygnacje. Nie sądzę więc, że powinien to stanowić problem dla klienta, jeśli pierwotny plan obejmował tylko dwie kondygnacje. Jeśli od samego początku zaplanowano sześć kondygnacji, tak, informowanie klienta może być problemem.
Giorgio,
@DipanMehta Mamy również szczęście, że klienci niekoniecznie wiedzą o tym projekcie. Jest to uaktualnienie do produktu, którego obecnie używają, z niejasną datą zakończenia około końca tego roku. Więc nie muszą nawet wiedzieć o opóźnieniu w refaktoryzacji;) (Kierownik zajmujący się większością decyzji projektowych jest na miejscu)
Izkata,
2

Kluczem do ograniczenia kosztów zmian w projekcie jest utrzymanie kodu w stanie SUCHYM, jak to możliwe. Spowoduje to przeniesienie większości kodu aplikacji na bardzo wysoki poziom, gdzie większość kodu bezpośrednio wyraża intencję, a stosunkowo mało określa mechanizm. Jeśli to zrobisz, decyzje projektowe będą miały najmniejsze możliwe wyrażenie w kodzie, a zmiany projektowe będą miały możliwie najmniejszy koszt.

Kevin Cline
źródło
2

Kluczem do uniknięcia ślepych uliczek projektu jest jak najwcześniejsze rozpoznanie, kiedy projekt wymaga zmiany, a następnie zmiana. Największe problemy nie wynikają z ciągłej ewolucji projektu, ale z odmowy rozwinięcia projektu, dopóki nie stanie się on wielkim problemem.

Na przykład Netflix ma funkcję profilu, w której różni członkowie rodziny mogą rozliczać się z tego samego planu, ale mają osobne kolejki. Kilka lat temu ogłosili, że będą musieli anulować tę funkcję, ponieważ tylko około 10% jej użytkowników korzystało z niej, ale z powodu włamania do implementacji pochłaniała ona nadmierną ilość prac konserwacyjnych. Po wrzawie ugryzli kulę i przeprowadzili kosztowną przeprojektowanie, aby zatrzymać tych klientów.

Jestem pewien, że byli inżynierowie, którzy rozpoznali nieoptymalny projekt, kiedy po raz pierwszy dodali tę funkcję. Gdyby to wtedy zmienili, nie byłoby to aż tak duże.

Karl Bielefeldt
źródło
1

Czy to nie Fred Brooks powiedział coś w stylu „Plan wyrzucenia pierwszego”? Nie czuj się zbyt ponuro, ślepe projekty pojawiają się również w projektach, które starają się wykonać cały projekt z góry. Przeprojektowanie ma miejsce we wszystkich rodzajach rozwoju, czy to dlatego, że od samego początku był to niewykonalny projekt (te ostatnie 20%, które zostały pomalowane - „diabeł tkwi w szczegółach”), czy też dlatego, że klient zmienił skupienie. Nie ma potrzeby używania dzwonków alarmowych, nie martw się zbytnio.

zaraz
źródło