<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>tag:plblog.danieljanus.pl,2019:category:fablo</id>
  <title>Daniel Janus – Fablo</title>
  <link href="http://plblog.danieljanus.pl/category/fablo/"/>
  <updated>2011-01-13T00:00:00Z</updated>
  <author>
    <name>Daniel Janus</name>
    <uri>http://danieljanus.pl</uri>
    <email>dj@danieljanus.pl</email>
  </author>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-13:post:protokoly-i-git-bisect</id>
    <title>Pierwsze użycie: protokoły i git-bisect</title>
    <link href="http://plblog.danieljanus.pl/protokoly-i-git-bisect/"/>
    <updated>2011-01-13T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Lubię rozwiązywać problemy przy użyciu narzędzi, których dotychczas nie znałem albo znałem tylko teoretycznie, na zasadzie „wiem, że istnieje coś takiego i do czego z grubsza służy”. Jeszcze przyjemniej jest, kiedy uzyskane rozwiązanie okazuje się czytelniejsze, zrozumialsze, szybsze albo pod innymi względami lepsze niż wersja używająca tylko dotychczasowego „arsenału”. W tych dniach zdarzyło mi się tego doświadczyć dwukrotnie, gdy pracowałem nad &lt;a href="http://fablo.pl"&gt;Fablo&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Fablo to napisany w &lt;a href="http://clojure.org"&gt;Clojure&lt;/a&gt; silnik wyszukiwarki dla polskich sklepów internetowych: zna odmianę polskich wyrazów, obsługuje literówki i błędy ortograficzne. Radzi sobie też z wyrazami wpisanymi bez polskich znaków, a więc wie, że „papryka zolta” to to samo, co „papryka żółta”. Działa to na ogół zupełnie dobrze, ale wciąż ulepszamy wyszukiwarkę i eliminujemy błędy polegające na tym, że na jakieś zapytanie nie wyszukują się niektóre produkty, choć powinny. Tym razem błąd brzmiał: zapytanie „sledzie” zwraca inne wyniki niż „śledzie”.&lt;/p&gt;&lt;p&gt;Analiza problemu ujawniła, że algorytm odpowiedzialny za „polszczenie” zapytania powinien być stosowany szerzej. Mamy dwie implementacje pewnej struktury danych używanej w tym algorytmie: jedna z nich jest czysto funkcyjna i zbudowana z rdzennie clojurowego tworzywa – map i wektorów; druga zaś jest ukryta za fasadą używanej przez nas biblioteki Javowej o prostym interfejsie, a więc explicite do tej pory z niej nie korzystaliśmy. Algorytm polszczenia był uruchamiany tylko na pierwszej implementacji (co wystarczy w wielu przypadkach, ale nie zawsze), a powinien być na obu.&lt;/p&gt;&lt;p&gt;Koncepcyjnie te dwie implementacje są podobne, ale mają zupełnie różne API – zrazu wydawało mi się więc, że będę musiał pisać drugą wersję funkcji realizującej „polszczenie”. To jednak nie byłoby optymalne: duplikacja kodu oznacza dwa razy więcej okazji do popełnienia błędu i konieczność pamiętania o wprowadzeniu zmian każdorazowo w obu miejscach. Z pomocą przyszły &lt;a href="http://clojure.org/Protocols"&gt;protokoły&lt;/a&gt; – nowy element języka wprowadzony w Clojure 1.2.&lt;/p&gt;&lt;p&gt;Protokół jest czymś bardzo zbliżonym do znanego z Javy interfejsu: to zestaw deklaracji nazwanych funkcji i ich argumentów, jednak bez implementacji. Implementacje definiuje się dla konkretnych typów. Mogą to być zarówno typy definiowane w Clojure (przy użyciu konstrukcji &lt;code&gt;deftype&lt;/code&gt;), jak i istniejące klasy Javowe. Można więc zadeklarować w protokole jakąś operację (nazwijmy ją blabalizacją), po czym zdefiniować, co oznacza blabalizowanie liczb, a co napisów. Efekt jest taki, jak gdybyśmy w Javie jakoś zmusili klasy &lt;code&gt;java.lang.Integer&lt;/code&gt; i &lt;code&gt;java.lang.String&lt;/code&gt; do implementowania interfejsu &lt;code&gt;Blabalize&lt;/code&gt;; możemy wołać odpowiednią funkcję bezpośrednio na obiektach odpowiednich klas, bez żadnych wrapperów!&lt;/p&gt;&lt;p&gt;Zacząłem więc od zdefiniowania protokołu, opisującego dwie operacje, jakich można dokonywać na mojej strukturze danych. Potem przerobiłem implementację algorytmu „polszczącego” zapytania tak, aby korzystała tylko z tych dwóch operacji, co uniezależniło ją od implementacji „pod spodem”. Napisanie Clojurowej implementacji protokołu było proste; trudniejsze okazało się odpowiednie skorzystanie z biblioteki Javowej, ale i to udało się zrobić w miarę szybko.&lt;/p&gt;&lt;p&gt;Efekt: brak duplikacji kodu i tylko nieznacznie zmodyfikowana dotychczasowa wersja. A przy tym udało się nagiąć bibliotekę Javową do robienia rzeczy, do których nie była zaprojektowana, bez ingerencji w jej kod. Clojure nie przestaje mnie zadziwiać.&lt;/p&gt;&lt;p&gt;Innym razem zauważyłem, że od kilku commitów przy próbie uruchomienia testów jednostkowych i regresji w logach testowania pojawia się komunikat, którego zdecydowanie nie powinno tam być. Mógłbym zabrać się do tego testując okolice pojawienia się komunikatu w REPL, ale tym razem postanowiłem zrobić to inaczej. Ponieważ używamy &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; do kontroli wersji, wykorzystałem narzędzie &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html"&gt;git-bisect&lt;/a&gt;, aby znaleźć commit wprowadzający komunikat. Potrzebuje ono do działania trzech rzeczy: identyfikatorów commitu „dobrego” i „złego” oraz skryptu, który odpowiada na pytanie, czy dany commit jest „dobry” czy „zły” (zwracającego kod wyjścia odpowiednio 0 albo 1). Skrypt wyglądał mniej więcej następująco:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;cake clean
cake deps
cake proto
&lt;span class="hljs-built_in"&gt;sleep&lt;/span&gt; 10
cake release
&lt;span class="hljs-built_in"&gt;rm&lt;/span&gt; *.&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;
make &lt;span class="hljs-built_in"&gt;test&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; grep -q &lt;span class="hljs-string"&gt;&amp;quot;PODEJRZANY_KOMUNIKAT&amp;quot;&lt;/span&gt; fablo*&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;then&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 1
&lt;span class="hljs-keyword"&gt;else&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 0
&lt;span class="hljs-keyword"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(&lt;a href="http://github.com/ninjudd/cake"&gt;Cake&lt;/a&gt; jest narzędziem, którego używamy do budowania oprogramowania; 10-sekundowa przerwa między &lt;code&gt;cake proto&lt;/code&gt; a &lt;code&gt;cake release&lt;/code&gt; jest remedium na błąd w cake.)&lt;/p&gt;&lt;p&gt;Potem wystarczyło tylko:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;$ git bisect start HEAD dobry_commit --
$ git bisect run check.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i już wiedziałem, w którym miejscu się zepsuło, a stąd było już niedaleko do znalezienia przyczyny problemu.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-19:post:filmujmy-bledy</id>
    <title>Filmujmy błędy!</title>
    <link href="http://plblog.danieljanus.pl/filmujmy-bledy/"/>
    <updated>2010-11-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Każdy programista (o ile jego oprogramowanie jest używane przez kogoś jeszcze poza nim samym) dostał kiedyś zgłoszenie błędu w programie. Wielu pracuje na co dzień z systemami śledzenia błędów, wielu zgłasza błędy w programach, których sami używają.&lt;/p&gt;&lt;p&gt;Dobre zgłoszenie powinno pomóc programiście odtworzyć błąd. Instrukcja odtworzenia jest bodaj najważniejszym z trzech nieodzownych elementów zgłoszenia. (Pozostałe dwa, jak pisze &lt;a href="http://www.joelonsoftware.com/articles/fog0000000029.html"&gt;Joel Spolsky&lt;/a&gt;, to odpowiedzi na pytania: &lt;em&gt;co powinno było się stać&lt;/em&gt; oraz &lt;em&gt;co naprawdę się stało&lt;/em&gt;.) Na ogół w systemach śledzenia błędów opisujemy po prostu prozą kroki, które wykonaliśmy, a które doprowadziły do błędnej sytuacji.&lt;/p&gt;&lt;p&gt;Dokładne opisanie wykonanych czynności jest jednak trudne. Znaczenie może mieć każdy, nawet najmniejszy szczegół, łatwo więc o nieścisłość. Zamiast „otworzyłem plik” lepiej napisać „nacisnąłem Alt-O” albo „kliknąłem w menu Plik i kliknąłem polecenie Otwórz”, w zależności od tego, co naprawdę zrobiliśmy. W świetnym eseju &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/bugs-pl.html"&gt;&lt;em&gt;Jak efektywnie zgłaszać błędy&lt;/em&gt;&lt;/a&gt;, z którego zaczerpnąłem ten przykład, Simon Tatham podaje inną, efektywniejszą metodę zgłaszania: należy posadzić programistę przed swoim komputerem i pokazać mu, w jaki sposób doprowadzamy do błędu.&lt;/p&gt;&lt;p&gt;Ta metoda jest dobra, kiedy da się zastosować. Problem pojawia się w przypadku, kiedy programista jest fizycznie na drugim końcu świata. Nie może wtedy zobaczyć monitora użytkownika i patrzeć mu na ręce... albo przynajmniej do niedawna nie mógł.&lt;/p&gt;&lt;p&gt;Od jakiegoś czasu w &lt;a href="http://fablo.pl"&gt;Fablo&lt;/a&gt; dołączamy do zgłoszeń błędów krótkie filmiki – &lt;em&gt;screencasty&lt;/em&gt; z komentarzem głosowym. Z każdym kolejnym błędem rozwiązanym dzięki wskazaniu w taki sposób coraz bardziej przekonuję się do rozlicznych zalet tej metody. Przede wszystkim film jest dokładnym zapisem sesji. Widać wszystko, co się działo krok po kroku, i nie trzeba prosić o dodatkowe szczegóły, co zabiera czas i naprawiającego, i zgłaszającego. Film można odtwarzać wielokrotnie i dowolnie po nim nawigować, a nawet spowalniać; to nawet lepsze niż patrzenie na żywo na pracę użytkownika z programem. Jeśli mamy do czynienia z &lt;em&gt;heisenbugiem&lt;/em&gt; („wywala się mniej więcej co pięć minut i w różnych sytuacjach, za cholerę nie mam pomysłu!”), możemy po prostu włączyć nagrywanie i zapomnieć o nim, pracując normalnie do momentu wystąpienia błędu. Film można wtedy obciąć do ostatnich dwóch-trzech minut i wysłać.&lt;/p&gt;&lt;p&gt;Co więcej, sporządzenie filmiku bardzo mało kosztuje. Na obie używane przez nas platformy dostępne są narzędzia pozwalające łatwo nagrać film. Dzięki &lt;a href="http://www.ambrosiasw.com/utilities/snapzprox/"&gt;Snapz Pro X&lt;/a&gt; na Mac OS X nagranie jest tak proste jak naciśnięcie Shift-Cmd-3 na początku i na końcu filmu; &lt;a href="http://recordmydesktop.sourceforge.net/about.php"&gt;Record My Desktop&lt;/a&gt; na Linuksa i FreeBSD jest nieco trudniejszy w obsłudze, ale wciąż łatwy.&lt;/p&gt;&lt;p&gt;Wypadałoby wspomnieć o minusach filmów. Przychodzą mi do głowy dwa. Jednym jest fakt, że nie każdy błąd można zgłosić w ten sposób, a mówiąc ściślej: błąd w nie każdym oprogramowaniu. Gdy pracuje się z bibliotekami zamiast z okienkowym lub przeglądarkowym oprogramowaniem, wyłapywane błędy są innej natury i polegają na ogół na nieoczekiwanym zachowaniu konkretnej funkcji lub klasy. To, &lt;em&gt;nolens volens&lt;/em&gt;, trzeba opisać.&lt;/p&gt;&lt;p&gt;Druga sprawa to ilość miejsca zajmowanego przez filmiki i czasu potrzebnego na ich wrzucenie do systemu kontroli błędów. To zapewne jeszcze do niedawna był powód, dla którego rzadko widzi się takie zgłoszenia np. w oprogramowaniu &lt;em&gt;open source&lt;/em&gt;. Jednak problem ten staje się mało znaczący wraz z upowszechnieniem się szybkich łącz. Typowa wielkość naszego filmiku to ok. 1.5-2 MB; to naprawdę nie jest dużo, a &lt;a href="http://fogbugz.com"&gt;FogBugz&lt;/a&gt; pozwala na bardzo łatwe – jednym przeciągnięciem myszy – dodanie screencastu do raportu błędu. Większe rzeczy z łatwością można udostępnić w &lt;a href="http://dropbox.com"&gt;Dropbox&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Filmujmy więc błędy!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
</feed>
