Jak usprawiedliwić pisanie więcej kodu, stosując praktyki czystego kodu?

106

Notka moderatora Na
to pytanie wysłano już siedemnaście odpowiedzi . Zanim opublikujesz nową odpowiedź, przeczytaj istniejące odpowiedzi i upewnij się, że Twój punkt widzenia nie jest odpowiednio uwzględniony.

Postępuję zgodnie z niektórymi praktykami zalecanymi w książce Roberta Martina „Czysty kod”, szczególnie te, które dotyczą tego rodzaju oprogramowania, z którym pracuję, i te, które mają dla mnie sens (nie traktuję tego jako dogmatu) .

Jedną z efektów ubocznych, którą zauważyłem, jest jednak to, że „czysty” kod, który piszę, to więcej kodu, niż gdybym nie przestrzegał pewnych praktyk. Konkretne praktyki, które do tego prowadzą, to:

  • Kapsułkowanie warunkowe

Więc zamiast

if(contact.email != null && contact.emails.contains('@')

Mógłbym napisać taką małą metodę

private Boolean isEmailValid(String email){...}
  • Zastępowanie komentarza wbudowanego inną metodą prywatną, tak aby nazwa metody opisywała się, zamiast umieszczania na nim komentarza wstawianego
  • Klasa powinna mieć tylko jeden powód do zmiany

I kilka innych. Chodzi o to, że to, co może być metodą 30 wierszy, ostatecznie staje się klasą, ze względu na małe metody, które zastępują komentarze i zawierają warunki warunkowe itp. Gdy zdasz sobie sprawę, że masz tak wiele metod, wówczas „sensowne” jest umieść całą funkcjonalność w jednej klasie, a tak naprawdę powinna to być metoda.

Zdaję sobie sprawę, że każda ekstremalna praktyka może być szkodliwa.

Konkretne pytanie, na które szukam odpowiedzi, brzmi:

Czy jest to akceptowalny produkt uboczny pisania czystego kodu? Jeśli tak, to jakich argumentów mogę użyć, aby uzasadnić fakt, że napisano więcej LOC?

Organizacja nie jest specjalnie zainteresowana większą liczbą LOC, ale więcej LOC może skutkować bardzo dużymi klasami (to znowu może zostać zastąpione długą metodą bez wielu funkcji pomocniczych, które kiedyś były używane, ze względu na czytelność).

Kiedy zobaczysz klasę, która jest wystarczająco duża, sprawia wrażenie, że klasa jest wystarczająco zajęta i że jej odpowiedzialność została zakończona. Możesz zatem stworzyć więcej klas, aby osiągnąć inne funkcje. W rezultacie powstaje wiele klas, z których każda robi „jedną rzecz” za pomocą wielu małych metod pomocniczych.

TO jest szczególny problem ... klasy te mogą być pojedynczą klasą, która wciąż osiąga „jedną rzecz”, bez pomocy wielu małych metod. Może to być pojedyncza klasa z 3 lub 4 metodami i pewnymi komentarzami.

CommonCoreTawan
źródło
98
Jeśli Twoja organizacja używa tylko LOC jako miernika dla twoich baz kodu, uzasadnienie czystego kodu jest beznadziejne.
Kilian Foth
24
Jeśli twoim celem jest łatwość utrzymania, LOC nie jest najlepszym miernikiem do oceny - jest jednym z nich, ale jest o wiele więcej do rozważenia niż tylko krótkie.
Zibbobz
29
To nie jest odpowiedź, ale należy zwrócić uwagę: istnieje cała społeczność związana z pisaniem kodu z jak najmniejszą liczbą wierszy / symboli. codegolf.stackexchange.com Można argumentować, że większość odpowiedzi nie jest tak czytelna, jak mogłyby być.
Antitheos
14
Poznaj przyczyny każdej najlepszej praktyki, a nie tylko zasady. Przestrzeganie zasad bez uzasadnienia to kult Cargo. Każda reguła ma swój własny powód.
Gherman
9
Tak na marginesie, i używając twojego przykładu, czasami wypychanie rzeczy do metod sprawi, że pomyślisz: „Może jest funkcja biblioteki, która może to zrobić”. Na przykład, aby sprawdzić poprawność adresu e-mail, możesz utworzyć System.Net.Mail.MailAddress, który zweryfikuje go dla Ciebie. Następnie możesz (miejmy nadzieję) zaufać autorowi tej biblioteki, aby poprawnie ją wykonać. Oznacza to, że twoja baza kodu będzie zawierała więcej abstrakcji i zmniejszy się.
Gregory Currie

Odpowiedzi:

130

... jesteśmy bardzo małym zespołem wspierającym stosunkowo dużą i nieudokumentowaną bazę kodu (którą odziedziczyliśmy), więc niektórzy programiści / menedżerowie widzą wartość w pisaniu mniejszej ilości kodu, aby zrobić rzeczy, abyśmy mieli mniej kodu do utrzymania

Ci ludzie poprawnie coś zidentyfikowali: chcą, aby kod był łatwiejszy do utrzymania. Tam, gdzie popełnili błąd, zakłada się, że im mniej kodu, tym łatwiej go utrzymać.

Aby kod był łatwy w utrzymaniu, musi być łatwy do zmiany. Zdecydowanie najłatwiejszym sposobem na uzyskanie łatwego do zmiany kodu jest posiadanie pełnego zestawu zautomatyzowanych testów, które zawiodą, jeśli twoja zmiana jest przełomowa. Testy są kodem, więc ich napisanie powiększy bazę kodu. I to jest dobra rzecz.

Po drugie, aby dowiedzieć się, co należy zmienić, kod musi być łatwy do odczytania i uzasadnienia. Bardzo zwięzły kod, zmniejszony tylko po to, aby utrzymać odliczanie linii, jest bardzo mało czytelny. Oczywiście trzeba znaleźć kompromis, ponieważ czytanie dłuższego kodu zajmie więcej czasu. Ale jeśli łatwiej to zrozumieć, warto. Jeśli nie oferuje tej korzyści, wówczas ta gadatliwość przestaje być korzyścią. Ale jeśli dłuższy kod poprawia czytelność, to znowu dobrze.

David Arno
źródło
27
„Zdecydowanie najłatwiejszym sposobem na uzyskanie łatwego do zmiany kodu jest posiadanie pełnego zestawu zautomatyzowanych testów, które zakończą się niepowodzeniem, jeśli twoja zmiana jest przełomowa”. To po prostu nieprawda. Testy wymagają dodatkowej pracy przy każdej zmianie behawioralnej, ponieważ testy również wymagają zmiany , jest to zgodne z projektem i wielu twierdzi, że zmiana jest bezpieczniejsza, ale koniecznie utrudnia zmiany.
Jack Aidley
63
Jasne, ale czas stracony na utrzymanie tych testów jest mniejszy niż czas, w którym straciłbyś diagnozowanie i naprawianie błędów, którym zapobiegają testy.
MetaFight
29
@JackAidley, konieczność zmiany testów wraz z kodem może sprawiać wrażenie większej pracy, ale tylko wtedy, gdy zignorujemy trudne do znalezienia błędy, które wprowadzą zmiany w nieprzetestowanym kodzie i które często nie zostaną znalezione przed wysyłką . Ten ostatni oferuje jedynie iluzję mniejszej pracy.
David Arno
31
@JackAidley, całkowicie się z tobą nie zgadzam. Testy ułatwiają zmianę kodu. Przyznaję jednak, że źle zaprojektowany kod, który jest zbyt ściśle powiązany, a zatem ściśle powiązany z testami, może być trudny do zmiany, ale dobrze skonstruowany, dobrze przetestowany kod jest łatwy do zmiany z mojego doświadczenia.
David Arno
22
@JackAidley Możesz dużo refaktoryzować bez zmiany interfejsu API lub interfejsu. Oznacza to, że możesz oszaleć podczas modyfikowania kodu bez konieczności zmiany jednej linii w testach jednostkowych lub funkcjonalnych. To znaczy, jeśli twoje testy nie testują konkretnej implementacji.
Eric Duminil
155

Tak, jest to akceptowalny produkt uboczny, a uzasadnieniem jest to, że jest on teraz tak skonstruowany, że nie musisz czytać większości kodu przez większość czasu. Zamiast czytać funkcję 30-liniową za każdym razem, gdy dokonujesz zmiany, czytasz funkcję 5-liniową, aby uzyskać ogólny przepływ, a może kilka funkcji pomocniczych, jeśli twoja zmiana dotknie tego obszaru. Jeśli twoja nowa „dodatkowa” klasa zostanie wywołana EmailValidatori wiesz, że twój problem nie dotyczy sprawdzania poprawności wiadomości e-mail, możesz całkowicie pominąć jej czytanie.

Łatwiej jest również ponownie używać mniejszych elementów, co zwykle zmniejsza liczbę linii w całym programie. EmailValidatorMoże być stosowany w każdym miejscu. Niektóre wiersze kodu, które sprawdzają poprawność wiadomości e-mail, ale są usuwane razem z kodem dostępu do bazy danych, nie mogą być ponownie użyte.

A następnie zastanów się, co należy zrobić, jeśli zasady sprawdzania poprawności wiadomości e-mail będą kiedykolwiek musiały zostać zmienione - co wolisz: jedna znana lokalizacja; lub wiele lokalizacji, być może brakuje kilku?

Karl Bielefeldt
źródło
10
zdecydowanie lepsza odpowiedź niż męczące „testowanie jednostkowe rozwiązuje wszystkie problemy”
Dirk Boer
13
Ta odpowiedź uderza w kluczową kwestię, której wujek Bob i przyjaciele zawsze wydają się omijać - refaktoryzacja na małe metody pomaga tylko wtedy, gdy nie musisz czytać wszystkich małych metod, aby dowiedzieć się, co robi twój kod. Tworzenie osobnej klasy do sprawdzania poprawności adresów e-mail jest rozsądne. Wciąganie kodu iterations < _maxIterationsdo metody o nazwie ShouldContinueToIteratejest głupie .
BJ Myers
4
@DavidArno: "być użytecznym"! = "Rozwiązuje wszystkie problemy"
Christian Hackl
2
@DavidArno: Kiedy ktoś narzeka na ludzi sugerujących, że testy jednostkowe „rozwiązują wszystkie problemy”, to oczywiście mają na myśli osoby, które sugerują, że testy jednostkowe rozwiązują lub przynajmniej przyczyniają się do rozwiązania prawie wszystkich problemów w inżynierii oprogramowania. Myślę, że nikt nie oskarża nikogo o sugerowanie testów jednostkowych jako sposobu zakończenia wojny, biedy i chorób. Innym sposobem na wyrażenie tego jest to, że skrajne przesadzanie testów jednostkowych w wielu odpowiedziach, nie tylko na to pytanie, ale ogólnie na SE, jest krytykowane (słusznie).
Christian Hackl
2
Cześć @DavidArno, mój komentarz był wyraźnie hiperbolą, a nie słuchem;) Dla mnie jest tak: pytam, jak naprawić mój samochód, a ludzie religijni przyjeżdżają i mówią mi, że powinienem prowadzić mniej grzeszne życie. Teoretycznie coś warte omówienia, ale tak naprawdę nie pomaga mi to w poprawianiu samochodów.
Dirk Boer
34

Bill Gates słynął z powiedzenia: „Mierzenie postępu programowania liniami kodu jest jak mierzenie postępu budowy samolotu według masy”.

Pokornie zgadzam się z tym sentymentem. Nie oznacza to, że program powinien dążyć do większej lub mniejszej liczby wierszy kodu, ale nie jest to ostatecznie to, co liczy się do stworzenia działającego i działającego programu. Pomaga pamiętać, że ostatecznie powodem dodania dodatkowych wierszy kodu jest to, że jest on teoretycznie bardziej czytelny w ten sposób.

Można mieć spory co do tego, czy konkretna zmiana jest bardziej lub mniej czytelna, ale nie sądzę, abyś był w błędzie, wprowadzając zmiany w swoim programie, ponieważ tak myślisz, czyniąc to bardziej czytelnym. Na przykład tworzenie an isEmailValidmoże być uważane za zbędne i niepotrzebne, zwłaszcza jeśli zostanie wywołane dokładnie przez klasę, która go definiuje. Wolałbym jednak zobaczyć isEmailValidwarunek niż ciąg warunków AND, w którym muszę określić, co sprawdza każdy warunek i dlaczego jest sprawdzany.

Problemem jest tworzenie isEmailValidmetody, która ma skutki uboczne lub sprawdzanie rzeczy innych niż e-mail, ponieważ jest to gorsze niż zwykłe wypisywanie wszystkich. Jest gorzej, ponieważ wprowadza w błąd i mogę z tego powodu przegapić błąd.

Chociaż oczywiście nie robisz tego w tym przypadku, zachęcam więc do kontynuowania. Zawsze powinieneś zadać sobie pytanie, czy dokonując zmiany, łatwiej jest ją przeczytać, a jeśli tak, to zrób to!

Neil
źródło
1
Ważna jest jednak waga samolotu. A podczas projektowania oczekiwana waga jest ściśle monitorowana. Nie jako oznaka postępu, ale jako ograniczenie. Monitorowanie linii kodu sugeruje, że im więcej tym lepiej, podczas gdy w konstrukcji samolotu lepsza jest mniejsza waga. Więc myślę, że pan Gates mógł wybrać lepszą ilustrację dla swojej tezy.
jos
21
@jos w konkretnym zespole OP, z którym współpracuje, wydaje się, że mniej LOC jest uważane za „lepsze”. Chodzi o to, że Bill Gates miał na myśli, że LOC nie jest związany z postępem w żaden znaczący sposób, tak jak waga konstrukcji samolotu nie jest związana z postępem w znaczący sposób. Samolot w budowie może stosunkowo szybko osiągnąć 95% masy końcowej, ale byłby to po prostu pusta skorupa bez żadnych systemów sterowania, nie jest w 95% kompletna. To samo w oprogramowaniu, jeśli program ma 100 000 wierszy kodu, nie oznacza to, że każde 1000 wierszy zapewnia 1% funkcjonalności.
Mr.Mindor
7
Monitorowanie postępów to trudne zadanie, prawda? Biedni menedżerowie.
jos
@ jos: również w kodzie lepiej jest mieć mniej linii dla tej samej funkcjonalności, jeśli wszystko inne jest równe.
RemcoGerlich
@jos Przeczytaj uważnie. Gates nie mówi nic o tym, czy waga jest ważną miarą dla samego samolotu. Mówi, że waga jest okropnym miernikiem postępu budowy samolotu. Po tym wszystkim, gdy tylko cały kadłub zostanie wyrzucony na ziemię, robisz to w zasadzie, ponieważ prawdopodobnie stanowi to 9x% masy całego samolotu.
Voo
23

więc niektórzy programiści / menedżerowie widzą wartość, pisząc mniej kodu, aby zrobić to, abyśmy mieli mniej kodu do utrzymania

Jest to kwestia utraty wzroku na rzeczywistym celu.

Liczy się obniżenie godzin spędzonych na rozwoju . Jest to mierzone czasem (lub równoważnym wysiłkiem), a nie liniami kodu.
To tak, jakby powiedzieć, że producenci samochodów powinni budować samochody przy użyciu mniejszej liczby śrub, ponieważ włożenie każdej śruby zajmuje niezerową ilość czasu. Chociaż jest to pedantycznie poprawne, wartość rynkowa samochodu nie jest określona przez liczbę śrub lub nie ma. Przede wszystkim samochód musi być wydajny, bezpieczny i łatwy w utrzymaniu.

Reszta odpowiedzi to przykłady tego, jak czysty kod może prowadzić do oszczędności czasu.


Wycięcie lasu

Weź aplikację (A), która nie ma logowania. Teraz utwórz aplikację B, która jest tą samą aplikacją A, ale z logowaniem. B zawsze będzie zawierało więcej wierszy kodu, dlatego musisz napisać więcej kodu.

Ale dużo czasu poświęci się na zbadanie problemów i błędów oraz ustalenie, co poszło nie tak.

W przypadku aplikacji A programiści utkną w czytaniu kodu i będą musieli nieustannie odtwarzać problem i przechodzić przez kod, aby znaleźć źródło problemu. Oznacza to, że programista musi przetestować od początku wykonania do końca, w każdej użytej warstwie, i musi obserwować każdą wykorzystaną logikę.
Może ma szczęście, że natychmiast go znalazł, ale może odpowiedź znajdzie się w ostatnim miejscu, o którym myśli.

W przypadku aplikacji B, zakładając idealne rejestrowanie, programista obserwuje dzienniki, może natychmiast zidentyfikować wadliwy komponent i teraz wie, gdzie szukać.

Może to być kwestia zaoszczędzonych minut, godzin lub dni; w zależności od wielkości i złożoności bazy kodu.


Regresje

Weź aplikację A, która wcale nie jest przyjazna dla SUCHA.
Weźmy aplikację B, która jest SUCHA, ale z powodu dodatkowych abstrakcji potrzebowała więcej linii.

Zgłoszenie zmiany jest składane, co wymaga zmiany logiki.

W przypadku aplikacji B programista zmienia logikę (unikalną, współdzieloną) zgodnie z żądaniem zmiany.

W przypadku aplikacji A programista musi zmienić wszystkie wystąpienia tej logiki, w których pamięta, że ​​jest ona używana.

  • Jeśli uda mu się zapamiętać wszystkie instancje, nadal będzie musiał wprowadzić tę samą zmianę kilka razy.
  • Jeśli nie uda mu się zapamiętać wszystkich przypadków, masz teraz do czynienia z niespójną bazą kodów, która jest sprzeczna. Jeśli programista zapomniał o rzadko używanym fragmencie kodu, ten błąd może nie być widoczny dla użytkowników końcowych aż do dalekiej przyszłości. Czy w tym czasie użytkownicy końcowi będą identyfikować źródło problemu? Nawet jeśli tak, deweloper może nie pamiętać, co pociąga za sobą zmiana, i będzie musiał wymyślić, jak zmienić tę zapomnianą logikę. Być może programista nawet do tego czasu nie pracuje w firmie, a wtedy ktoś inny musi to wszystko zrozumieć od zera.

Może to prowadzić do ogromnej straty czasu. Nie tylko w fazie rozwoju, ale także podczas polowania i znajdowania błędu. Aplikacja może zacząć zachowywać się niepoprawnie w sposób, którego programiści nie mogą łatwo zrozumieć. Doprowadzi to do długich sesji debugowania.


Zamienność programistów

Deweloper A stworzona aplikacja A. Kod nie jest czysty ani czytelny, ale działa jak urok i działa w produkcji. Nic dziwnego, że nie ma również dokumentacji.

Deweloper A jest nieobecny przez miesiąc z powodu wakacji. Zgłoszono żądanie zmiany awaryjnej. Nie może się doczekać kolejnych trzech tygodni na powrót Dev A.

Deweloper B musi wprowadzić tę zmianę. Teraz musi przeczytać całą bazę kodu, zrozumieć, jak wszystko działa, dlaczego działa i co próbuje osiągnąć. To trwa wieki, ale powiedzmy, że może to zrobić za trzy tygodnie.

W tym samym czasie aplikacja B (utworzona przez dev B) ma awarię. Dev B jest zajęty, ale Dev C jest dostępny, mimo że nie zna bazy kodów. Co robimy?

  • Jeśli będziemy kontynuować pracę B nad A, a C pracować nad B, to mamy dwóch programistów, którzy nie wiedzą, co robią, a praca jest wykonywana suboptymalnie.
  • Jeśli odciągniemy B od A i każemy mu wykonać B, a teraz umieścimy C na A, wówczas cała praca dewelopera B (lub jej znaczna część) może zostać odrzucona. To potencjalnie zmarnowane dni / tygodnie wysiłku.

Dev A wraca z wakacji i widzi, że B nie zrozumiał kodu i dlatego źle go zaimplementował. To nie wina B, ponieważ wykorzystał wszystkie dostępne zasoby, kod źródłowy po prostu nie był wystarczająco czytelny. Czy A musi teraz poświęcać czas na ustalanie czytelności kodu?


Wszystkie te problemy i wiele innych powodują marnowanie czasu . Tak, w krótkim okresie czysty kod wymaga teraz większego wysiłku , ale w przyszłości będzie on wypłacał dywidendy, kiedy trzeba będzie rozwiązać nieuniknione błędy / zmiany.

Kierownictwo musi zrozumieć, że krótkie zadanie pozwoli Ci zaoszczędzić kilka długich zadań w przyszłości. Niezaplanowanie planuje niepowodzenie.

Jeśli tak, to jakich argumentów mogę użyć, aby uzasadnić fakt, że napisano więcej LOC?

Moje wyjaśnienie polega na pytaniu kierownictwa, co wolą: aplikacji z bazą kodową 100KLOC, którą można opracować za trzy miesiące, lub bazą kodową 50KLOC, którą można opracować za sześć miesięcy.

Oczywiście wybiorą krótszy czas programowania, ponieważ zarząd nie dba o KLOC . Menedżerowie, którzy koncentrują się na KLOC, zarządzają mikrozarządzaniem, a jednocześnie nie są informowani o tym, czym próbują zarządzać.

Flater
źródło
23

Myślę, że powinieneś bardzo uważać na stosowanie praktyk „czystego kodu”, na wypadek gdyby prowadziły one do większej ogólnej złożoności. Przedwczesne refaktoryzowanie jest źródłem wielu złych rzeczy.

Wyodrębnienie warunku do funkcji prowadzi do prostszego kodu w punkcie, z którego warunek został wyodrębniony , ale prowadzi do bardziej ogólnej złożoności, ponieważ masz teraz funkcję, która jest widoczna z większej liczby punktów w programie. Dodajesz niewielką złożoność do wszystkich innych funkcji, w których ta nowa funkcja jest teraz widoczna.

Nie twierdzę, że nie powinieneś wyodrębniać warunku, po prostu powinieneś dokładnie rozważyć, jeśli musisz.

  • Jeśli chcesz konkretnie przetestować logikę sprawdzania poprawności wiadomości e-mail. Następnie musisz wyodrębnić tę logikę do osobnej funkcji - prawdopodobnie nawet do klasy.
  • Jeśli ta sama logika jest używana z wielu miejsc w kodzie, to oczywiście musisz wyodrębnić ją do pojedynczej funkcji. Nie powtarzaj się!
  • Jeśli logika jest oczywiście osobną odpowiedzialnością, np. Sprawdzanie poprawności wiadomości e-mail odbywa się w środku algorytmu sortowania. Sprawdzanie poprawności wiadomości e-mail zmieni się niezależnie od algorytmu sortowania, dlatego powinny znajdować się w osobnych klasach.

We wszystkich powyższych przypadkach przyczyną wyodrębnienia jest po prostu „czysty kod”. Co więcej, prawdopodobnie nie miałbyś nawet wątpliwości, czy byłoby to właściwe.

W razie wątpliwości zawsze wybieram najprostszy i najprostszy kod.

JacquesB
źródło
7
Muszę się zgodzić, że przekształcenie każdego warunku w metodę sprawdzania poprawności może wprowadzić niechcianą złożoność, jeśli chodzi o konserwację i przeglądy kodu. Musisz teraz przełączać się w kodzie, aby upewnić się, że metody warunkowe są prawidłowe. A co się stanie, gdy będziesz mieć inne warunki dla tej samej wartości? Teraz możesz mieć koszmar nazewnictwa z kilkoma małymi metodami, które wywoływane są tylko raz i wyglądają w większości tak samo.
pboss3010
7
Łatwo najlepsza odpowiedź tutaj. Zwłaszcza spostrzeżenie (w trzecim akapicie), że złożoność nie jest po prostu własnością całego kodu jako całości, ale czymś, co istnieje i różni się na wielu poziomach abstrakcji jednocześnie.
Christian Hackl
2
Myślę, że jednym ze sposobów na wyrażenie tego jest to, że generalnie wyodrębnianie warunku powinno być wykonywane tylko wtedy, gdy dla tego warunku istnieje sensowna, nie zaciemniona nazwa. Jest to warunek konieczny, ale niewystarczający.
JimmyJames
Re „... ponieważ masz teraz funkcję, która jest widoczna z większej liczby punktów w programie” : w Pascalu można mieć funkcje lokalne - „... Każda procedura lub funkcja może mieć własną deklarację etykiet goto, stałych , typy, zmienne oraz inne procedury i funkcje, ... ”
Peter Mortensen
2
@PeterMortensen: Jest to również możliwe w języku C # i JavaScript. I to jest świetne! Pozostaje jednak kwestia, funkcja, a nawet funkcja lokalna, jest widoczna w większym zakresie niż wbudowany fragment kodu.
JacquesB
9

Zwracam uwagę, że nie ma w tym nic złego:

if(contact.email != null && contact.email.contains('@')

Przynajmniej zakładając, że zostanie użyty jeden raz.

Mógłbym z tym bardzo łatwo mieć problemy:

private Boolean isEmailValid(String email){
   return email != null && email.contains('@');
}

Kilka rzeczy, na które uważam:

  1. Dlaczego to jest prywatne? Wygląda jak potencjalnie przydatny kod pośredniczący. Czy jest wystarczająco użyteczny, aby być metodą prywatną i nie ma szans na szersze zastosowanie?
  2. Nie nazwałbym metody IsValidEmail osobiście, być może ContainsAtSign lub LooksVaguelyLikeEmailAddress, ponieważ prawie nie wykonuje prawdziwej weryfikacji, co może być dobre, a może nie to, czego się spodziewałem.
  3. Czy jest używany więcej niż jeden raz?

Jeśli jest używany raz, jest łatwy do przeanalizowania i zajmuje mniej niż jedną linię, po drugie zgadłbym decyzję. Prawdopodobnie nie zadzwoniłbym, gdyby nie był to szczególny problem zespołu.

Z drugiej strony widziałem, jak metody robią coś takiego:

if (contact.email != null && contact.email.contains('@')) { ... }
else if (contact.email != null && contact.email.contains('@') && contact.email.contains("@mydomain.com")) { //headquarters email }
else if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") ) { //internal contract teams }

Ten przykład oczywiście nie jest SUCHY.

Lub nawet ta ostatnia wypowiedź może dać inny przykład:

if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") )

Celem powinno być zwiększenie czytelności kodu:

if (LooksSortaLikeAnEmail(contact.Email)) { ... }
else if (LooksLikeFromHeadquarters(contact.Email)) { ... }
else if (LooksLikeInternalEmail(contact.Email)) { ... }

Kolejny scenariusz:

Możesz mieć metodę taką jak:

public void SaveContact(Contact contact){
   if (contact.email != null && contact.email.contains('@'))
   {
       contacts.Add(contact);
       contacts.Save();
   }
}

Jeśli jest to zgodne z logiką biznesową i nie jest ponownie wykorzystywane, nie ma problemu.

Ale kiedy ktoś pyta „Dlaczego„ @ ”jest zapisywane, bo to nie w porządku!” i zdecydujesz się dodać rzeczywistą weryfikację, a następnie wypakuj ją!

Będziesz zadowolony, że tak zrobiłeś, gdy będziesz musiał również uwzględnić drugie konto e-mail prezydentów Pr3 $ sid3nt @ h0m3! @ Mydomain.com i zdecydujesz się po prostu wyjść i spróbować wspierać RFC 2822.

Na czytelność:

// If there is an email property and it contains an @ sign then process
if (contact.email != null && contact.email.contains('@'))

Jeśli Twój kod jest tak wyraźny, nie potrzebujesz tutaj komentarzy. W rzeczywistości nie potrzebujesz komentarzy, aby powiedzieć, co robi kod przez większość czasu, ale raczej dlaczego :

// The UI passes '@' by default, the DBA's made this column non-nullable but 
// marketing is currently more concerned with other fields and '@' default is OK
if (contact.email != null && contact.email.contains('@'))

Niezależnie od tego, czy komentarze powyżej instrukcji if lub wewnątrz małej metody są dla mnie pedantyczne. Mógłbym nawet argumentować przeciwnie do przydatności z dobrymi komentarzami w innej metodzie, ponieważ teraz musiałbyś przejść do innej metody, aby zobaczyć, jak i dlaczego robi to, co robi.

Podsumowując: Nie mierz tych rzeczy; Skoncentruj się na zasadach, z których zbudowano tekst (DRY, SOLID, KISS).

// A valid class that does nothing
public class Nothing 
{

}
AthomSfere
źródło
3
Whether the comments above an if statement or inside a tiny method is to me, pedantic.Jest to problem „słomy, która złamała grzbiet wielbłąda”. Masz rację, że ta jedna rzecz nie jest szczególnie trudna do odczytania od razu. Ale jeśli masz duży metody (np duży import), która ma dziesiątki tych małych ocenach, uwzględniając te zamknięte w czytelnych nazw metodą ( IsUserActive, GetAverageIncome, MustBeDeleted, ...) stanie się zauważalna poprawa na odczytanie kodu. Problem w tym przykładzie polega na tym, że obserwuje tylko jedną słomkę, a nie cały pakiet, który łamie grzbiet wielbłąda.
Flater
@ Flater i mam nadzieję, że to duch, który czytelnik czerpie z tego.
AthomSfere
1
Ta „enkapsulacja” jest anty-wzorcem, a odpowiedź faktycznie to pokazuje. Wracamy, aby przeczytać kod w celu debugowania i rozszerzenia kodu. W obu przypadkach kluczowe znaczenie ma zrozumienie, co faktycznie robi kod. Uruchomienie bloku kodu if (contact.email != null && contact.email.contains('@'))jest błędne. Jeśli if jest fałszem, żadna z pozostałych linii if nie może być prawdziwa. Nie jest to wcale widoczne w LooksSortaLikeAnEmailbloku. Funkcja zawierająca pojedynczy wiersz kodu nie jest dużo lepsza niż komentarz wyjaśniający, jak działa ten wiersz.
Quirk
1
W najlepszym razie kolejna warstwa pośredni zaciemnia rzeczywistą mechanikę i utrudnia debugowanie. W najgorszym przypadku nazwa funkcji stała się kłamstwem, podobnie jak komentarze stają się kłamstwem - treść jest aktualizowana, ale nazwa nie jest. Nie jest to w ogóle strajk przeciwko enkapsulacji, ale ten konkretny idiom jest symptomem wielkiego współczesnego problemu z inżynierią oprogramowania „korporacyjnego” - warstwami i warstwami abstrakcji i kleju zakrywającymi odpowiednią logikę.
Quirk
@ quirk Myślę, że zgadzasz się z moim ogólnym punktem? A z klejem masz zupełnie inny problem. Właściwie używam map kodów, kiedy patrzę na nowy kod zespołów. To przerażające, co zrobiłem dla niektórych dużych metod nazywających serię dużych metod nawet na poziomie wzorca mvc.
AthomSfere
6

Clean Code to doskonała książka, którą warto przeczytać, ale nie jest to ostateczny autorytet w takich sprawach.

Podział kodu na funkcje logiczne jest zwykle dobrym pomysłem, ale niewielu programistów robi to w takim stopniu, w jakim robi to Martin - w pewnym momencie otrzymujesz malejące zwroty z przekształcania wszystkiego w funkcje i może być trudne do naśladowania, gdy cały kod jest mały sztuk.

Jedną z opcji, gdy nie warto tworzyć zupełnie nowej funkcji, jest po prostu użycie zmiennej pośredniej:

boolean isEmailValid = (contact.email != null && contact.emails.contains('@');

if (isEmailValid) {
...

Dzięki temu kod jest łatwy do naśladowania bez konieczności częstego przeskakiwania do pliku.

Innym problemem jest to, że Clean Code staje się już dość stary jak książka. Wiele inżynierii oprogramowania poszło w kierunku programowania funkcjonalnego, podczas gdy Martin robi wszystko, aby dodawać stan do rzeczy i tworzyć obiekty. Podejrzewam, że napisałby zupełnie inną książkę, gdyby napisał ją dzisiaj.

Rich Smith
źródło
Niektórzy są zaniepokojeni dodatkową linią kodu w pobliżu warunku (wcale nie jestem), ale być może rozwiązują to w twojej odpowiedzi.
Peter Mortensen
5

Biorąc pod uwagę fakt, że warunek „aktualny adres e-mail” akceptuje obecnie bardzo nieprawidłowy adres e-mail „ @”, uważam, że masz wszelkie powody, by wyodrębnić klasę EmailValidator. Co więcej, użyj dobrej, dobrze przetestowanej biblioteki do sprawdzania poprawności adresów e-mail.

Linie kodu jako metryki nie mają znaczenia. Ważnymi pytaniami w inżynierii oprogramowania nie są:

  • Czy masz za dużo kodu?
  • Czy masz za mało kodu?

Ważne pytania to:

  • Czy aplikacja jako całość jest poprawnie zaprojektowana?
  • Czy kod jest poprawnie zaimplementowany?
  • Czy kod można utrzymać?
  • Czy kod jest testowalny?
  • Czy kod jest odpowiednio przetestowany?

Nigdy nie zastanawiałem się nad LoC podczas pisania kodu w jakimkolwiek celu oprócz Code Golf. Zadałem sobie pytanie: „Czy mógłbym napisać to bardziej zwięźle?”, Ale dla celów czytelności, łatwości konserwacji i wydajności, a nie tylko długości.

Jasne, może mógłbym użyć długiego łańcucha operacji typu boolean zamiast metody użyteczności, ale czy powinienem?

Twoje pytanie w gruncie rzeczy przypomina mi długie łańcuchy boolanów, które napisałem i zdałem sobie sprawę, że prawdopodobnie powinienem napisać jedną lub więcej metod użyteczności.

Clement Cherlin
źródło
3

Na jednym poziomie mają rację - mniej kodu jest lepsze. Inną odpowiedzią jest Brama, wolę:

„Jeśli debugowanie jest procesem usuwania błędów oprogramowania, programowanie musi być procesem ich wprowadzania”. - Edsger Dijkstra

„Podczas debugowania nowicjusze wstawiają kod naprawczy; eksperci usuwają wadliwy kod. ”- Richard Pattis

Najtańsze, najszybsze i najbardziej niezawodne komponenty to te, których nie ma. - Gordon Bell

Krótko mówiąc, im mniej masz kodu, tym mniej może się nie udać. Jeśli coś nie jest konieczne, to wytnij.
Jeśli kod jest zbyt skomplikowany, uprość go, aż pozostaną rzeczywiste elementy funkcjonalne.

Ważne jest tutaj to, że wszystkie odnoszą się do funkcjonalności i mają tylko minimum wymagane do tego. Nie mówi nic o tym , jak to jest wyrażone.

To, co robisz, próbując mieć czysty kod, nie jest sprzeczne z powyższym. Dodajesz do LOC, ale nie dodajesz nieużywanej funkcjonalności.

Ostatecznym celem jest posiadanie czytelnego kodu, ale bez zbędnych dodatków. Te dwie zasady nie powinny działać przeciwko sobie.

Metaforą byłoby budowanie samochodu. Funkcjonalną częścią kodu jest podwozie, silnik, koła ... co sprawia, że ​​samochód jeździ. To, jak je rozbijasz, przypomina bardziej zawieszenie, wspomaganie kierownicy itp., Ułatwia obsługę. Chcesz, aby mechanika była tak prosta, jak to tylko możliwe, jednocześnie wykonując swoją pracę, aby zminimalizować ryzyko, że coś pójdzie nie tak, ale to nie przeszkadza ci mieć przyjemnych miejsc.

Baldrickk
źródło
2

Istnieje wiele mądrości w istniejących odpowiedziach, ale chciałbym dodać jeszcze jeden czynnik: język .

Niektóre języki wymagają więcej kodu niż inne, aby uzyskać ten sam efekt. W szczególności, podczas gdy Java (która, jak podejrzewam, jest językiem tego pytania), jest niezwykle dobrze znana i ogólnie bardzo solidna, przejrzysta i prosta, niektóre bardziej nowoczesne języki są o wiele bardziej zwięzłe i wyraziste.

Na przykład w Javie napisanie nowej klasy z trzema właściwościami, z których każda zawiera getter i setter, oraz jednego lub więcej konstruktorów może zająć 50 wierszy, podczas gdy możesz osiągnąć dokładnie to samo w jednym wierszu Kotlina * lub Scali. (Jeszcze większa oszczędność jeśli chciał również odpowiednie equals(), hashCode()i toString()metod).

Rezultat jest taki, że w Javie dodatkowa praca oznacza większe prawdopodobieństwo ponownego użycia ogólnego obiektu, który tak naprawdę nie pasuje, do ściśnięcia właściwości w istniejących obiektach lub do przekazania pojedynczej wiązki „nagich” właściwości indywidualnie; w zwięzłym, ekspresyjnym języku bardziej prawdopodobne jest, że napiszesz lepszy kod.

(Podkreśla to różnicę między złożonością „powierzchniową” kodu, a złożonością pomysłów / modeli / przetwarzania, które implementuje. Linie kodu nie są złą miarą pierwszego, ale mają znacznie mniej wspólnego z drugim .)

Tak więc „koszt” właściwego wykonania zależy od języka. Być może jednym ze znaków dobrego języka jest taki, który nie każe wybierać między robieniem rzeczy dobrze a robieniem ich po prostu!

(* To nie jest tak naprawdę miejsce na wtyczkę, ale Kotlin jest warty obejrzenia IMHO.)

gidds
źródło
1

Załóżmy, że obecnie pracujesz z klasą Contact. Fakt, że piszesz inną metodę weryfikacji adresu e-mail, świadczy o tym, że klasa Contactnie ponosi żadnej odpowiedzialności.

Zajmuje się również pewną odpowiedzialnością za wiadomości e-mail, co idealnie byłoby własną klasą.


Kolejnym dowodem na to, że Twój kod jest połączeniem Contacti Emailklasą, jest to, że nie będziesz w stanie łatwo przetestować kodu weryfikacyjnego e-mail. Wymaganie wielu manewrów, aby dotrzeć do kodu sprawdzającego pocztę e-mail w dużej metodzie z właściwymi wartościami. Zobacz metodę viz poniżej.

private void LargeMethod() {
    //A lot of code which modifies a lot of values. You do all sorts of tricks here.
    //Code.
    //Code..
    //Code...

    //Email validation code becoming very difficult to test as it will be difficult to ensure 
    //that you have the right data till you reach here in the method
    ValidateEmail();

    //Another whole lot of code that modifies all sorts of values.
    //Extra work to preserve the result of ValidateEmail() for your asserts later.
}

Z drugiej strony, jeśli miałeś osobną klasę e-mail z metodą sprawdzania poprawności wiadomości e-mail, to aby przeprowadzić test jednostkowy kodu weryfikacyjnego, wystarczy wykonać jedno proste połączenie Email.Validation()z danymi testowymi.


Treść dodatkowa: rozmowa MFeather na temat głębokiej synergii między testowalnością a dobrym projektem.

wyświetlana nazwa
źródło
1

Stwierdzono, że zmniejszenie LOC jest skorelowane ze zmniejszonymi wadami, niczym więcej. Zakładając, że ilekroć zmniejszysz LOC, zmniejszysz prawdopodobieństwo wystąpienia defektów, wpadając w pułapkę przekonania, że ​​korelacja równa się przyczynowości. Zmniejszony LOC jest wynikiem dobrych praktyk programistycznych, a nie tego, co czyni kod dobrym.

Z mojego doświadczenia wynika, że ​​ludzie, którzy potrafią rozwiązać problem z mniejszym kodem (na poziomie makro), zwykle są bardziej wykwalifikowani niż ci, którzy piszą więcej kodu, aby zrobić to samo. To, co ci wykwalifikowani programiści robią, aby zmniejszyć liczbę wierszy kodu, to wykorzystanie / tworzenie abstrakcji i rozwiązań wielokrotnego użytku w celu rozwiązania typowych problemów. Nie spędzają czasu na liczeniu linii kodu i zadręczaniu się, czy mogą przeciąć linię tu czy tam. Często kod, który piszą, jest bardziej szczegółowy, niż to konieczne, po prostu piszą mniej.

Dam ci przykład. Musiałem poradzić sobie z logiką wokół przedziałów czasowych i tego, jak się pokrywają, czy sąsiadują ze sobą i jakie luki istnieją między nimi. Kiedy zacząłem pracować nad tymi problemami, miałem bloki kodu wykonujące obliczenia wszędzie. W końcu zbudowałem klasy reprezentujące przedziały czasowe i operacje, które obliczały nakładanie się, uzupełnienia itp. To natychmiast usunęło duże obszary kodu i przekształciło je w kilka wywołań metod. Ale same te klasy w ogóle nie były pisane zwięźle.

Mówiąc wprost: jeśli próbujesz zmniejszyć LOC, próbując wyciąć wiersz kodu tu lub tam z większą liczbą krótkich, robisz to źle. To tak, jakby próbować schudnąć, zmniejszając ilość spożywanych warzyw. Napisz kod, który jest łatwy do zrozumienia, utrzymania i debugowania oraz ograniczenia LOC poprzez ponowne użycie i abstrakcję.

JimmyJames
źródło
1

Zidentyfikowałeś ważny kompromis

Zatem rzeczywiście istnieje tutaj kompromis i jest on nieodłączny od abstrakcji jako całości. Ilekroć ktoś próbuje wciągnąć N linii kodu do swojej funkcji w celu nazwania go i odizolowania, jednocześnie ułatwia czytanie strony wywołującej (odwołując się do nazwy, a nie do wszystkich krwawych szczegółów leżących u podstaw tej nazwy) i bardziej złożone (teraz masz znaczenie splątane w dwóch różnych częściach bazy kodu). „Łatwy” jest przeciwieństwem „twardego”, ale nie jest synonimem „prostego”, który jest przeciwieństwem „złożonego”. Obie nie są przeciwieństwami, a abstrakcja zawsze zwiększa złożoność w celu wstawienia jakiejś formy z łatwością.

Widzimy dodatkową złożoność bezpośrednio, gdy jakaś zmiana wymagań biznesowych powoduje, że abstrakcja zaczyna wyciekać. Być może jakaś nowa logika poszedłaby najbardziej naturalnie w środek wstępnie wyodrębnionego kodu, na przykład, jeśli wyodrębniony kod przechodzi przez jakieś drzewo i naprawdę chciałbyś zbierać (i być może działać) jakieś informacje, gdy jesteś przemierzając drzewo. W międzyczasie, jeśli wyodrębniłeś ten kod, mogą istnieć inne strony wywołujące, a dodanie wymaganej logiki w środku metody może uszkodzić te inne strony wywoływania. Zobacz, ilekroć zmieniamy wiersz kodu, musimy tylko spojrzeć na jego bezpośredni kontekst; kiedy zmieniamy metodę, musimy Cmd-F cały nasz kod źródłowy szukać wszystkiego, co może się zepsuć w wyniku zmiany kontraktu tej metody,

W takich przypadkach chciwy algorytm może zawieść

Złożoność sprawiła również, że kod jest w pewnym sensie mniej czytelny niż bardziej. W poprzednim zadaniu miałem do czynienia z interfejsem API HTTP, który był bardzo starannie i precyzyjnie podzielony na kilka warstw, każdy punkt końcowy jest określany przez kontroler, który sprawdza poprawność kształtu wiadomości przychodzącej, a następnie przekazuje ją menedżerowi „warstwy logiki biznesowej” , który następnie zwrócił się z prośbą o „warstwę danych”, która była odpowiedzialna za kilka zapytań do warstwy „obiektu dostępu do danych”, która była odpowiedzialna za utworzenie kilku Delegatów SQL, którzy faktycznie odpowiedzieliby na twoje pytanie. Pierwszą rzeczą, jaką mogę o tym powiedzieć, było to, że około 90% kodu to płyta kopiująca i wklejająca, innymi słowy, nie było żadnych operacji. Tak więc w wielu przypadkach odczytanie dowolnego fragmentu kodu było bardzo „łatwe”, ponieważ „och ten menedżer właśnie przekazuje żądanie do tego obiektu dostępu do danych”.dużo przełączania kontekstu i znajdowania plików oraz próby śledzenia informacji, których nigdy nie powinieneś śledzić, „na tej warstwie nazywa się X, na tej drugiej warstwie nazywa się X”, a na drugiej inna warstwa ”.

Myślę, że kiedy skończyłem, ten prosty interfejs CRUD API był na etapie, w którym wydrukowanie go przy 30 liniach na stronie zajęłoby 10-20 pięćset stron podręczników na półce: była to cała encyklopedia powtarzalnych kod. Jeśli chodzi o zasadniczą złożoność, nie jestem pewien, czy była tam nawet połowa podręcznika o zasadniczej złożoności; mieliśmy do dyspozycji tylko 5-6 diagramów baz danych. Dokonanie jakiejkolwiek drobnej zmiany było gigantycznym przedsięwzięciem, uczenie się, że to gigantyczne przedsięwzięcie, dodanie nowej funkcjonalności stało się tak bolesne, że faktycznie mieliśmy pliki szablonów, których użylibyśmy do dodania nowej funkcjonalności.

Widziałem więc z pierwszej ręki, jak uczynienie każdej części bardzo czytelną i oczywistą może sprawić, że całość będzie bardzo nieczytelna i nieoczywista. Oznacza to, że chciwy algorytm może zawieść. Znasz chciwy algorytm, tak? „Zamierzam zrobić wszystko, by lokalny krok najbardziej poprawił sytuację, a potem będę mieć pewność, że znajdę się w sytuacji globalnej poprawy”. Jest to często piękna pierwsza próba, ale można ją również pominąć w skomplikowanym kontekście. Na przykład w produkcji możesz spróbować zwiększyć wydajność każdego poszczególnego etapu złożonego procesu produkcyjnego - rób większe partie, krzycz na ludzi na podłodze, którzy wydają się nic nie robić, aby zająć się czymś innym - i może to często zniszczyć globalną wydajność systemu.

Najlepsza praktyka: nawiąż połączenie używając SUCHOŚCI i długości

(Uwaga: ten tytuł sekcji jest trochę żartem; często mówię moim znajomym, że gdy ktoś mówi „powinniśmy zrobić X, bo tak mówią najlepsze praktyki ”, to w 90% przypadków nie mówią o czymś takim jak wstrzyknięcie SQL lub mieszanie hasła czy cokolwiek innego - jednostronne najlepsze praktyki - dlatego stwierdzenie to można przełożyć w 90% przypadków na „powinniśmy zrobić X, bo tak mówię ”. Na przykład mogą mieć artykuł na blogu z jakiegoś biznesu, który spisał się lepiej z X zamiast X ', ale generalnie nie ma gwarancji, że Twoja firma jest podobna do tej firmy, i ogólnie jest jakiś inny artykuł z innej firmy, który lepiej sobie radził z X' niż X. Więc nie bierz tytułu poważnie.)

To, co poleciłbym, opiera się na wykładzie Jacka Diedericha pt. Stop Writing Classes (youtube.com) . Mówi w tym przemówieniu o kilku ważnych kwestiach: na przykład, że możesz wiedzieć, że klasa jest tak naprawdę tylko funkcją, gdy ma tylko dwie publiczne metody, a jedną z nich jest konstruktor / inicjator. Ale w jednym przypadku mówi o tym, w jaki sposób hipotetyczna biblioteka, którą zastąpił ciągiem mowy, gdy „Muffin” zadeklarowała własną klasę „MuffinHash”, która była podklasą wbudowanego dicttypu, który ma Python. Implementacja była całkowicie pusta - ktoś pomyślał: „być może będziemy musieli później dodać niestandardowe funkcje do słowników Pythona, na wszelki wypadek wprowadzimy abstrakcję”.

A jego wyzywająca odpowiedź brzmiała po prostu: „zawsze możemy to zrobić później, jeśli zajdzie taka potrzeba”.

Myślę, że czasami udajemy, że w przyszłości będziemy gorszymi programistami niż obecnie, więc możemy chcieć wstawić coś małego, co może nas uszczęśliwić w przyszłości. Przewidujemy przyszłe potrzeby. „Jeśli ruch jest 100 razy większy, niż się spodziewamy, takie podejście nie będzie się skalować, dlatego musimy włożyć w to trudniejsze podejście, które będzie skalowane”. Bardzo podejrzane.

Jeśli poważnie podchodzimy do tych rad, musimy ustalić, kiedy nadejdzie „później”. Prawdopodobnie najbardziej oczywistą rzeczą byłoby ustalenie górnej granicy długości rzeczy ze względów stylowych. I myślę, że najlepszą pozostałą radą byłoby użycie OSUSZANIA - nie powtarzaj się - z tymi heurystykami dotyczącymi długości linii, aby załatać dziurę w zasadach SOLID. Opierając się na heurystyce 30 wierszy będących „stroną” tekstu i analogią do prozy,

  1. Prześlij ponownie czek do funkcji / metody, jeśli chcesz go skopiować i wkleić. Niby istnieją uzasadnione powody, by kopiować i wklejać, ale zawsze powinieneś czuć się z tego powodu brudny. Prawdziwi autorzy nie zmuszają cię do ponownego przeczytania dużego długiego zdania 50 razy w całej narracji, chyba że naprawdę próbują podkreślić temat.
  2. Funkcja / metoda powinna być idealnie „akapitem”. Większość funkcji powinna mieć długość około połowy strony lub 1-15 linii kodu, a tylko 10% funkcji powinno mieć zakres do półtorej strony, 45 linii lub więcej. Gdy znajdziesz się w ponad 120 liniach kodu i komentarzy, rzecz musi zostać podzielona na części.
  3. Plik powinien być idealnie „rozdziałem”. Większość plików powinna mieć maksymalnie 12 stron, więc 360 linii kodu i komentarzy. Tylko może 10% twoich plików powinno mieć zasięg do 50 stron lub 1500 linii kodu i komentarzy.
  4. Idealnie, większość twojego kodu powinna być wcięta linią bazową funkcji lub głębokością jednego poziomu. Opierając się na niektórych heurystykach dotyczących drzewa źródeł Linuksa, jeśli podchodzisz do niego religijnie, tylko może 10% twojego kodu powinno mieć wcięcie 2 lub więcej poziomów w linii bazowej, mniej niż 5% wcięcia 3 poziomy lub więcej. Oznacza to w szczególności, że rzeczy, które muszą „zawinąć” jakieś inne obawy, takie jak obsługa błędów w dużych próbach / połowach, powinny zostać usunięte z rzeczywistej logiki.

Jak już wspomniałem, przetestowałem te statystyki na bieżącym drzewie źródeł Linuksa, aby znaleźć te przybliżone wartości procentowe, ale są one również w pewnym sensie uzasadnione w literackiej analogii.

CR Drost
źródło