Najlepszy kod to brak kodu w ogóle

Oryginalny post: The Best Code is No Code At All

Autor: Jeff Atwood

Rich Skrenta pisze, że kod jest naszym wrogiem.

Kod jest nieznośny. Gnije. Wymaga okresowego konserwowania. Zawiera błędy, które trzeba znaleźć. Nowe funkcjonalności wymagają adaptowania starego kodu. Im więcej masz kodu, tym więcej miejsc, gdzie mogą ukrywać się błędy. Tym dłuższe przygotowanie i kompilacja programu. Tym więcej czasu zajmie nowemu pracownikowi zrozumienie twojego systemu. Jeśli będziesz musiał go refaktoryzować, będzie więcej elementów do zreorganizowania.

Kod jest tworzony przez inżynierów. Napisanie większej ilości kodu wymaga większej liczby inżynierów. Inżynierowie komunikują się z kosztem kwadratowym (n^2), kod dodawany przez nich do systemu zwiększa jego możliwości, ale równocześnie koszty. Powinieneś robić wszystko co możliwe, aby zwiększyć produktywność poszczególnych programistów, aby pisany przez nich kod był tym, który jest najbardziej zwięzły. Mniej kodu do zrobienia tego samego (a może też lepiej). Mniej programistów do zatrudnienia. Mniejsze koszty komunikacyjne.

Na to wskazuje Rich, ale prawdziwym problemem nie jest kod. Kod w momencie napisania go dla świata jest zupełnie niewinny, niczym nowonarodzone dziecko. Kod nie jest naszym wrogiem. Chcesz zobaczyć prawdziwego wroga? Idź spojrzeć w lustro. Tam jest twój problem, właśnie tam.

samochód lusterko widok

Jako programista jesteś swoim najgorszym wrogiem. Im wcześniej to dostrzeżesz, tym lepiej dla Ciebie.

Wiem, że masz najlepsze intencje. My wszyscy je mamy. Jesteśmy programistami, kochamy pisać kod. To jest to, co robimy. Nigdy nie spotkaliśmy się z problemem, którego nie moglibyśmy rozwiązać przy pomocy taśmy klejącej, prowizorycznego wieszaka i szczypty kodu. Natomiast Wil Shipley przekonuje, że powinniśmy powstrzymać się w naszej naturalnej tendencji do pisania dużej ilości kodu:

Fundamentalną właściwością kodowania z naszego programistycznego punktu widzenia jest spostrzeżenie, że każda decyzja jaką podejmujemy jest kompromisem. Aby być dobrym programistą należy zrozumieć naturę tych kompromisów i być ich świadomym podczas pisania czegokolwiek.

W kodowaniu masz wiele kryteriów wg których możesz oceniać kod:

  • zwięzłość
  • szeroki zakres funkcjonalności
  • szybkość wykonywania
  • czas spędzony na kodowaniu
  • solidność
  • elastyczność

Teraz zapamiętaj, te kryteria stoją do siebie w opozycji. Możesz spędzić trzy dni pisząc algorytm, który jest naprawdę piękny i szybki, zwiększyć dwukrotnie ocenę w tej kategorii, ale poświęciłeś na to trzy dni, więc ocena w kategorii “czas spędzony na kodowaniu” spada w dół.

Więc, kiedy to się opłaca? Na jakiej podstawie podejmujemy takie decyzje? Odpowiedź okazuje się być bardzo rozsądna, bardzo prosta, a także taka, że nikt nigdy jej nie słucha: Zacznij zwięźle. Polepszaj inne kryteria kiedy testy pokażą, że jest to niezbędne.

Całkowicie się zgadzam. Dawałem podobne rady, kiedy nawoływałem programistów do Zwięzłego Kodowania (Code Smaller). I nie mówię tutaj o zredukowanych do absurdu konkursach podczas których używamy wszystkich mądrych sztuczek z naszych książek, aby kod zajmował mniej fizycznego miejsca. Mówię o praktycznych, rozsądnych zasadach, które pozwolą zmniejszyć objętość kodu, który programista musi przeczytać, aby zrozumieć jak program działa. Tutaj jest trywialny i krótki przykład tego, o czym mówię:

if (s == String.Empty) if (s == "")

Dla mnie oczywistym jest, że drugi przypadek jest lepszy, ponieważ jest po prostu krótszy. A jestem zupełnie pewny, że znajdą się programiści, którzy będą ze mną walczyć, na śmierć i życie, ponieważ są absolutnie przekonani, że nadmiarowość String.Empty jest przyjaźniejsza dla kompilatora. Gdybym się tym przejmował. Gdyby ktokolwiek się tym przejmował.

Dla większości programistów, którzy tak bardzo kochają kodowanie, bolesne jest, żeby to potwierdzić, ale najlepszy kod to brak kodu w ogóle. Każda nowa linia kodu, którą chcesz dać światu wymaga debugowania, kod będzie musiał być czytany, zrozumiany, kod będzie musiał być wspierany. Za każdym razem kiedy piszesz nowy kod, powinieneś robić to niechętnie, jakby pod przymusem, ponieważ nie masz innego wyjścia, wyczerpałeś wszystkie inne możliwości. Kod jest naszym wrogiem dlatego, że nas - programistów piszących cholernie dużo kodu - jest tak wielu. Jeśli nie możesz wytrzymać bez kodu, najlepszą rzeczą jaką możesz zrobić jest zacząć zwięźle.

Jeśli kochasz pisanie kodu – naprawdę, szczerze kochasz pisanie kodu – pisz go tak mało jak jest to tylko możliwe.

Data publikacji oryginału: maj 30, 2007

20 komentarze:

Anonimowy pisze...

"Nowe funkcjonalności" :(

copernic777 pisze...

A co jeśli wprowadzenie nowego kodu poprawia jego czytelność:

if (s == "")

vs

boolean nieWpisanoLoginu = s == "";
if (nieWpisanoLoginu)

;]

Robert Pankowecki pisze...

@copernic777: Gdyby zmienna tekstowa nazywała się "login" zamiast "s" to nie trzeba by bylo wprowadzac dodatkowej zmiennej typu bool, której to nazwa dopiero sugeruje co robi kod...

Moje rozwiązanie:
if login.empty?

Anonimowy pisze...

Chyba chodziło o to że if (s == "") jest lepsze, chciaż fakt, przydało by się to nazwać 'login' ;-)

Anonimowy pisze...

Kto z was miał smiałosć spojrzec w lustro/ Co do artykulu to idac za glosem autora proponuje uciac sobie palce ku chwale niewidzialnego kodu.
9tekst napisalem nosem0

batman pisze...

Czasami nadmiarowość jest dobra:
if(jakas_zmienna == Klasa.JakasStala) vs if(jakas_zmienna == 123)
Skąd nowa osoba ma widzieć co oznacza 123? Oczywiście można przeorać dokumentację projektu, ale jak wiadomo czas to pieniądz i szukanie w dokumentacji opisu jakiejś liczby zajmie więcej czasu, niż napisanie kilku liter i kropki.

"najlepszy kod to brak kodu w ogóle" - To może od razu wyłączmy komputery i zacznijmy żyć z leśnych jagód ;)

Anonimowy pisze...

@batman
Podany przez ciebie przykład ciężko nazwać nadmiarowością. To raczej samodokumentujący się kod. Pozbywamy się nadmiarowości w postaci zewnętrznej dokumentacji, której nikt nie czyta i która trzeba zarządzać, więc PLUS.

Anonimowy pisze...

Dla mnie niekoniecznie "" jest bardziej przejrzyste niż string.Empty. Widząc string.Empty wiem, że jest to string.Empty, widząc "" muszę się przyjrzeć przez kilka milisekund, czy jest to "", czy " ", więc czas percepcji jest dłuższy, choć szybciej czyta się "" :)

koziołek pisze...

Autor słusznie zauważa, że prostota jest najlepszym rozwiązaniem. Jednocześnie prostotę można osiągnąć za pomocą odpowiednio przemyślanego kodu:
if(s == "")
if(s == string.Empty)
to kod zwięzły, ale nieprzemyślany. Odpowiednio przemyślany kod wyglądał by tak:

if(String.isEmpty(s))

W ten sposób przenosimy meritum do zewnętrznej biblioteki, która może dla nas być czarną skrzynką. Nie musimy zastanawiać się jak działa ten kod, ważny jest tylko wynik. Każda biblioteka zewnętrzna, która wyręcza nas z zadań jest dobra bo ogranicza nasz kod i nasze koszty.

Immortal pisze...

@koziolek

Dobrze napisane: "Każda biblioteka zewnętrzna ... ograniczna nasz kod..." ;)

koziołek pisze...

@Immortal, by nie było niedopowiedzeń. Ogranicza ilościowo nasz kod. Zazwyczaj wzbogaca go też jakościowo zdejmując z nas odpowiedzialność za testowanie czarnej skrzynki. Jeżeli coś pójdzie nie tak w kodzie "obcym" to zawsze można go naprawić jeżeli jest na GPLopodobnej licencji lub zgłosić błąd do producenta.

rafek pisze...

Jako dodatek do tego wpisu, proponuję jeszcze lekturę: Spartańskie programowanie

Immortal pisze...

@koziolek: Jasne, wiem, co chciałeś przekazać i oczywiście zgadzam się z tym. Ale akurat tak mi się skojarzyło, że czasami jakieś drobne błędy w bibliotekach czy frameworkach potrafią irytować i ograniczać sposób, w jaki piszemy kod :]

koziołek pisze...

@Immortal, @rafek, daliście mi do myślenia. Właśnie czy minimalizacja i ograniczanie zawsze są dobre.
Kod silnie skompresowany "w edytorze" zazwyczaj jest dobry. Jednak są miejsca, w których taka kompresja nie jest dobra z punktu widzenia utrzymania.
Najlepszymi przykładami są globalne zmienne domyślne w perlu i rubym (w obu jest to bodajże $_). Mechanizm jest super, bo pozwala na kompresowanie kodu. Z drugiej strony jeżeli kod ma złożoność choć trochę większą od Hello World to użycie zmiennych domyślnych jest niebezpieczne. Wymaga od nas utrzymywania "zawsze aktualnej i dokładnej dokumentacji, która nigdy się nie myli". Wiadomo jednak, że jak taka dokumentacja to świat przestanie istnieć.
Można zatem założyć, że minimalizacja powinna być doprowadzana do poziomu, na którym dalsze minimalizowanie powoduje konieczność pisania dokumentacji tłumaczącej jak kod działa, a nie co robi.

Pozdrawiam

ps. Dobra robota ten blog.

Wiktor Wojtylak pisze...
Ten komentarz został usunięty przez autora.
mixer pisze...

@koziolek
Zgadzam się co do stosowania bibliotek.
Kwoli ścisłości if(s == string.Empty) też może wykorzystywać jakąś zewnętrzną bibliotekę i jej metodę. Konkretnie: == może być przeciążonym operatorem ;)

PS. Pamiętajcie, że ten przykład z if(s == "") to tylko przykład, a przesłanie jest szersze ;) Na deser: tutaj są rozważania nad wyższością jednej opcji nad drugą: C# “” BETTER THAN STRING.EMPTY?

Anonimowy pisze...

Co do wspaniałych wyrażeń, to idealny przykład z C#:
obrazek.Clone(new Rectangle(x, y, w, h), System.Drawing.Imaging.PixelFormat.DontCare);

tak, to dobry sposób na powiedzenie, że nie zależy mi na formacie :)

Anonimowy pisze...

Podany przykład if (s == "") jest fatalny. Aby wykonać takie porównanie kompilator musi zrobić następujące rzeczy:
1. Utworzyć nowy obiekt typu String o pustej zawartości
2. Prawdopodobnie w pętli porównywać znak po znaku - w szczególności jeśli s jest puste liczba iteracji będzie równa 1.
Dodatkowo jeśli s = NULL porównanie nie zwróci oczekiwanej wartości (string domyślnie przyjmuje wartość NULL). Jak dla mnie jedyna słuszna alternatywa to String.IsNullOrEmpty(). Zadziwiające jest też to, że wielu programistów nie zna tej funkcji (wiem, bo sprawdzam testy rekrutacyjne). Przeraża mnie też bezmyślność i ślepa wiara w Garbage Collectora niektórych autorów artykułów (czytaj: .NET wyjadaczy), przy pomocy którego nie musimy już o niczym myśleć. Niech autorzy napiszą, że proponowany przez nich styl programowania dobry jest do aplikacji typu kalkulator, a nie złożonych systemów.

Piotr pisze...

Dokładnie. Aż dziw, że dopiero tak późno ktoś wspomniał o:

if(String.IsNullOrEmpty(s)) ,ewentualnie
if(!String.IsNullOrEmpty(s))

Dla mnie, podczas pracy z .NET jest to podstawa podstaw, bez której nie da się miarodajnie żyć i pracować.

Wiele razy zdarzało mi się sprawdzać podobny input, a że nie zawsze wiemy skąd pochodzi i jaką drogę przebywa (np. fragment kodu pisany przez innego programistę) to rozwiązanie z:

string s = "";

kompletnie się nie sprawdza (jak poleci null to jesteśmy w "czarnej dupie" - czytaj - "NullReferenceException").

Jak dla mnie o wiele bardziej bezpieczny, czytelny i zwyczajnie lepszy jest kod z:
String.IsNullOrEmpty()

P.S. Myślę iż Java oraz inne technologie również posiadają w swojej podst. bibliotece odpowiednik tej funkcji.

Anonimowy pisze...

Nie rozumiem nie których komentarzy może dlatego, że ich treść jest nijak adekwatna do artykułu lub problemu przedstawionego.

Przedstawię jak ja odebrałem artykuł ponieważ każdy patrząc po komentarzach inaczej na to spojrzał. Dlatego i ja mam swoje zdanie.

Wydaje mi się, że autorowi nie chodziło o brak kodu w zrozumieniu "brak" ale o brak zbędnych linijek, które de facto wpływają negatywnie na wydajność (nie koniecznie na czytelność). Te linijki które w sposób jasny pomagają odczytać kod osobie 3, która nie tworzyła bezpośrednio programu są potrzebne!

Prosty przykład przedstawiony gdzieś wyżej "if(jakas_zmienna == Klasa.JakasStala) vs if(jakas_zmienna == 123)" - Wg mnie jak najbardziej pasuje do tematu.
Spójrzmy na to od tej strony. Mamy prosty formularz logowania, abstrahuje od technologii. W którym prócz loginu i hasła sprawdzamy np. zwracany przez serwer typ uprawnienia w formie int. 1 - administrator, 2 - moderator itd....
Jeśli, gdzieś w stosie kodu dokonamy jawnego porównania if (1){} else if (2) {} ; kod byłby delikatnie mówiąc nie czytelny dlatego też nadmiarowość pożyteczna w formie np struktury czy typu wyliczeniowego i sprawdzanie wyglądało by następująco if(Convert.ToInt16(Rola.Administrator) == zwracany int){ } kod jest czytelny :)

Prześlij komentarz

Related Posts with Thumbnails