Każdy kod można usprawnić / napisać lepiej. Bez wyjątku. Nie ma kodu idealnego (jak już zresztą kiedyś pisałem). Jednak w pewnym momencie trzeba przestać, tzn. nie można dopieszczać w nieskończoność jednego kawałka kodu zaniedbując wszystko dookoła. Ale nie oznacza to, że powinniśmy akceptować każde poplątane ścierwo jakie wyjdzie spod naszych skrzypiących paluchów i mówić “spoko, jest wystarczająco dobrze“.
Istnieją dziesiątki metryk pozwalających określić “dobrość” kodu. Wszystkie one mają dwie wspólne wady (albo o czymś nie wiem:)). Po pierwsze: są “generic“, więc aplikuje się je bez szerszego spojrzenia na to, co w życiu (a przynajmniej w branży software) najważniejsze: kontekst. Po drugie: nie odpowiadają na pytanie “po co dążyć do granic określonych tymi metrykami?”.
W miniony weekend, gdy z głowy całkowicie wyrzuciłem życie codzienne i zanurzyłem się w gorącej wodzie z bąbelkami, przy akompaniamencie My Dying Bride (norrrrrmalnie romantische situazion!), niespodziewanie naszły mnie refleksje na ten właśnie temat. Ot, figiel spłatany przez dość mocno ostatnio zaniedbany dev-umysł. Z przemyśleń wyszła mi całkiem krótka lista pytań, które należy sobie zadać. Znalezienie granicy, za którą nasza praca jest “wystarczająco dobra” sprowadza się do paru prostych punktów:
Czy poniższe kwestie mieszczą się w akceptowalnych ramach czasowych?:
- wprowadzenie nowej funkcjonalności
- poprawienie buga
- dodanie nowej osoby do projektu
Czy poniższe kwestie mieszczą się w akceptowalnych ramach ilościowych?:
- bugi wprowadzane przy implementacji nowych funkcjonalności
- bugi wprowadzane przy poprawianiu bugów
- bugi znajdowane przez końcowych użytkowników
Jeżeli na każdy z podpunktów odpowiedź brzmi “tak, jest ok” – to system jest wystarczająco dobry. Mało tego, proces wytwarzania i zarządzania cyklem życia tego systemu również jest wystarczająco dobry. W przeciwnym wypadku – trzeba zmienić albo implementację części systemu, albo architekturę, albo proces, albo… wartości kryteriów użytych do zadawania pytań.
Im dłużej się nad tym zastanawiam, tym bardziej jestem przekonany, że tak, to takie proste. W końcu właśnie po to tworzymy “ładny i porządny kod” a nie masę spaghetti…
…która to masa także może być dobrym wyborem, w zależności od kontekstu. Czy w PoC, którego czas życia od napisania pierwszej linii do skasowania z dysku wynosi max miesiąc stosowanie TDD daje jakieś korzyści? Może tak, a może nie. A co z IoC/DI? Niekoniecznie. Intensywne testy przeprowadzane przez dedykowany zespól testerów? Pewnie nie. Bo wartości dla zadawanych pytań są zupełnie inne niż dla produktu komercyjnego. Dla systemu “internal” będą jeszcze inne. Dla aplikacji wykorzystywanej przez 10 osób będą jeszcze inne, a dla portalu z setkami tysięcy użytkowników – jeszcze inne.
Tak więc: pojęcie “kod wystarczająco dobry” oznacza co innego dla każdego systemu. I, co ważne i charakterystyczne: nie jest to odpowiedź stała. To, co dzisiaj jest wystarczająco dobre, za rok może wymagać dodatkowych inwestycji aby jednak podnieść jakość odpowiednią dla nowych realiów.
Czy coś pominąłem, jakieś kluczowe pytanie nie podpadające pod jedno z powyższych? Kryterium, dla którego stosujemy wszelkiego rodzaju best practices, a które nie podpada pod powyższe kwestie?
Dobry kod to krótki kod. I to na każdym poziome, tj. projekt – liczba klas ograniczona, klasa – liczba metod do ogarnięcia, metoda – widać całą na ekranie. Mniej kodu to mniej błędów. Oczywiście zawsze będą wyjątki.
Liczba klas jest (ceteris paribus) odwrotnie proporcjonalna do wielkości tych klas.
Uzupełniając: nie widzę nic złego w dużej liczbie klas samej w sobie. Nie ma tu na czym oszczędzać.
Oczywiście nie mówię o pompowaniu tej liczby na siłę (np. poprzez tworzenie wieżowcowych hierarchii dziedziczenia i mnożenie abstrakcji w charakterze sztuki dla sztuki).
Klasy powinny mieć jedną odpowiedzialność. Jeśli system jest na tyle złożony, że musi wypełniać pierdylion różnych odpowiedzialności (znowu, zakładając że są prawidłowo rozpoznane i zdefiniowane), to widać musi zawierać tyle właśnie klas. So sorry
Co do liczby klas to się całkowicie zgadzam – lepiej więcej niż mniej moim zdaniem. Chociaż to również zależy od… kontekstu! Ostatnio pisałem mały PoC, wszystko zmieściłem w jednej klasie Form1.cs mającej ponad 1000 linii kodu. I to było OK. Kodu "produktowego" tak bym nie napisał, ale tu liczyła się szybkość rozpoznania tematu a nie porządny kod.
Także wracamy do meritum tego posta: nie ma jednoznacznej odpowiedzi prawdziwej dla każdego projektu.
A ja chciałbym troszkę z innej beczki ale jednak w kontekście dobrego kodu. Mianowicie jak się nauczyć pisać dobry kod.
Dla mnie (przecież mogę pisać jedynie z doświadczenia) najlepszą szkołą pisania dobrego kodu był projekt dla klienta który stosował metodykę Scrum i korzystał z jej dobroci, jak Code Review.
Jeżeli osoba która przegląda kod to dobry, doświadczony programista, mający pogląd nie tylko na bebechy aplikacji, ale także na jej biznesowy aspekt, to możemy być pewni że nauczymy się naprawdę dużo poprawiając swoje spaghetti.
Pozdrawiam,
PK.
patryk: thank you.
Dobry tekst. Co prawda temat "dobrego kodu" już przerabiałem, ale teraz staram się na to uczulić innych. Jak się okazuje kwestia wcale nie jest tak oczywista.
Kryterium nadrzędne:
Czy kod/system jest wystarczająco szybki?