niedziela, 3 sierpnia 2008

Niejawne założenia

Błędy (bugi) są bardzo ciekawym zjawiskiem w programowaniu. Jakże często zdarza nam się mówić "to źle działa", "tu jest na pewno jakiś błąd" itp. Rodzi się pytanie, jak w deterministycznym środowisku może zaistnieć błąd? Programista pisze kod, a następnie komputer przetwarza go na kod maszynowy i wykonuje w kółko kilka operacji na liczbach binarnych. Gdzie jest tu miejsce na błąd? Mniej więcej od połowy lat 90., kiedy pewna seria procesorów Pentium miała faktyczną usterkę techniczną, komputery w zasadzie "nie mylą się". Skąd zatem błędy? Bugi?
Otóż, wg mnie, występują następujące zasadnicze źródła "błędów" (zachowań systemu niezgodnych z przeznaczeniem; pomijam błędy składniowe):
  • niejasne lub sprzeczne wymagania

  • niedbałość, ograniczenia techniczne

  • niejawne założenia

O ile o pierwszych dwóch przyczynach napisano bardzo wiele i wszyscy zdają sobie z nich świetnie sprawę, o tyle niejawne założenia uważam za zaniedbane, dlatego chciałbym zanalizować ich szkodliwość.

Ileż razy zdarzyło się Wam siedzieć nad debuggerem kilka godzin zanim zorientowaliście się, że analizowana usterka wynika z tego, że inny programista założył coś o jakimś fragmencie kodu? Np. "A no tak, bo ta metoda spodziewa się czegoś tam..."? NullPointerException to chyba najczęściej lecący wyjątek w Javie, a z czego on wynika? Oczywiście z założenia, że w tym miejscu na pewno nie pojawi się wartość null. Trudno w takich przypadkach mówić o czyjejkolwiek winie bezpośrednio.

Problemy z niejawnymi założeniami występują wszędzie tam, gdzie

  • nie ma żadnej dokumentacji (albo jest marna)
  • brak komunikacji w zespole
  • brak głębokiego zrozumienia działania systemu
  • architektura systemu jest nadmiernie skomplikowana i niezrozumiała
Dobry kod to taki, który nie tylko "działa", ale też ma czysty prosty i przejrzysty design oraz jest dobrze przetestowany (wszyscy to wiedzą). Zazwyczaj w parze z tymi cechami idzie jawność założeń. Przeglądając dobry kod i dokumentację, zyskujemy jasność co do założeń: na czym ta klasa operuje (a na czym nie), jakich argumentów ta metoda się spodziewa (a jakich nie) itd. Postulat jawności założeń prowadzi nas wprost w ramiona Design By Contract, metodologii, w której zależności pomiędzy klasami formułuje się na zasadzie kontraktu między dostawcą usługi a klientem (analogicznie do prawdziwych kontraktów biznesowych).
Jaki płynie wniosek z tych rozważań? Według mnie, powinniśmy pójść w stronę pełnej jawności założeń. Wszystkie publiczne metody, nagłówki klas, powinny jasno i dobitnie stwierdzać: co przyjmują jako parametr, a co nie (np. czy może być null), co zwracają itd. Idea ta wywodzi się ze wspomnianego Design by Contract - jasno określić kontrakty klas (zasady, na jakich działają), żeby potem nie było "niejasności" (jak w życiu). Jestem także za tworzeniem "zewnętrznej" dokumentacji (poza kodem) - oczywiście nie jakichś opasłych tomów, ale kilka zwięzłych diagramów, rzeczowy opis działania jakiegoś mechanizmu z pełną specyfikacją założeń - wtedy nie będziemy musieli narażać się na wielogodzinne "odkrywanie" nieujawnionych domniemań.

2 komentarze:

ags pisze...

Nie byłbym sobą, gdybym się nie przyczepił: błędy w procesorach mają się świetnie i nie zanosi się na poprawę - vide Conroe (niestety, dokumentu Intel's Core 2 Errata z VIII 2006 nie mogę już znaleźć). Wynikało z niego, że z 67 znalezionych błędów kolejne serie nie będą pozbawione jedynie 47. (Okrutnie upraszczając, Conroe ma 290 milionów tranzystorów i 67 błędów, czyli nie myli się w 99.99995%.)

Analogicznie jest z Athlonami i wszystkim, co robi człowiek.

Odnośnie DbC - zgadzam się z Tobą, potrafi być bardzo pomocne :) Bardzo żałuję natomiast, że nie zacząłeś narzekać na niesłychanie modne w pewnych kręgach unit testy, które 'niektórzy' uważają za panaceum na zło tego świata. Nie ma nic o zbieżności implementacji i oczekiwań. Zaraz, zaraz - a czy nie był to jeden z częściej wymienianych przytyków do pewnej metodologii spadku swobodnego?
'Leniwi' (hint: od której grupy zawodowej najczęściej pochodzi takie podsumowanie?) programiści nie chcą testować kodu - bo 'przecież to dobrze działa'. Pisanie testów bywa nudne, nie doprowadzi do finalnego rozwiązania kwestii błędnego działania programów, które często powstają w szalonych warunkach.
Entreth remedy: TDD. I co? I nic :D
Otoczony blisko religijnym kultem (kto widział czyste stężone TDD niech pierwszy rzuci Exception) sposób na zwiększenie kreatywności [mwahahaha, 'automagicznie' robienie czegoś nudnego nagle stanie się ciekawe] i poprawienie designu - można uprawiać TDD w oparciu o toString lub getAsString, a to chyba z Dobrym Designem różne uliczki. Bądźmy jednak zwinni, zamknijmy oczy (bardzo mocno!) i jakoś to będzie. Design zrobi się sam, dodatkowo będziemy mieli wysoki coverage.

Bingo!

Paweł Zubkiewicz pisze...

ja tez dorzuce swoje 3 grosze i powiem tak: NIE DOKUMENTACJI. NIE KOMENTARZOM W KODZIE.

od 2 lat pracuje w projekcie w ktorym jedyne komentarze w Javie to //TODO i //FIXME ;-)

Jesli jakas metoda wymaga komentarza to znaczy ze:
a) jest zle napisana
b) technologia jest do dupy
c) jest to jakis hack; patrz punkt b.

Jedyna dokumentacje techniczna dla dewelopera jaką uważam za słuszną to dokumentacja dotycząca srodowiska pracy.
A i tak idealna powinna sie zawrzeć w prostej liście:
1. Sciagnij z SVNa takie projekty 2. Ustaw zmienne środowiskowe w Eclipsie
3. Uruchom odpowiedniego build.xmla (defaultowy target)
4. Zaloguj sie na localhost:port
5. Wszystko działa.

Niestety aby coś było proste jasne i przejrzyste wymaga to pracy, chęci, wiedzy i umiejętności. Często jednak programistom brakuje przynajmniej jednej z tych cech...