Jaka jest odpowiednia równowaga między spójnością kodu a poprawą kodu?

53

Niedawno rozmawiałem z kolegą na temat stylu kodu. Twierdził, że korzystanie z interfejsów API i ogólnych wzorców, których używasz, powinno być możliwie jak najbardziej zbliżone do otaczającego kodu, jeśli nie z bazą kodu jako całością, podobnie jak wygląd kodu (pozycjonowanie nawiasów, wielkie litery itp.) . Na przykład, gdybym dodawał metodę do klasy DAO w języku C #, spróbowałbym użyć LINQ tam, gdzie to właściwe, aby mój kod był czysty i łatwy w utrzymaniu, nawet jeśli żadna z innych metod w tej klasie go nie używała. Jednak mój kolega argumentowałby, że nie powinienem go używać w tym przypadku, ponieważ byłoby to sprzeczne z istniejącym stylem tej klasy i dlatego trudniejsze do zrozumienia.

Na początku jego pozycja była dość ekstremalna, ale po przemyśleniu jej przez chwilę zaczynam rozumieć jego rację. W hipotetycznym przykładzie LINQ być może ta klasa go nie zawiera, ponieważ moi koledzy nie znają LINQ? Jeśli tak, to czy mój kod nie byłby łatwiejszy w utrzymaniu dla innych programistów, gdybym go nie używał? Z drugiej strony, jeśli naprawdę wierzę, że zastosowanie takiej techniki zapewni czystszy kod, to czy nie powinienem go używać, nawet jeśli drastycznie różni się on od otaczającego kodu?

Myślę, że sedno argumentu mojego kolegi polega na tym, że jeśli wszyscy zaczniemy wdrażać podobną funkcjonalność w bazie kodu na różne sposoby i każdy z nas będzie uważał, że nasza droga jest „najlepsza”, to ostatecznie cały kod staje się coraz trudniejszy rozumieć. Jednak w tej chwili nadal uważam, że jeśli będziemy zbyt ślepo podążać za istniejącym kodem, wówczas jakość z czasem będzie powoli gnić.

W jakim stopniu wzorce są częścią stylu kodu i gdzie powinniśmy wytyczyć granicę między zachowaniem spójności a wprowadzaniem ulepszeń?

Robert Johnson
źródło
14
Jak więc poprawiłaby się jakość kodu, gdyby wszyscy ograniczali się do „złej” drogi?
Izkata
Kiedy piszesz LINQ, nie masz na myśli LINQ na SQL? Jeśli tak, to mogę się zgodzić z twoim przyjacielem. Jeśli mówisz o LINQ, to nie zgadzam się z nim. W dzisiejszych czasach wszyscy muszą znać LINQ. To nie jest ich wybór. Zapłaci się za znajomość.
Piotr Perak
@Peri Miałem na myśli tylko podstawowy LINQ - wydaje mi się, że mój przykład powinien dotyczyć pracy z IEnumerables zamiast DAO.
Robert Johnson
2
ICYMI - komiks Dilberta na ten temat: dilbert.com/strips/2013-09-21
Jim Bethancourt

Odpowiedzi:

53

Aby udzielić bardziej ogólnej odpowiedzi:

W takim przypadku istnieją dwie „najlepsze praktyki” programowania, które są sobie przeciwne: spójność kodu jest ważna, ale tak samo jak wybór najlepszej możliwej metody wykonania zadania. Nie ma jednej poprawnej odpowiedzi na ten dylemat; zależy to od kilku czynników:

  • Jak korzystny jest „właściwy” sposób?

    • Czasami nowa i ulepszona najlepsza praktyka radykalnie zwiększy wydajność, wyeliminuje błędy, będzie znacznie łatwiejsza do zaprogramowania itp. W takim przypadku skłaniałbym się mocno do zastosowania nowej metody. Z drugiej strony „właściwy sposób” może być niewiele więcej niż cukrem syntaktycznym lub uzgodnioną idiomatyczną metodą robienia czegoś, co nie jest tak naprawdę lepsze. W takim przypadku spójność kodu jest prawdopodobnie ważniejsza.
  • Jak duży problem spowodowałby niespójność?

    • W jakim stopniu nowy kod jest powiązany ze starszym kodem? Czy Twój nowy kod jest częścią biblioteki? Czy tworzy obiekt przekazywany do wielu części programu? W takich przypadkach spójność jest bardzo ważna. Korzystanie z nowego interfejsu API lub ogólnie nowego sposobu robienia rzeczy może powodować subtelnie różne wyniki, które łamią założenia w innym miejscu programu. Z drugiej strony , jeśli piszesz dość izolowany fragment kodu, mniej prawdopodobne jest, że niespójność będzie stanowić problem.
    • Jak duży i jak dojrzały jest Twój kod? Ilu programistów musi to zrozumieć i pracować nad tym? Uzgodnione, spójne standardy są znacznie ważniejsze w przypadku większych projektów.
    • Czy kod musi działać w starszych środowiskach, które mogą nie obsługiwać najnowszych funkcji?

W oparciu o równowagę tych problemów musisz dokonać właściwego wyboru trasy. Osobiście uważam, że spójność jest niewielka ze względu na spójność i wolałbym stosować najnowsze, najlepsze metody, chyba że będzie to wiązało się ze znacznymi kosztami.

Oczywiście istnieje trzecia opcja: przepisanie istniejącego kodu, aby korzystał z najlepszych metod i był spójny. Są chwile, kiedy jest to konieczne, ale wiąże się to z wysokimi kosztami.

samthebrand
źródło
7
Bardzo dobra i zrównoważona odpowiedź (+1). Z mojego doświadczenia wynika, że ​​zespół może być bardziej produktywny, jeśli wszyscy zgodzą się na stosunkowo niewielki zestaw dobrze rozumianych idiomów niż wtedy, gdy każdy członek zespołu może swobodnie używać nowych i różnych idiomów, które inni członkowie zespołu mogą uznać za trudne do zrozumienia. Jedna drobna kwestia dotycząca stwierdzenia „przepisanie istniejącego kodu, tak aby korzystał z najnowszych praktyk i był spójny”: „najnowsze” nie oznacza automatycznie „lepszego”. Zawsze trzeba decydować od przypadku do przypadku.
Giorgio
1
@Giorgio, dzięki za opinie; Poprawiłem język ostatniego punktu.
6
Dobra odpowiedź i dobry komentarz od Giorgio. Być może warto wziąć pod uwagę, że w tym przykładzie, w zależności od zespołu / kultury, możliwe jest skoordynowane przyjęcie zespołu, a nie rozpoczęcie samemu. Zmniejsza to część ryzyka związanego z konserwacją (ponieważ wszyscy są na pokładzie). (tzn. kładą obecnie większy nacisk na rzeczywiste umiejętności zespołu niż na kod, który napisali w przeszłości.)
Matt
+1. Uzgodniona, dokładna i wyważona odpowiedź - dobre uwzględnienie różnych scenariuszy. Znowu dobry komentarz od Giorgio.
Robert Johnson
1
Jeśli chodzi o przyjęcie nowych idiomów, technologie: Uważam rozpoczęcie nowego projektu za dobry punkt do wprowadzenia nowych rzeczy. Następnie, w trakcie trwania całego projektu, styl kodu powinien być możliwie jak najbardziej spójny. Niedawno musiałem wykopać pięcioletni kod i bardzo się cieszyłem, że udało mi się znaleźć ten kod, ponieważ cały zespół przestrzegał wspólnej architektury i zestawu uzgodnionych przez nas idiomów (nie bez walki! ) na początku projektu.
Giorgio
26

Zachowanie spójności ma niewielką wartość z mojej perspektywy; ciągłe wprowadzanie ulepszeń jest koniecznością.

Pozycja twojego kolegi naprawdę hamuje innowacje. Argument spójności wprowadza cię w sytuację, w której możesz użyć, na przykład LINQ, tylko jeśli migrujesz cały kod, aby użyć LINQ. No cóż, nie mamy na to czasu, prawda?

Wolałbym mieć niespójność, gdy jakiś kod nadal działa foreachna ArrayLists, a inne części używają LINQ IEnumerable<T>, zamiast trzymać się najstarszego sposobu robienia rzeczy do końca czasu.

Obowiązkiem współpracowników jest pozostawanie aktualnym i uczenie się nowych sposobów działania.

Joppe
źródło
3
„Obowiązkiem współpracowników jest pozostawanie aktualnym i uczenie się nowych sposobów robienia rzeczy.”: Możliwe jest użycie nowych idiomów i bibliotek w nowym kodzie (nowe moduły) i zachowanie spójnego stylu w starszym kodzie.
Giorgio
8

Spójność interfejsu API jest bardzo ważna, zarówno dla publicznych, jak i wewnętrznych interfejsów API.

Spójność formatowania kodu jest ważna i idealnie powinna być egzekwowana przez narzędzie do automatycznego formatowania z tymi samymi regułami formatowania dla wszystkich. Ułatwia życie ze współdzieloną bazą kodu kontrolowaną wersją.

Konwencje nazewnictwa powinny być spójne, również w przypadku zmiennych lokalnych itp.

Narzędzia do analizy statycznej powinny być stosowane w celu egzekwowania pewnych innych konwencji i dobrych praktyk (ale nie ślepo przestrzegaj domyślnych ustawień takiego narzędzia, wartości domyślne mogą czasem graniczyć z obłędem), chociaż jeśli coś tego wymaga, nie bój się wyłączyć niektórych tymczasowo sprawdź (zwykle z dyrektywą w komentarzu), czy możesz to uzasadnić.

To, co dzieje się w funkcjach / metodach , oprócz tego, co wymieniono powyżej, nie musi być w żaden sposób spójne, o ile sam jest dobrym kodem . Dobry, zrozumiały, dobrze skomentowany kod jest bardzo ważny, ale potem, jeśli kompilator i narzędzia analizy statycznej uważają, że jest to spójny kod, to wystarczająco spójny.


Jeśli chodzi o nowe funkcje językowe, takie jak LINQ (cóż, to nie jest dokładnie nowe), jedyną rzeczą, którą należy wziąć pod uwagę, jest to, czy nowa wystarczająca wersja języka / bibliotek będzie używana wszędzie tam, gdzie będzie używany kod? W razie wątpliwości trzymaj się funkcji, o których wiadomo, że są kompatybilne. To oczywiście nie przeszkadza w rozpowszechnianiu aktualizacji wersji w całym systemie, dzięki czemu możesz zacząć korzystać z nowych fajnych rzeczy.

I każdy programista pracujący z kodem powinien być na bieżąco, więc każdy programista .NET powinien znać LINQ, a jeśli nie, powinien być zmuszony do nauki, dla własnego dobra (nigdy nie wiadomo, kiedy będziesz szukał nowego praca w tym biznesie, z tego czy innego powodu).

hyde
źródło
6

Odpowiedź na to pytanie jest prosta.

Spójność ma ogromne znaczenie.

ale zawiera zastrzeżenie ...

Ty i twój współpracownik prawdopodobnie macie obsesję na punkcie niewłaściwej konsystencji

Realizacje są jednorazowe. Można je całkowicie przebudować z różnym stopniem łatwości, w zależności od jakości i kompleksowości zestawu testowego. Martwiąc się o rzeczy takie jak „Czy to powinna być właściwość?”, „Czy cienki kod nie powinien używać LINQ zamiast konstrukcji niższego poziomu?” ma wątpliwą wartość. Trudno jest powiązać jakiekolwiek pomiary z wartością spójności na poziomie wdrożenia. O wiele lepszym pytaniem na tym poziomie jest „Czy ten kod działa zgodnie z reklamą?”. Spójność implementacji TL; DR polega na włączeniu hobgoblinów przez „małe umysły”.

Dlaczego spójność nie jest tutaj tak ważna? Wdrożenia zwykle mają niewielką liczbę współpracowników. Większość metod jest zapisywana i nigdy więcej nie dotykana. Z pozostałego kodu liczba metod, które mają dwóch autorów, prawie na pewno większość. Ten wzór trwa w nieskończoność . W tym kontekście spójność po prostu nie jest tak ważna. Jeśli okres przechowywania kodu jest dość mały ( kilka lat ), korzyści wynikające z agresywnej spójności są prawdopodobnie nieistotne.

Nie oznacza to, że powinieneś zwariować w swoich implementacjach. Mówi raczej, że ładny, czysty i prosty projekt będzie rzędami wielkości bardziej wartościowymi dla twojego hipotetycznego przyszłego opiekuna niż głupia metoda konsystencji płyty kotła metodą. To prowadzi nas do prawdziwego punktu ...

Interfejsy API nie są jednorazowe.

To wszystko poziom kodu API, usługi sieciowe, SDK itp. Muszą one, muszą, MUSI być spójne. Wzrost wydajności z tej różnorodności konsystencji jest ogromny z wielu powodów:

Testy integracyjne:

Jeśli utrzymasz spójność interfejsu API, możesz tworzyć pakiety testów integracyjnych. Dzięki temu programiści mogą swobodnie wymieniać szczegóły implementacji i uzyskiwać natychmiastową weryfikację. Chcesz zamienić swoje badziewie na LINQ? Czy działają testy integracyjne? Zapewnia również sprawdzanie poprawności podczas przygotowań do przejścia do produkcji. Ponieważ komputery są szybkie, pojedynczy laptop może wykonać pracę tysiąca testerów wykonujących przyziemne zadania. Jest to równoznaczne ze znacznym zwiększeniem liczby pracowników w organizacji.

Wydajność

Gdy interfejsy API są spójne, możesz zgadywać, jak korzystać z interfejsu API, po prostu postępując zgodnie z tym, czego się nauczyłeś na temat używania innych części interfejsu API. Wynika to z faktu, że interfejs API zapewnia naturalny, spójny „wygląd i działanie”. Oznacza to, że Twój klient spędza mniej czasu na przeglądaniu dokumentacji. Wbudowanie jest łatwiejsze i tańsze. Osoby, które opracowały interfejs API, zadają mniej pytań. Spójność sprawia, że ​​wszyscy są zwycięzcami

Dlaczego spójność jest ważna w tym scenariuszu? Ponieważ interfejsy API mają dokładnie odwrotny problem z implementacjami. Liczba osób korzystających z nich jest zwykle znacznie większa niż liczba osób przyczyniających się do ich implementacji. Niewielkie zyski z niewielkiej spójności są zwielokrotniane, a koszty utrzymania tej spójności są amortyzowane.

Wniosek

Konsystencja jest droga. Na pierwszy rzut oka obniża produktywność. Ogranicza programistów i utrudnia ich życie. Ogranicza to sposób, w jaki mogą rozwiązać problem, niekiedy zmuszając go do rozwiązania go w nieoptymalny sposób. Często dzieje się tak z powodów, których nie rozumieją, są źle rozumiani lub nie są wtajemniczeni (umowy, większe zasady organizacyjne lub międzyorganizacyjne).

Raymond Hettinger napisał kilka doskonałych punktów w swoim przemówieniu Pycon 2015 na temat używania przewodnika po stylu PEP8 dla zespołów programistów pythonowych. Pokazał, że obsesja na punkcie spójności stylistycznej fragmentu kodu spowodowała, że ​​recenzenci kodu stracili poważne wady logiczne i projektowe. Jego hipotezę można streścić, ponieważ znalezienie niespójności stylistycznych jest łatwe; ustalenie prawdziwej jakości fragmentu kodu jest trudne

Punkt tutaj jest krytyczny. Określ, gdzie spójność jest ważna i chroń ją agresywnie. Tam, gdzie to nie jest ważne, nie marnuj czasu. Jeśli nie możesz zapewnić obiektywnego sposobu pomiaru wartości spójności (w powyższych przypadkach „efektywne zatrudnienie”, koszt jako funkcja wydajności) i nie możesz wykazać, że zwroty są znaczące, prawdopodobnie robisz krzywdę Twoja organizacja.

nsfyn55
źródło
4

Nie znasz LINQ? Jeśli tak, to czy mój kod nie byłby łatwiejszy w utrzymaniu dla innych programistów, gdybym go nie używał?

Język C # wciąż się rozwija. Gdyby ludzie nie nauczyli się zmian z C # 1, straciliby:

  • Generics
  • Częściowe
  • Anonimowe metody
  • Iteratory
  • Typy zerowalne
  • Auto-właściwości
  • Anonimowe typy
  • Metody rozszerzenia
  • Molwa
  • Lambdas
  • Metody asynchroniczne

To tylko niewielki wybór typowych funkcji znalezionych w artykule w Wikipedii. Chodzi mi o to, że jeśli programiści się nie nauczą, baza kodów pozostanie w próżni. Można mieć nadzieję, że Twoi programiści ciągle się ulepszają, a baza kodu ewoluuje, obsługiwana przez kompletny zestaw testów. Czy pamiętasz, jak źle było z .NET 1 w przypadku ręcznego wdrażania właściwości.

Linq ułatwia życie. Użyj go tam, gdzie możesz. Możesz motywować członków swojego zespołu.

W jakim stopniu wzorce są częścią stylu kodu i gdzie powinniśmy wytyczyć granicę między zachowaniem spójności a wprowadzaniem ulepszeń?

Ulepszenia są stopniowe. Dla mnie nie ma sensu utrzymywanie kodu w starym stylu, który jest mniej czytelny i potencjalnie mniej konserwowalny. Członkowie twojego zespołu powinni przynajmniej być w stanie ustalić, co się dzieje, nawet jeśli nie mogą tego napisać.

Sam
źródło
2
Z całego serca zgadzam się z tym, co mówisz, ale moje pytanie dotyczy mniej nowych funkcji językowych i odpowiedzialności programistów za ich nauczenie, ale raczej bardziej ogólne pytanie dotyczące rozwiązywania podobnych problemów na różne sposoby w stosunkowo niewielkim obszarze bazy kodu. Wspomniałem o LINQ, ponieważ jest to dobry przykład zastosowania innego sposobu rozwiązania problemu. Mój kolega chętnie korzysta z nowych funkcji językowych, ale tylko wtedy, gdy nie są sprzeczne z kodem, nad którym pracuje.
Robert Johnson
@RobertJohnson Chciałbym zwrócić uwagę na twojego kolegę, że łatwość konserwacji przeważa nad spójnością, więc nawet jeśli coś „stoi w sprzeczności z zasadą” istniejącego kodu, jeśli jest łatwiejsze do utrzymania, powinieneś to zrobić. Wątpię, aby korzystanie ze starego pobytu DAO było łatwiejsze w utrzymaniu niż Linq.
Andy,
3

Powinieneś być spójny, ale niekoniecznie ze starym kodem. Oznacza to, że Twój zespół powinien uzgodnić właściwy sposób zrobienia czegoś i używać go w ten sposób, gdy zostanie napisany nowy kod lub zostaną wprowadzone istotne zmiany.

W ten sposób czerpiesz większość korzyści wynikających ze spójności (w szczególności nie będzie miało znaczenia, kto pisze kod), ale wciąż możesz poprawić, jeśli zespół dostrzeże wyraźną korzyść, robiąc rzeczy w inny sposób.

W idealnym świecie przepisalibyśmy cały stary kod, aby był zgodny z nowym właściwym sposobem. Chociaż nie jest to ekonomicznie opłacalne, powinno mieć sens techniczny (w przeciwnym razie nowy sposób nie jest lepszym sposobem), a twoim długoterminowym planem powinno być jego ulepszenie. Jeśli to nie jest tego warte, nie przejmuj się nowym sposobem. Innymi słowy, nowy sposób na uznanie go za właściwy musi mieć wyraźną przewagę.

meriton - podczas strajku
źródło
1

Myślę, że ważne jest przestrzeganie stylu kodu w projekcie np. konwencje nazewnictwa, wcięcia itp.

Nie zgadzam się, że powinieneś być ograniczony w używaniu nowych konstrukcji językowych lub wzorców projektowych tylko dlatego, że twoi koledzy ich nie rozumieją lub nie byli wcześniej wykorzystywani w projekcie.

Oczywiście te rzeczy powinny mieć dobre powody, aby z nich korzystać np. wydajność lub zwięzłość, w stosownych przypadkach.

ozz
źródło
Jaki był minus?
ozz
0

Byłbym bardzo ostrożny na ślepo podążając za obecnymi wzorami projektowymi. Oczywiście, gdy nie ma znaczących wad, zawsze należy próbować stosować podobne wzorce w całej bazie kodu.

Jednak zbyt łatwo jest wejść w sytuacje, w których większość programistów uważa, że ​​„najlepszą praktyką” jest podążanie za złymi wzorcami prowadzącymi do sytuacji, w których dobrzy programiści piszą zły, niemożliwy do utrzymania kod.

Z drugiej strony spójność jest ważna. Kiedy mamy do czynienia z olbrzymimi podstawami kodu, niezwykle przydatne jest staranie się przestrzegać podobnych wzorców, aby nawet jeśli programiści nie przeczytali każdego cala podstawy kodu, wiedzą, czego się spodziewać.

Dlatego uważam, że najlepiej jest ustalić i zaktualizować wytyczne dotyczące tego, jakie wzorce powinni stosować programiści, jeśli to możliwe. W ten sposób każdy nowy kod jest zgodny z innym nowym kodem.

użytkownik606723
źródło
-1

Odbywamy regularne, dość nieformalne spotkania techniczne, które każdy z nas może prowadzić. Jeśli znajdziemy nową technikę, prezentujemy ją na spotkaniu. Na ogół masz poczucie, jak dobrze pomysł jest akceptowany i rozumiany przez twoich kolegów. Pomaga ci to zdecydować, czy mądrze jest włączyć go do twojego kodu, pomaga innym rozszyfrować go, jeśli to zrobisz, a także ustanawia osobę „idź do”, jeśli inni potrzebują pomocy w tym stylu. Gdyby nikt nie wymyślił koła na nowo, nadal ciągnęlibyśmy rzeczy po kłodach.

Mrówka
źródło
Po co głosowano?
Ant
Nie byłem spadkobiercą, ale tak naprawdę nie wydaje się, aby była ukierunkowana na to pytanie. Jest to proces zespołowy, który można zastosować do czegokolwiek, zamiast dyskusji na temat problemów związanych ze stylem kodowania. Czy nie oznacza to raczej „wynalazł koło”, a nie „wynalazł koło”?
Przypuszczam, że to semantyka, czy kłoda jest rodzajem koła, czy nie, ale obraca się, zmniejsza tarcie, ma powierzchnię, która styka się z ziemią i jest okrągła. Jestem pewien, że istnieją lepsze przykłady tego, co mam na myśli - pozostawię to jako ćwiczenie dla czytelnika.
Ant