Jak utrzymać duże i złożone oprogramowanie, które można utrzymywać przez lata?

156

Od wielu lat pracuję jako programista. Z mojego doświadczenia wynika, że ​​projekty stają się bardziej złożone i niemożliwe do utrzymania, ponieważ coraz więcej programistów angażuje się w rozwój produktu.

Wydaje się, że oprogramowanie na pewnym etapie rozwoju ma tendencję do „hackier” i „hackier”, zwłaszcza gdy żaden z członków zespołu, który zdefiniował architekturę, nie pracuje już w firmie.

To frustrujące, że deweloper, który musi coś zmienić, ma trudności z uzyskaniem pełnego obrazu architektury. Dlatego istnieje tendencja do rozwiązywania problemów lub wprowadzania zmian w sposób, który działa w stosunku do oryginalnej architektury. Rezultatem jest kod, który staje się coraz bardziej złożony i jeszcze trudniejszy do zrozumienia.

Czy jest jakaś pomocna rada na temat tego, jak utrzymać kod źródłowy w sposób umożliwiający jego utrzymanie przez lata?

chrmue
źródło
9
gorąco polecam książki: „Software Project Survival Guide” Steve'a McConnella, „Rapid Development” Steve'a McConnella, „Refaktoryzacja” Martina Fowlera
Imran Omar Bukhsh
15
... i „Clean Code” Uncle Bob;) (Robert C. Martin)
Gandalf
34
Czy to nie jest pytanie, które zrodziło kilkadziesiąt lat intensywnego czytania i całych kursów na uniwersytetach?
detly
17
@Eric Yin - Nie zgadzam się z komentarzami. Dla mnie to zapach kodu i na dłuższą metę projekty przynoszą więcej szkody niż pożytku, ponieważ nieuchronnie się umawiają i stają się mylące.
JohnFx,
8
@Eric Yin: dążyć do samodokumentowania kodu. Komentarze zamierzeń należy wykorzystywać tylko wtedy, gdy poprawiają zrozumienie.
Mitch Wheat

Odpowiedzi:

138

Jedynym prawdziwym rozwiązaniem pozwalającym uniknąć zgnilizny kodu jest prawidłowe kodowanie!

Jak dobrze kodować to kolejne pytanie. Jest to wystarczająco trudne, nawet jeśli jesteś doskonałym programistą pracującym sam. W heterogenicznym zespole jest jeszcze trudniej. W projektach (pod) zlecanych na zewnątrz ... po prostu módlcie się.

Zwykłe dobre praktyki mogą pomóc:

  1. Nie komplikuj.
  2. Nie komplikuj. Dotyczy to zwłaszcza architektury „dużego obrazu”. Jeśli deweloperzy mają twardy czas, aby uzyskać duży obraz, oni mają zamiar kodować przeciwko niemu. Uprość więc architekturę, aby wszyscy programiści ją otrzymali. Jeśli architektura musi być mniej niż prosta, to programiści muszą zostać przeszkoleni, aby zrozumieć tę architekturę. Jeśli go nie internalizują, nie powinni w nim kodować.
  3. Dąży do niskiego sprzężenia i wysokiej kohezji . Upewnij się, że wszyscy członkowie zespołu rozumieją ten pomysł. W projekcie składającym się z luźno połączonych, spójnych części, jeśli niektóre z części staną się nie do utrzymania, możesz po prostu odłączyć i przepisać tę część. To jest trudniejsze lub prawie niemożliwe, jeśli sprzęgło jest szczelne.
  4. Bądź konsekwentny. Które standardy mają niewielkie znaczenie, ale proszę postępować zgodnie z niektórymi standardami. W zespole każdy powinien oczywiście przestrzegać tych samych standardów. Z drugiej strony łatwo przywiązać się do standardów i zapomnieć o reszcie: proszę zrozumieć, że chociaż standardy są przydatne, to tylko niewielka część tworzenia dobrego kodu. Nie rób z tego dużej liczby.
  5. Przeglądy kodu mogą być przydatne, aby zespół pracował konsekwentnie.
  6. Upewnij się, że wszystkie narzędzia - IDE, kompilatory, kontrola wersji, systemy kompilacji, generatory dokumentacji, biblioteki, komputery , krzesła , ogólne środowisko itp. - są dobrze utrzymane, aby programiści nie musieli tracić czasu na drugorzędne problemy, takie jak jak walka z konfliktami między wersjami plików projektu, aktualizacjami systemu Windows, hałasem i wszelkimi banalnymi, ale irytującymi rzeczami. Konieczność ciągłego marnowania czasu na takie nieciekawe rzeczy obniża morale, co najmniej nie poprawi jakości kodu. W dużym zespole może być jeden lub więcej osób, których głównym zadaniem jest utrzymanie narzędzi programistycznych.
  7. Podejmując decyzje technologiczne, zastanów się, co trzeba zrobić, aby zmienić technologię; które decyzje są nieodwracalne, a które nie. Bardzo dokładnie oceń nieodwracalne decyzje. Na przykład, jeśli zdecydujesz się napisać projekt w Javie , jest to dość nieodwracalna decyzja. Jeśli zdecydujesz się na użycie gotowego formatu binarnego dla plików danych, jest to również dość nieodwracalna decyzja (gdy kod zostanie opublikowany na wolności i musisz nadal obsługiwać ten format). Ale kolory GUI można łatwo dostosować, funkcje początkowo pominięte można później dodać, więc mniej stresuj się na takie problemy.
Joonas Pulakka
źródło
8
To są świetne punkty. Muszę przyznać, że walczę z „upraszczaniem”. Wydaje się, że oznacza to różne rzeczy dla różnych ludzi w różnych kontekstach, co czyni „proste” raczej złożonym (ale wtedy mam naturalną tendencję do komplikowania rzeczy).
Kramii
3
Zgadzam się doskonale z twoimi punktami, zwłaszcza „KIS”. Widzę jednak tendencję, że coraz więcej (młodszych?) Programistów używa raczej złożonych struktur do opisywania nawet najprostszych kontekstów.
chrmue
10
@chrmue: Zobacz „jak pisać Factorial w Javie” ;-)
Joonas Pulakka
8
Umm i „8. Prostota”.
Dawood ibn Kareem
7
# 6 jest warte +10.
Jim G.
55

Testy jednostkowe są twoim przyjacielem . Ich wdrożenie wymusza niskie sprzężenie. Oznacza to również, że „hacky” części programu można łatwo zidentyfikować i zreorganizować. Oznacza to również, że wszelkie zmiany można szybko przetestować, aby upewnić się, że nie psują istniejącej funkcjonalności. Powinno to zachęcić programistów do modyfikowania istniejących metod, a nie do powielania kodu z obawy przed uszkodzeniem.

Testy jednostkowe działają również jako dodatkowa dokumentacja twojego kodu, określając, co każda część powinna zrobić. Dzięki obszernym testom jednostkowym programiści nie powinni znać całej architektury programu, aby wprowadzać zmiany i korzystać z wychodzących klas / metod.

Jako miły efekt uboczny, testy jednostkowe również mają nadzieję zmniejszyć liczbę błędów.

Tom Squires
źródło
3
Bardzo ważny punkt. Przejęłem starszy system, wiele klas, wiele linii kodu, brak dokumentacji, brak testów jednostkowych. Po starannym utworzeniu testów jednostkowych dla wszystkich poprawek i ulepszeń kodu projekt systemu ewoluował do czystszego i łatwiejszego do utrzymania stanu. I mamy „odwagę”, aby przepisać istotne części podstawowe (objęte testami jednostkowymi).
Sam Goldberg,
40

Wszyscy tutaj szybko wspominają o zgniliznie kodu i całkowicie to rozumiem i zgadzam się z tym, ale nadal brakuje większego obrazu i większego problemu tutaj. Zgnilizna kodu nie tylko się zdarza. Ponadto wymieniono testy jednostkowe, które są dobre, ale tak naprawdę nie rozwiązują problemu. Można mieć dobry zasięg testu jednostkowego i stosunkowo wolny od błędów kod, jednak nadal ma on zepsuty kod i wygląd.

Wspomniałeś, że deweloper pracujący nad projektem ma trudności z wdrożeniem funkcji i brakuje szerszego obrazu ogólnej architektury, a tym samym wprowadza hack do systemu. Gdzie jest przywództwo techniczne w zakresie egzekwowania i wpływania na projekt? Gdzie są recenzje kodu w tym procesie?

W rzeczywistości nie cierpisz na zgniliznę kodu, ale cierpisz na zgniliznę drużyny . Faktem jest, że nie powinno mieć znaczenia, czy oryginalni twórcy oprogramowania nie należą już do zespołu. Jeśli kierownik techniczny istniejącego zespołu w pełni i naprawdę rozumie projekt i jest dobry w roli lidera technicznego, nie byłoby to problemem.

wałek klonowy
źródło
Bardzo dobrze, trafiłeś w dziesiątkę! Przykro mi to mówić, ale dokładnie to się tutaj dzieje. I wydaje się niemożliwe, aby zmienić wszystko bez lidera technologii ...
chrmue,
4
@chrmue Chyba tak, jak się potoczyło, ale coraz bardziej mnie to znudzi. Pod wieloma względami żałuję, że nie byłem jeszcze młodszym programistą, kiedy nie byłem tak świadomy, jak źle wszystko wokół mnie wygląda. Wygląda na to, że wcześnie uderzam w kryzys w połowie kariery. Ale włóczę się ... cieszę się, że mogę pomóc.
maple_shaft
1
@Murph Dlaczego nie powinieneś mieć wszechstronnego zespołu w fazie konserwacji? Każdy szef, jakiego kiedykolwiek spodziewałem się od lidera zespołu, a kiedy byłem liderem zespołu, nie oczekiwałem niczego od siebie.
wałek klonowy
1
@maple_shaft ponieważ a) nie zakładamy, że istnieje ołów zespół dedykowany do tego jednego projektu i to jest mniej więcej to pierwszy warunek b) Myślę, że trzeba zrozumieć konstrukcję i się implementacja (to wszystko) i to ciężko. Z jednej strony wszyscy twierdzimy, że nie powinniśmy mieć w naszych projektach ani jednej czcionki całej wiedzy, a przecież mówimy, że musimy ją mieć? To się nie sumuje?
Murph
2
@maple_shaft prawdopodobnie jestem zrzędliwym starym programistą (-: Ale istnieje problem polegający na tym, że często jest to „styl” implementacji, który musi być przestrzegany głęboko w istniejącej bazie kodu - i może być obcy zarówno programistowi, jak i leadowi (dla wielu z prawdziwych powodów)
Murph,
18

Jest kilka rzeczy, które możemy zrobić:

Prześlij jedną osobę ogólną odpowiedzialność za architekturę. Wybierając tę ​​osobę, upewnij się, że ma wizję i umiejętności do opracowania i utrzymania architektury oraz że ma wpływ i autorytet, aby pomóc innym deweloperom podążać za architekturą. Ta osoba powinna być doświadczonym programistą, któremu ufa zarządzanie i któremu szanują rówieśnicy.

Utwórz kulturę, w której wszyscy programiści przejmą własność architektury. Wszyscy programiści muszą być zaangażowani w proces opracowywania i utrzymywania integralności architektonicznej.

Rozwiń środowisko, w którym decyzje architektoniczne są łatwo przekazywane. Zachęć ludzi do rozmowy na temat projektowania i architektury - nie tylko w kontekście bieżącego projektu, ale także ogólnie.

Najlepsze praktyki kodowania sprawiają, że architektura jest lepiej widoczna z kodu - poświęć trochę czasu na refaktoryzację, komentowanie kodu, opracowanie testów jednostkowych itp. Rzeczy takie jak konwencje nazewnictwa i czyste praktyki kodowania mogą bardzo pomóc w komunikacji architektury, więc jako zespół, którego potrzebujesz poświęcić czas na opracowanie i przestrzeganie własnych standardów.

Upewnij się, że cała niezbędna dokumentacja jest jasna, zwięzła, aktualna i dostępna. Upublicznij zarówno schematy architektury wysokiego, jak i niskiego poziomu (przypinanie ich do ściany może pomóc) i utrzymywać je publicznie.

Wreszcie (jako naturalny perfekcjonista) muszę uznać, że integralność architektoniczna jest godnym dążeniem, ale mogą istnieć ważniejsze rzeczy - na przykład zbudowanie zespołu, który może dobrze współpracować i faktycznie wysłać działający produkt.

Kramii
źródło
1
+1, szczególnie za „zbudowanie zespołu, który może dobrze współpracować i faktycznie wysłać działający produkt”.
deworde
1
Dobrym pomysłem jest, aby jedna osoba była rootem odpowiedzialnym za architekturę. Masz jednak „zapach zespołu”, jeśli ta odpowiedzialność jest często wykorzystywana: zespół powinien oczywiście dojść do wspólnych wniosków, zamiast polegać na jednej osobie, która udzieli odpowiedzi. Dlaczego? Całkowita wiedza na temat projektu jest zawsze dzielona, ​​przypisanie go do jednej osoby doprowadzi w końcu do większych problemów: tylko jego widok jest zadowolony, skutecznie odcinając skrzydła reszcie zespołu. Zamiast tego zatrudnij najlepszych ludzi i pozwól im wspólnie to wypracować.
casper
1
@casper: Dokładnie. Wyrażasz to, co miałem na myśli raczej lepiej niż ja.
Kramii
18

Sposób, w jaki podchodzę do tego problemu, to wycinanie go w katalogu głównym:

Moje wyjaśnienie będzie używać terminów z Microsoft / .NET , ale będzie miało zastosowanie do dowolnej platformy / zestawu narzędzi:

  1. Używaj standardów nazewnictwa, kodowania, meldowania, przepływu błędów, przepływu procesów - w zasadzie wszystko.
  2. Nie bój się pożegnać z członkami zespołu, którzy nie przestrzegają standardów. Niektórzy programiści po prostu nie mogą pracować w ramach określonego zestawu standardów i staną się wrogami piątej kolumny na polu bitwy, aby utrzymać bazę kodu w czystości
  3. Nie bój się przydzielać słabo wykwalifikowanych członków zespołu do testowania przez długi czas.
  4. Użyj każdego narzędzia w swoim arsenale, aby uniknąć sprawdzania gnijącego kodu: dotyczy to dedykowanych narzędzi, a także wstępnie napisanych testów jednostkowych, które testują pliki kompilacji, pliki projektów, strukturę katalogów itp.
  5. W zespole około 5-8 członków poproś swojego najlepszego faceta, aby refaktoryzował prawie cały czas - sprzątając bałagan, który pozostawiają inni. Nawet jeśli znajdziesz najlepszych specjalistów w tej dziedzinie, nadal będziesz mieć bałagan - jest to nieuniknione, ale może być ograniczone ciągłym refaktoryzacją.
  6. Pisz testy jednostkowe i utrzymuj je - NIE polegaj na testach jednostkowych, aby utrzymać projekt w czystości, tak nie jest.
  7. Omów wszystko. Nie bój się spędzać godzin na rozmowach w zespole. Spowoduje to rozpowszechnienie informacji i usunie jedną z głównych przyczyn złego kodu: zamieszanie w zakresie technologii, celów, standardów itp.
  8. Bądź bardzo ostrożny z konsultantami piszącymi kod: ich kod, z definicji, będzie naprawdę gównianą rzeczą.
  9. Rób recenzje najlepiej jako etap procesu przed zameldowaniem. Nie bój się wycofać zatwierdzeń.
  10. Nigdy nie używaj zasady otwierania / zamykania, chyba że na ostatnim etapie przed wydaniem: prowadzi to po prostu do gnicia kodu, który ma zapach.
  11. Ilekroć pojawia się problem, poświęć czas na jego pełne zrozumienie przed wdrożeniem rozwiązania - większość zgnilizny kodu pochodzi z implementacji rozwiązania problemów, które nie są w pełni zrozumiane.
  12. Używaj odpowiednich technologii. Często pojawią się w zestawach i będą świeże: Lepiej jest polegać na wersji beta frameworka, od którego masz zapewnioną obsługę w przyszłości, niż polegać na wyjątkowo stabilnych, ale przestarzałych frameworkach, które nie są obsługiwane.
  13. Zatrudnij najlepszych ludzi.
  14. Odpuść resztę - nie prowadzisz kawiarni.
  15. Jeśli zarząd nie jest najlepszym architektem i ingeruje w proces decyzyjny - znajdź inną pracę.
Casper
źródło
1
Co byś zrobił, gdybyś musiał utrzymywać i ulepszać koszmarną 12-letnią aplikację VB6 z setkami formularzy i zajęć, pracując nad ponownym pisaniem w języku C # w „wolnym czasie”?
jfrankcarr
7
Nie zgadzaj się z numerem 3. Testowanie nie powinno być postrzegane jako kara dla nieprzeszkolonych. W rzeczywistości powinno to być zrobione przez wszystkich programistów i liczone jako równie ważne jak kodowanie i projektowanie! Jeśli masz pana Niewprawnego w drużynie, to zabierz go z drużyny na trening!
NWS
1
„Nie zgadzaj się z numerem 3. Testy nie powinny być postrzegane jako kara dla nieprzeszkolonych”. - To nie powinno i nie jest to, co napisałem, pozwólcie, że wyjaśnię: Testowanie to dobry sposób, aby pozwolić osobom, których nie znasz, na zaufanie do wprowadzenia zmian w kodzie. Najlepsi testerzy, których znalazłem, to ci, którzy chcą zostać autorami kodu i udowadniają swoje kompetencje, przeglądając kod, uruchamiając program i pokazując zdolność do korelowania swoich wyników w środowisku wykonawczym z kodem źródłowym. To nie jest kara - jej szkolenie.
casper
1
„Nie zgadzam się z numerem 10 - jeśli poprawność jest poprawna, poprawia to jakość kodu”. Z mojego doświadczenia zawodowego jest to absolutnie nieprawda: kodu zablokowanego nie można przywrócić, co oznacza, że ​​pozostanie w obecnym stanie do momentu odblokowania. Ten stan można sprawdzić na pewnym etapie, ale na późniejszym etapie weryfikacja ta jest fałszywie dodatnia: cały kod powinien pozostać otwarty do refaktoryzacji aż do etapu przed końcowym testem systemu.
casper
3
@casper: IMHO, Zasady otwartego / zamkniętego nie należy rozumieć jako „nie możesz zmienić źródła”, ale raczej „zaprojektuj kod tak, jakby się zawiesił”. Upewnij się, że można rozszerzyć kod jako niezbędny bez konieczności wprowadzania zmian. Wynik jest z natury bardziej luźno sprzężony i bardzo spójny niż przeciętny kod. Zasada ta ma również zasadnicze znaczenie przy opracowywaniu biblioteki do użytku przez osoby trzecie, ponieważ nie mogą one po prostu wejść i zmodyfikować kodu, więc trzeba go odpowiednio rozszerzyć.
Kevin Cathcart
12

Wyczyść zgniły kod poprzez refaktoryzację podczas pisania testów jednostkowych. Spłacaj (to) zadłużenie projektowe na całym dotkniętym kodzie, ilekroć:

  • Opracuj nową funkcję
  • Napraw problem

Znacznie przyspiesz swój pierwszy cykl testowy poprzez:

  • Refaktoryzacja w celu konwersji modułów kodu na język skryptowy
  • Używaj szybkich maszyn testowych w chmurze

Kod refaktoryzujący do zastosowania niskiego sprzężenia (jednostek o dużej spójności wewnętrznej) przez:

  • Prostsze, (więcej) funkcji (procedur)
  • Moduły
  • Obiekty (i klasy lub prototypy)
  • Czyste funkcje (bez skutków ubocznych)
  • Preferowanie delegacji zamiast dziedziczenia
  • Warstwy (z API)
  • Kolekcje małych, jednofunkcyjnych programów, które mogą działać razem

Wzrost organiczny jest dobry; duży projekt z przodu jest zły.

Miej lidera, który ma wiedzę na temat obecnego projektu. Jeśli nie, czytaj kod projektu, dopóki nie zdobędziesz wiedzy.

Przeczytaj książki o refaktoryzacji.

MarkDBlackwell
źródło
1
+1 Dobra pierwsza odpowiedź, idealny sposób na przedstawienie się nam!
yannis
11

Prosta odpowiedź: nie możesz .

Dlatego powinieneś dążyć do pisania małego i prostego oprogramowania. To nie jest łatwe.

Jest to możliwe tylko wtedy, gdy myślisz wystarczająco długo o swoim pozornie złożonym problemie, aby zdefiniować go w możliwie najprostszy i zwięzły sposób.

Rozwiązanie naprawdę dużych i złożonych problemów można często rozwiązać, bazując na małych i prostych modułach.

Innymi słowy, jak zauważyli inni, kluczem są prostota i luźne połączenie.

Jeśli nie jest to możliwe lub wykonalne, prawdopodobnie prowadzisz badania (złożone problemy bez znanych prostych rozwiązań lub w ogóle nieznanych). Nie oczekuj, że dzięki badaniom bezpośrednio powstają produkty, które można utrzymać, nie po to są.

Joh
źródło
9

Pracuję na bazie kodu dla produktu, który jest w ciągłym rozwoju od 1999 roku, więc jak można sobie wyobrazić, jest on teraz dość złożony. Największym źródłem włamań w naszej bazie kodu jest to, że wiele razy musieliśmy przenosić go z ASP Classic na ASP.NET , z ADO na ADO.NET, z postbacków na Ajax , przełączania bibliotek UI, standardów kodowania itp.

Podsumowując, zrobiliśmy rozsądną robotę, utrzymując bazę kodu w utrzymaniu. Główne rzeczy, które zrobiliśmy, które się do tego przyczyniły:

1) Stałe refaktoryzacja - jeśli musisz dotknąć fragmentu kodu, który jest hackerski lub trudny do zrozumienia, oczekuje się, że poświęcisz dodatkowy czas na jego wyczyszczenie i będziesz mieć swobodę w tym zakresie. Testy jednostkowe sprawiają, że jest to o wiele mniej przerażające, ponieważ łatwiej można przetestować pod kątem regresji.

2) Utrzymuj porządek w środowisku programistycznym - bądź czujny, usuwając nieużywany kod i nie pozostawiaj kopii zapasowych / kopii roboczych / kodu eksperymentalnego w katalogu projektu.

3) Spójne standardy kodowania przez cały czas trwania projektu - Spójrzmy prawdzie w oczy, nasze poglądy na standardy kodowania ewoluują z czasem. Sugeruję trzymanie się standardu kodowania, który zacząłeś przez całe życie projektu, chyba że masz czas, aby cofnąć się i zmodernizować cały kod, aby był zgodny z nowym standardem. To wspaniale, że masz już notację węgierską , ale zastosuj tę lekcję do nowych projektów i nie przełączaj się tylko w połowie nowego projektu.

JohnFx
źródło
8

Ponieważ otagowałeś pytanie zarządzaniem projektami, próbowałem dodać punkty inne niż kodowe :)

  • Planuj obroty - zakładaj, że cały zespół programistów zniknie, zanim osiągnie fazę konserwacji - żaden programista warty swojej soli nie chce utknąć na utrzymaniu swojego systemu na zawsze. Zacznij przygotowywać materiały do ​​przekazania, gdy tylko będziesz mieć czas.

  • Nie można wystarczająco podkreślić spójności / jednolitości. To zniechęci kulturę „idź sam” i zachęci nowych programistów, by zapytali, czy mają wątpliwości.

  • Pozostań w głównym nurcie - zastosowane technologie, wzorce projektowe i standardy - ponieważ nowy programista w zespole (na dowolnym poziomie) będzie miał większe szanse na szybkie uruchomienie i działanie.

  • Dokumentacja - zwłaszcza architektura - dlaczego podjęto decyzje i standardy kodowania. Zachowaj także referencje / notatki / mapy drogowe w dokumentacji domeny biznesowej - zdziwisz się, jak trudno firmom biznesowym wytłumaczyć, co robią deweloperowi bez doświadczenia w domenie.

  • Jasno określ zasady - nie tylko dla obecnego zespołu programistów, ale pomyśl o przyszłych programistach konserwacji. Jeśli oznacza to umieszczenie hiperłącza do odpowiedniego projektu i standardowej dokumentacji na każdej stronie, niech tak będzie.

  • Upewnij się, że architektura, a zwłaszcza warstwy kodu, są wyraźnie rozgraniczone i oddzielone - potencjalnie pozwoli to na zastąpienie warstw kodu w miarę pojawiania się nowych technologii, na przykład zastąpienie interfejsu Web Forms interfejsem jQuery HTML5 itp., Co może kup około roku na dłuższą żywotność.

StuartLC
źródło
7

Jedną z właściwości wysoce możliwego do utrzymania kodu jest czystość funkcji .

Czystość oznacza, że ​​funkcje powinny zwracać ten sam wynik dla tych samych argumentów. Oznacza to, że nie powinny zależeć od skutków ubocznych innych funkcji. Ponadto przydaje się, jeśli same nie wywołują skutków ubocznych.

Ta właściwość jest łatwiejsza do zaobserwowania niż właściwości sprzęgania / kohezji. Nie musisz się starać, aby to osiągnąć, a ja osobiście uważam to za bardziej wartościowe.

Gdy twoja funkcja jest czysta, jej typ sam w sobie jest bardzo dobrą dokumentacją. Ponadto pisanie i czytanie dokumentacji pod względem argumentów / zwracanych wartości jest znacznie łatwiejsze niż wspominanie o jakimś stanie globalnym (możliwe, że są dostępne inne wątki O_O).

Jako przykład szerokiego używania czystości w celu ułatwienia konserwacji, możesz zobaczyć GHC . Jest to duży projekt mający około 20 lat, w którym dokonuje się dużych refaktoryzacji i wciąż wprowadzane są nowe ważne funkcje.

Wreszcie, nie podoba mi się punkt „Uprość to”. Nie możesz uprościć programu podczas modelowania skomplikowanych rzeczy. Spróbuj stworzyć prosty kompilator, a wygenerowany kod prawdopodobnie skończy się powolnym działaniem. Oczywiście, możesz (i powinieneś) uprościć poszczególne funkcje, ale w rezultacie cały program nie będzie prosty.

Rotsor
źródło
2
Inną nazwą tego, co
opisujesz,
6

Oprócz innych odpowiedzi polecam warstwy. Nie za dużo, ale wystarczająco, aby oddzielić różne typy kodu.

Używamy wewnętrznego modelu API dla większości aplikacji. Istnieje wewnętrzny interfejs API, który łączy się z bazą danych. Następnie warstwa interfejsu użytkownika . Różne osoby mogą pracować na każdym poziomie bez zakłócania działania innych aplikacji.

Innym podejściem jest zachęcenie wszystkich do czytania ryzyk comp.risks i The Daily WTF, aby mogli dowiedzieć się o konsekwencjach złego projektu i złego programowania, i będą się bać, widząc swój własny kod opublikowany na The Daily WTF .

James
źródło
6

Ponieważ wiele z tych odpowiedzi wydaje się koncentrować na dużych zespołach, nawet od samego początku, zamierzam przedstawić swój pogląd jako część dwuosobowego zespołu programistów (trzech, jeśli włączysz projektanta) na starcie.

Oczywiście najlepsze są proste projekty i rozwiązania, ale kiedy masz faceta, który dosłownie wypłaca ci pensję, oddychając ci za szyję, niekoniecznie masz czas, aby pomyśleć o najbardziej eleganckim, prostym i łatwym do utrzymania rozwiązaniu. Mając to na uwadze, moim pierwszym ważnym punktem jest:

Dokumentacja Nie komentarze, kod powinien być w większości samodokumentujący, ale takie rzeczy jak dokumenty projektowe, hierarchie klas i zależności, paradygmaty architektoniczne itp. Wszystko, co pomaga nowemu lub nawet istniejącemu programistowi zrozumieć bazę kodu. Pomocne może być także udokumentowanie tych nieparzystych pseudobibliotek, które w końcu się wyskakują, np. „Dodaj tę klasę do elementu tej funkcji”, ponieważ uniemożliwia to również ponowne zapisywanie funkcji.

Jednak nawet jeśli masz ograniczony termin, uważam, że kolejną dobrą rzeczą do zapamiętania jest:

Unikaj włamań i szybkich poprawek. O ile szybka poprawka nie jest faktyczną poprawką, zawsze lepiej jest znaleźć rozwiązanie problemu, a następnie naprawić. Jeśli nie masz dosłownie scenariusza „zacznij działać w ciągu następnych 2 minut lub zostaniesz zwolniony”, poprawka jest teraz lepszym pomysłem, ponieważ nie zamierzasz naprawiać kodu później, po prostu przejdź do następnego zadania, które masz.

Moja ulubiona wskazówka to raczej cytat, choć nie pamiętam źródła:

„Koduj tak, jakby osoba, która Cię ścigała, była morderczym psychopatą, który wie, gdzie mieszkasz”

Aatch
źródło
Zawsze uważałem, że komentarze do funkcji i klas są pomocne, nawet jeśli tylko zapewniają separację segmentów kodu w lokalizacjach funkcji i klas za pomocą podświetlania składni. Bardzo rzadko umieszczam komentarze w kodzie funkcji, ale piszę wiersz dla każdej klasy i funkcji, takich jak /** Gets the available times of a clinic practitioner on a specific date. **/lub /** Represents a clinic practitioner. **/.
Nick Bedford
5

Jedną z zasad, o której nie wspomniano, ale którą uważam za ważną, jest zasada otwarta / zamknięta .

Nie należy modyfikować kodu, który został opracowany i przetestowany: każdy taki fragment kodu jest zapieczętowany. Zamiast tego rozszerz istniejące klasy za pomocą podklas lub użyj ich do pisania opakowań, klas dekoratorów lub przy użyciu dowolnego wzorca, który uważasz za odpowiedni. Ale nie zmieniaj działającego kodu .

Tylko moje 2 centy.

Giorgio
źródło
Co się stanie, jeśli zmienią się wymagania biznesowe wyrażone w działającym kodzie? Nie dotykaj źle rozłożonego, ale technicznie „działającego” kodu, może cię zranić na dłuższą metę, szczególnie gdy przyjdzie czas na wprowadzenie niezbędnych zmian.
jinglesthula
@jinglesthula: Otwarte na rozszerzenie oznacza, że ​​możesz dodać nową funkcjonalność jako nową implementację (np. klasę) istniejącego interfejsu. Oczywiście słabo skonstruowany kod nie pozwala na to: powinna istnieć abstrakcja podobna do interfejsu, który umożliwia kodowi „zmianę” poprzez dodanie nowego kodu zamiast modyfikacji istniejącego kodu.
Giorgio
5
  • Bądź zwiadowcą . Zawsze zostawiaj kod czystszy, niż go znalazłeś.

  • Napraw uszkodzone okna . Wszystkie te komentarze „zmiana w wersji 2.0”, gdy jesteś w wersji 3.0.

  • W przypadku poważnych włamań, zaprojektuj lepsze rozwiązanie jako zespół i zrób to. Jeśli nie możesz naprawić włamania jako zespołu, to nie rozumiesz wystarczająco dobrze systemu. „Poproś dorosłego o pomoc”. Najstarsi ludzie w okolicy mogli to wcześniej widzieć. Spróbuj narysować lub wyodrębnić schemat systemu. Spróbuj narysować lub wyodrębnić przypadki użycia, które są szczególnie hackerskie jako diagramy interakcji. To nie naprawia tego, ale przynajmniej możesz to zobaczyć.

  • Jakie założenia nie są już prawdą, które popchnęły projekt w określonym kierunku? Za tym bałaganem może kryć się małe refaktoryzacja.

  • Jeśli wyjaśnisz, jak działa system (nawet tylko jeden przypadek użycia) i okaże się, że musisz przepraszać podsystem w kółko, to jest problem. Jakie zachowanie uprościłoby resztę systemu (bez względu na to, jak trudne jest wdrożenie w porównaniu z tym, co tam jest). Klasyczny podsystem do przepisania to taki, który zanieczyszcza każdy inny podsystem swoją semantyką operacyjną i implementacją. „Och, musisz zebrać wartości, zanim podasz je do podsystemu„ froo ”, a następnie ponownie je odszyfrujesz, gdy uzyskasz dane wyjściowe z froo. Może wszystkie wartości powinny zostać odczytane, gdy zostaną odczytane przez użytkownika i pamięć, a reszta systemu jest nieprawidłowa? Staje się to bardziej ekscytujące, gdy istnieją dwa lub więcej różnych problemów.

  • Spędź tydzień jako zespół usuwając ostrzeżenia, aby widoczne były prawdziwe problemy.

  • Ponownie sformatuj cały kod do standardu kodowania.

  • Upewnij się, że system kontroli wersji jest powiązany z twoim modułem do śledzenia błędów. Oznacza to, że przyszłe zmiany są miłe i możliwe do rozliczenia, i możesz wypracować DLACZEGO.

  • Zrób trochę archeologii. Znajdź oryginalne dokumenty projektowe i przejrzyj je. Mogą znajdować się na starym komputerze w rogu biura, w opuszczonej przestrzeni biurowej lub w szafce na dokumenty, której nikt nigdy nie otwiera.

  • Opublikuj ponownie dokumenty projektowe na wiki. Pomaga to zinstytucjonalizować wiedzę.

  • Napisz procedury podobne do list kontrolnych dla wydań i kompilacji. To powstrzymuje ludzi od myślenia, dzięki czemu mogą skoncentrować się na rozwiązywaniu problemów. Automatyzuj kompilacje tam, gdzie to możliwe.

  • Spróbuj ciągłej integracji . Im szybciej kompilacja się nie powiedzie, tym mniej czasu projekt może poświęcić na szyny.

  • Jeśli lider zespołu nie robi tych rzeczy, to źle dla firmy.

  • Postaraj się, aby cały nowy kod uzyskał prawidłowe testy jednostkowe ze zmierzonym zasięgiem. Tak więc problem nie może się znacznie pogorszyć.

  • Spróbuj przeprowadzić test jednostkowy niektórych starych bitów, które nie są testowane jednostkowo. Pomaga to zmniejszyć strach przed zmianą.

  • Zautomatyzuj test integracji i regresji, jeśli możesz. Przynajmniej mieć listę kontrolną. Piloci są sprytni i zarabiają dużo, korzystają z list kontrolnych. Spieprzają też dość rzadko.

Tim Williscroft
źródło
4

Przeczytaj, a następnie ponownie przeczytaj Kod ukończony przez Steve'a McConnella. To jest jak biblia dobrego pisania oprogramowania, od wstępnego projektu do pojedynczej linii kodu i wszystkiego pomiędzy. Najbardziej podoba mi się to, że jest on poparty dziesięcioleciami solidnych danych; to nie tylko kolejny najlepszy styl kodowania.

dwenaus
źródło
3

Nazywam to „efektem tajemniczego domu Winchester”. Podobnie jak dom, zaczął się dość prosto, ale z biegiem lat wielu różnych pracowników dodało tak wiele dziwnych funkcji bez ogólnego planu, że nikt tak naprawdę go nie rozumie. Dlaczego te schody prowadzą donikąd i dlaczego te drzwi otwierają się tylko w jedną stronę? Kto wie?

Sposobem ograniczenia tego efektu jest rozpoczęcie od dobrego projektu, który jest wykonany tam, gdzie jest wystarczająco elastyczny, aby poradzić sobie z rozszerzeniem. Zaproponowano już kilka sugestii na ten temat.

Ale często podejmujesz pracę, w której szkody już zostały wyrządzone, a jest już za późno na dobry projekt bez wykonywania kosztownej i potencjalnie ryzykownej przeprojektowania i przepisania. W takich sytuacjach najlepiej jest znaleźć sposób na ograniczenie chaosu, jednocześnie do pewnego stopnia go obejmując. Może to drażnić twoją wrażliwość projektową, że wszystko musi przejść przez ogromną, brzydką, singletonową klasę „manager” lub warstwa dostępu do danych jest ściśle związana z interfejsem użytkownika, ale naucz się sobie z tym radzić. Koduj defensywnie w tych ramach i staraj się spodziewać nieoczekiwanego pojawienia się „duchów” programistów z przeszłości.

jfrankcarr
źródło
2

Refaktoryzacja kodu i testy jednostkowe są całkowicie w porządku. Ale ponieważ ten długo działający projekt wkracza do hacków, oznacza to, że kierownictwo nie kładzie nóg, aby oczyścić zgniliznę. Zespół jest zobowiązany do wprowadzenia hacków, ponieważ ktoś nie przeznacza wystarczających zasobów na szkolenie ludzi i analizowanie problemu / żądania.

Utrzymanie długoterminowego projektu jest tak samo obowiązkiem kierownika projektu, jak indywidualnego programisty.

Ludzie nie wprowadzają hacków, ponieważ im się podoba; są zmuszeni przez okoliczności.

Peter Mortensen
źródło
Zmuszony przez okoliczności? Ludzie wprowadzają hacki, ponieważ (a) nie wiedzą lepiej => potrzebują coachingu, (b) nie widzą większego obrazu => potrzebna komunikacja, dokumentacja i dyscyplina, (c) uważają, że są mądrzejsi => to jest największe przeszkoda do przezwyciężenia (d) wymuszona przez okoliczności => szybkie poprawki są poprawne, gdy są pod presją czasu, ale ktoś musi wziąć odpowiedzialność i wyczyścić kod później. Wszelkie inne „okoliczności” to po prostu BS . Istnieją wyjątki od tej ogólnej zasady, ale najbardziej tak zwane wyjątki oznaczają „leniwy”.
JensG
2

Chcę tylko postawić kwestię nietechniczną i (być może) pragmatyczne podejście.

Jeśli menedżerowi nie zależy na jakości technicznej (zarządzalny kod, prosta architektura, niezawodna infrastruktura itp.), Trudno jest ulepszyć projekt. W takim przypadku konieczne jest wykształcenie wspomnianego menedżera i przekonanie do „zainwestowania” wysiłków w utrzymanie i rozwiązywanie problemów technicznych .

Jeśli marzysz o jakości kodu znalezionej w tych książkach, potrzebujesz również szefa, który się tym martwi.

A jeśli chcesz oswoić „projekt Frankensteina”, oto moje wskazówki:

  • Organizuj i upraszczaj
  • Skróć funkcje
  • Pierwszeństwo ma czytelność nad wydajnością (oczywiście jeśli jest to do zaakceptowania, a niektóre przyrosty wydajności są zbyt nieszczęśliwe, aby można je było utrzymać)

Z mojego doświadczenia wynika, że ​​programowanie jest raczej entropijne niż wschodzące (przynajmniej w popularnym paradygmacie o strukturze imperatywnej). Kiedy ludzie piszą kod „po prostu działają”, ma tendencję do utraty organizacji. Teraz porządkowanie kodu wymaga czasu, czasem znacznie więcej niż sprawienie, by działało.

Oprócz implementacji funkcji i poprawek błędów, nie spiesz się z czyszczeniem kodu.

Eric.Void
źródło
„... projekt jest skazany na zagładę” - z mojego doświadczenia niekoniecznie tak jest. Alternatywą jest wykształcenie wspomnianego menedżera i przekonanie do „zainwestowania” wysiłków w utrzymanie i rozwiązywanie problemów technicznych
komnata
Przepraszam, nie mogłem się powstrzymać przed pisaniem tego, ponieważ miałem już doświadczenie, gdy kierownik zignorował wszystkie moje porady dotyczące długu technicznego. Ale myślę, że masz rację: około rok po tym, jak zrezygnowałem z tego projektu, menedżer stracił całą władzę nad zespołem technicznym z powodu niemożności zarządzania nimi.
Eric.Void
1

Byłem zaskoczony, gdy stwierdziłem, że żadna z licznych odpowiedzi nie uwypukliła oczywistości: spraw, aby oprogramowanie składało się z wielu małych, niezależnych bibliotek. Dzięki wielu małym bibliotekom możesz zbudować duże i złożone oprogramowanie. Jeśli wymagania się zmienią, nie musisz wyrzucać całej bazy kodu ani badać, jak zmodyfikować dużą bazę kodu honking, aby zrobić coś innego niż to, co obecnie robi. Po prostu decydujesz, które z tych bibliotek są nadal odpowiednie po zmianie wymagań i jak je połączyć, aby uzyskać nową funkcjonalność.

Używaj dowolnych technik programowania w tych bibliotekach, które ułatwiają korzystanie z biblioteki. Zauważ, że np. Jakikolwiek język nie zorientowany obiektowo obsługujący wskaźniki funkcji obsługuje faktycznie programowanie obiektowe (OOP). Tak więc, np. W C, możesz zrobić OOP.

Możesz nawet rozważyć udostępnienie tych małych, niezależnych bibliotek między wieloma projektami (podmoduły git są twoim przyjacielem).

Nie trzeba dodawać, że każda mała, niezależna biblioteka powinna być testowana jednostkowo. Jeśli określonej biblioteki nie można przetestować jednostkowo, robisz coś źle.

Jeśli używasz C lub C ++ i nie podoba ci się pomysł posiadania wielu małych plików .so, możesz połączyć wszystkie biblioteki razem w większy plik .so lub alternatywnie możesz wykonać statyczne łączenie. To samo dotyczy Javy, wystarczy zmienić .so na .jar.

juhist
źródło
Z mojego punktu widzenia wydaje się to zbyt teoretyczne. Oczywiście projekty, o których wspomniałem w moim pytaniu, były zbudowane z wielu bibliotek i modułów. Moje ostatnie 26-letnie doświadczenie w tworzeniu oprogramowania było takie, że im starszy projekt otrzymuje początkową organizację
chrmue
0

Prostota: obniż koszty utrzymania większości kodu do zera, aż uzyskasz możliwą do utrzymania liczbę ruchomych części. Kod, którego nigdy nie trzeba zmieniać, nie wiąże się z żadnymi kosztami utrzymania. Polecam dążyć do tego, aby kod naprawdę miał zerowy koszt utrzymania, nie próbując obniżać kosztów w porównaniu z wieloma małymi i wybrednymi iteracjami refaktoryzacji. Spraw, by od razu kosztowało zero .

Okej, wprawdzie to jest o wiele trudniejsze niż się wydaje. Ale nie jest trudno zacząć. Możesz wziąć część bazy kodu, przetestować ją, zbudować ładny interfejs, jeśli projekt interfejsu jest bałaganem, i zacząć rozwijać części bazy kodu, które są niezawodne, stabilne (ponieważ nie ma powodów do zmiany), a jednocześnie kurczenie części, które są zawodne i niestabilne. Podstawy, które wydają się koszmarem do utrzymania, często nie odróżniają ruchomych części, które należy zmienić, od tych, które tego nie robią, ponieważ wszystko jest uważane za zawodne i podatne na zmiany.

Właściwie polecam przejść całą drogę dzielenia organizacji bazy kodu na części „stabilne” i „niestabilne”, przy czym części stabilne to ogromna PITA do przebudowy i zmiany (co jest dobre, ponieważ nie powinny one wymagać zostać zmienione i przebudowane, jeśli naprawdę należą do sekcji „stabilnej”).

To nie jest rozmiar bazy kodu, która utrudnia utrzymanie. To wielkość bazy kodu, którą należy zachować. Jestem zależny od milionów wierszy kodu, ilekroć używam interfejsu API systemu operacyjnego. Ale to nie wpływa na koszty utrzymania mojego produktu, ponieważ nie muszę utrzymywać kodu źródłowego systemu operacyjnego. Po prostu używam kodu i działa. Kod, którego używam i którego nigdy nie muszę utrzymywać, nie wiąże się z żadnymi kosztami utrzymania.

user204677
źródło