Category Archives: Uncategorized

Potrestán odměnami

Zrovna čtu zajímavou knížku. No a protože si potřebuji urovnat myšlenky, tak vám o ní píšu i když ji ještě nemám dočtenou. Kniha se jmenuje Punished by Rewards a napsal ji Alfie Kohn. Jak název napovídá, není to o počítačích, ale o lidech. Je to souhrn výzkumů o motivaci a docela těžko se to čte. Částečně proto, že je to na můj vkus psáno trochu suchopárně, až vědecky. Ale hlavně proto, že to o čem se tam píše se člověku těžko vstřebává.
Jak už název napovídá, je to kniha o škodlivosti odměn. A to jak při výchově dětí doma, ve škole, ale i při motivaci lidí v práci. Celé je to o tom, že existují dva druhy motivace, vnitřní a vnější.
Vnitřní motivace znamená, že něco dělat chci, protože mě to baví, nacházím v tom uspokojení a podobně. Vnější motivaci přichází z venku a to buď ve formě odměny nebo trestu. Většinou protože po mě někdo chce abych něco udělal.
No a celá kniha je o tom, jak vnější motivace nefunguje. Prostě, když mi někdo nabídne spoustu peněz, tak mě to nepřinutí pracovat lépe. Když mě někdo bude něčím hrozit, nestanu se tvořivějším a šikovnějším. Když to člověk takhle napíše tak to zní jako pitomost. Vždyť se s odměnami setkáváme celý život, proč by je lidi používali, když nefungují?
Ono se nedá říci, že by tak úplně nefungovaly. Odměny jsou super, když chci někoho donutit aby dělal to co mu řeknu. Bohužel ho ale nedokáží donutit k tomu aby dělal to co chci. V tom je docela rozdíl.
Hodně zajímavý je vztah mezi odměnami a výkonností. Odměnou mohu zvýšit výkonnost u jednoduchých opakujících se činností. A to jen krátkodobě. Výzkumem bylo naopak dokázáno, že u činností vyžadujících kreativitu jsou odměny dokonce škodlivé. Krátkodobě zvýší kvantitu na úkor kvality, ale ani to zvýšení kvantity netrvá moc dlouho. Když se nad tím člověk tak zamyslí tak to docela dává smysl. Představte si, že by se česká Java komunita rozhodla mi za mé skvělé články platit. K čemu by to vedlo. Pokud by ta odměna byla hodně zajímavá, tak bych psal víc článků. Ale jelikož jsem vykuk, tak bych je psal samozřejmě kratší. Po nějaké době by mi došla inspirace, tak bych tak něco psal, abych dostal odměnu. Po nějaké době by mě to začalo asi dost nudit a kvalita i kvantita by šla ještě víc dolů. To je samozřejmě jenom smyšlený příklad, v knize můžete najít výsledky desítek výzkumů, které pozorují podobné důsledky.
Nejen to. Bylo pozorováno, že když někoho za něco odměním, tak mu to znechutím. Nejvíc mě zaujal výzkum, kdy jedné skupině dětí slíbili, že když budou chvíli kreslit voskovkami, tak si za odměnu budou moci kreslit fixami. Druhé skupině naopak slíbili, že když budou chvíli kreslit fixami, tak budou moci za odměnu kreslit voskovkami. I po dlouhé době první skupina radši kreslila fixami a druhá voskovkami. No taky zkoušeli dětem slíbit, že když teď snědí zmrzlinu, tak si za odměnu budou moci dát kyselé rybičky, ale tomu se jen smály a na zmrzlinu nezanevřely.
Samozřejmě je tu také negativní dopad odměn na ty, kteří ji nedostali, je tu negativní vliv na spolupráci a podobně. Zajímavý je i fakt, že odměny ignorují příčiny. Když moji zaměstnanci špatně pracují, tak jim mohu zkusit před nosem zamávat odměnou. Ale nebylo by lepší přijít na to proč špatně pracují? Třeba to má objektivní příčiny, které by se daly odstranit.
To mě přivádí k tomu, co používat, když jsou odměny tak špatné? Jak lidi motivovat? Odpověď zní lakonicky: „Nemotivujte je“. Prostě se snažte zařídit, aby se zajímali o to co dělají, aby je to bavilo a uspokojovalo. Pak to budou dělat dobře a kvalitně. Zařídit aby lidi bavilo to co po nich chceme je hodně těžké, v knize se dočtete jak tomu pomoci. Docela pěkně se to překrývá s triky, které používají agilní metodiky. Znáte to, nechte lidi vybírat si a řídit svoji práci, nechte je spolupracovat a podobně.
Samozřejmě to neznamená, že bych měl pracovat zadarmo. Na to zapomeňte. Autor ale klade důraz na to, aby se odměna co nejvíce oddělila od úkolu. Zjednodušeně řečeno, aby lidi dostávali plat, ale aby to bylo co nejvíce odděleno od toho co dělají.
Což mě vede k tomu, s čím mám v knize největší problém. Autor mě přesvědčil, že mávat lidem odměnou před nosem nevede k lepším výsledkům. Spíše naopak. Ale kdyby to byla pravda tak by to znamenalo, že komunismus nebyl zas tak špatný nápad. A to jak z historie víme, pravda není. V realitě lidi prostě chtějí mít víc něž soused, chtějí být pochváleni před nastoupenou jednotkou, chtějí dokázat světu, že jsou nejlepší. Tak nevím. Je lepší odměňovat lidi podle zásluh nebo všechny stejně? Ale abych nekončil tak rozpačitě tak provedu důkaz anekdotou (hodně volně přeloženo z knihy):

Byl jednou jeden dědek. Bydlel nedaleko od školy a každé odpoledne na něj kolemjdoucí školáci hulákali a nadávali mu. Jednoho dne dědek vyhlásil, že každý, kdo mu zítra přijde nadávat, dostane dvacet korun. Druhý den přišla hromada dětí, nadávali mu a on jim každému vyplatil dvacet korun. Potom co jim zaplatil, vyhlásil, že kdo přijde další den, dostane deset korun. No deset korun taky není k zahození, tak mu i další den přišla spousta dětí nadávat. Dědek vyhlásil, že kdo mu přijde nadávat zítra, dostane korunu. Děti na sebe nevěřícně koukali a říkali, „Na to zapomeň, za blbou korunu ti nikdo nadávat nebude.“

Zatraceně líná inicializace

Dneska jsem strávil pěkných pár hodin hledáním záhadné chyby. Nedávno jsme v práci implementovali webové služby nad stávající aplikací. Protože jsem se kdysi infikoval testováním, tak jsem si v SoapUI vyrobil takovou pěknou sadu automatických testů. Nejsou to unit testy. Unit testy máme pokrytou vrstvu zařizující webové služby. Pomocí SoapUI děláme funkční testy, které otestují všechno odshora dolů. Testují webové služby, stávající kód aplikace, který není testy pokryt skoro vůbec, a dokonce i interakci s několika systémy na pozadí. Tyto funkční testy obvykle procházejí, pokud tedy počasí přeje, v databázi jsou ta správná data a lety do Amsterdamu nejsou zrovna moc v módě.
Včera odpoledne jsem si ale všiml jedné zajímavé věci. Když nastartuji server a pustím všechny testy najednou, většina z nich selže. Když jsem se podíval do logu, tak jsem uviděl pěknou řadu NullPointerException vyhozených z knihovny Castor. Tu používáme pro serializaci do XML při komunikaci s jedním systémem pod námi. Zajímavé bylo, že když jsem ty testy spustil kdykoliv později, všechny prošly (až na výjimky uvedené dříve). Chvíli jsem si pohrával s myšlenku chybu ignorovat. S Castorem nikdy problémy nebyly, používají ho spokojeně několik let. Třeba je to jen nějaká taková podivnost s kterou se přeci nemusím zabývat. No ale nakonec moje poctivé já zvítězilo a vrhl jsem se do zkoumání. Přeci jenom ve mně trochu hlodaly pochyby, že by to mohlo být nějak způsobené mojí chybou. Ano, dokonce i já o své genialitě někdy pochybuji.
Nejdřív jsem si ověřil, že se to děje pouze hned po nastartování aplikace. A to jenom když spustím několik dotazů najednou. Klasické indicie na problém se synchronizací vláken. Zkusil jsem osvědčené řešení. Zeptal jsem se pana Googla. Světe div se, ani on mi neuměl poradit. To by naznačovalo, že je chyba na naší straně. Castor býval docela populární, kdyby v něm byla chyba, někdo by na ni přeci narazil.
Ale naše použití Castoru bylo naprosto v pořádku. Je tak jednoduché, že na něm není co zkazit

Marshaller.marshal(objectToBeMarshalled, writer);

Takže jsem se ocitl zase na začátku. Dobré bylo, že k chybě docházelo vždy na tom samém řádku. Takže se dal použít debugger. Po pár hodinách strávením restartováním serveru, debuggováním a přemýšlením co dělám špatně jsem přišel na to, že se vše točí kolem třídy DurationDescriptor. Když jsem uviděl její konstruktor, bylo mi jasné že jsem narazil na viníka. Posuďte sami

    private static XMLFieldDescriptorImpl _contentDescriptor = null;


    //----------------/
    //- Constructors -/
    //----------------/

    public DurationDescriptor() {
        super(_xmlName, Duration.class);
        if (_contentDescriptor == null) {
            _contentDescriptor = new XMLFieldDescriptorImpl(String.class,
                "content", "content", NodeType.Text);
            //-- setHandler
            _contentDescriptor.setHandler(new DurationFieldHandler());
        }

    } 
    ...
    _contentDescriptor.getHandler().getValue();

Zkušení harcovníci už určitě vidí kde je problém a mlátí hlavou o stůl. Ostatním se to pokusím vysvětlit.
Představte si že mám dvě vlákna, pojmenujme si je Franta a Pepík. Oba bohužel trpí takovou ošklivou nemocí, kvůli které usínají v ten nejnevhodnější okamžik. Začněme u Pepíka. Ten skočí do konstruktoru a jukne se na statický _contentDescriptor. Ten je prázný takže se Pepík rozhodne ho naplnit. Jenže než to stihne, tak usne někde na cestě mezi řádkami 9 a 10. To je příležitost pro Frantu. Ten se vrhne do konstruktoru, mrkne na _contentDescriptor, ten je pořád ještě prázdný a tak se rozhodne ho naplnit. Obcházení spícího Pepíka ho ale unaví natolik, že z toho taky usne. Jak sebou žuchne probudí Pepíka, který je plný elánu. Vyrobí XMLFieldDescriptorImpl, nastaví Handler a vyrazí do světa. Bohužel usne těsně před tím než stihne Handler použít. Mezitím se vzbudí Franta. Ten si tolik neodpočinul, jenom si vzpomene, že chtěl vyrobit XMLFieldDescriptorImpl. Tak to udělá ale ukládání reference do proměnné _contentDescriptor ho unaví ho to natolik, že zas hned usne. Mezitím se vzbudí energičtější Pepík, který zrovna potřebuje použít Handler. Tak zavolá _contentDescriptor.getHandler(). A co dostane milé děti? null přeci. Zazvonil zvonec a já mám plný log NullPointerException.
Na obhajobu autorů Castoru musím říci, že chybu opravili v roce 2006 commitem číslo 6421. Bohužel ji opravili potichu a nikomu o ní neřekli, takže mi pan Google neuměl poradit. Opravená verze vypadá následovně.

    private static final XMLFieldDescriptorImpl _contentDescriptor;

    static {
        _contentDescriptor = new XMLFieldDescriptorImpl(String.class, "content",
                                                        "content", NodeType.Text);
      _contentDescriptor.setHandler(new DurationDescriptor().new DurationFieldHandler());
    }

Je to mnohem jednodušší a bezpečnější. JVM mi zaručí, že se statický inicializátor zavolá nejvýše jednou.
Jaké z toho plyne poučení? Jedno je evidentní. Používejte nejnovější verze knihoven. (Pokud tedy můžete, my máme Castor upravený od subdodavatele, nikdo neví jaký je rozdíl oproti standardní verzi takže se nikdo neodváží upgradovat na novější verzi).
Druhé poučení je složitější. Dejte si pozor na línou inicializaci. Není to tak jednoduché jak to vypadá. Pokud má někdo dojem, že by to vyřešilo vhodně umístěné klíčové slovo synchronize, přečtěte si tento článek. Není to vůbec jednoduché.
Používání líné inicializace je jistý druh optimalizace. Většinou se k ní uchylujeme, když máme dojem, že můžeme ušetřit pár drahocenných instrukcí. Většinou to ale nestojí za to. Pokud si opravdu myslíte, že ji potřebujete, udělejte to jak radí šílený Bob Lee. No a já se s vámi rozloučím citací zlatých pravidel optimalizace:

Pravidlo 1: Neoptimalizujte
Pravidlo 2: (jen pro experty) Zatím neoptimalizujte

Tomu říkám prosakující abstrakce

Každý problém v programování můžeme vyřešit přidáním další vrstvy abstrakce

Každá netriviální abstrakce do určité míry prosakuje.
Zákon prosakujících abstrakcí (Joel Spolsky)

Prosakující (děravá, leaky) abstrakce je termín, který zpopularizoval Joel Spolsky ve svém článku „Zákon prosakujících abstrakcí“. O co jde? V programování a obecně v informatice si potrpíme na abstrakce. Uvedu příklad ze světa Javy. Používáme ORM nástroje jako je Hibernate. Ty nám krásně odabstrahují všechny ty složitosti za tím. Nemusíme vědět nic o JDBC, SQL, Oraclu nebo Solarisu, stačí nám znát JPA a o zbytek je ukryt za stěnou abstrakcí. Prostě si jenom žijeme v krásném objektovém světe a od hrůz vrstev které jsou pod námi jsme odstíněni. To je ovšem pravda jen dokud nedojde k problémům. Jakmile něco přestane fungovat, začnou k nám prosakovat vrstvy od kterých jsme byli původně odstíněni. Takže musíme vzít kladivo a prosekat se na místo kde problémy vznikly. Takže se najednou musíme začít zabývat nastavením JDBC nebo efektivitou generovaných SQL příkazů. Když je problém hodně exotický, tak si musíme nastudovat jak fungují transakce v Oraclu, indexy nebo dokonce souborový systém v Solarisu. Ocituji Joela:

Všechno tohle paradoxně znamená, že ačkoliv máme programovací nástroje stále vyšší a vyšší úrovně s vyšší a vyšší úrovní abstrakce, je stále těžší a těžší stát se dobrým programátorem.

Kdysi programátorovi stačilo umět JDBC a SQL. Pak přišli ORM nástroje a od JDBC ho odstínili. No jo jenže chudák programátor potřebuje řešit problémy. Takže mu nestačí znát jen ORM, musí se orientovat i ve vrstvách pod ním. Dnešní programátor tak toho musí umět víc než kdysi.

No a proč to vlastně píšu. Narazil jsem na úplně úžasný příklad prosakujících abstrakcí. Mám notebook s Ubuntu. Když jdu ve Firefoxu na stránky grails.org, celý systém na pár sekund úplně zatuhne. Když se podívám co se v děje, tak vidím, že X Windows žerou 100% procesoru. Ostatní stránky ale fungují normálně. Evidentně je tedy chyba na stránce grails.org. No ale v ostatních prohlížečích stránka funguje skvěle. Takže by to bylo Firefoxem? Ale na Windows to ve Firefoxu funguje dobře. Tak že by za to mohlo Ubuntu? Jiné prohlížeče nemají s danou stránkou na Ubuntu problém. Tak čím to může být? Záhada. Naštěstí máme Google. Ten nám prozradí, že se to děje v pouze v kombinaci nVidia grafická karta, nVidia proprietární ovladač, Linux a grails.org nebo pár dalších stránek. Vypadá to, že grails.org mají v kaskádovém stylu (CSS) zadanou nějakou nesmyslně velkou hodnotu, kterou daný ovladač špatně vyhodnotí a začne počítat na CPU místo na grafické kartě. Nebo tak něco.

V tomto případě nám prosákly detaily práce grafické karty až do CSS. Zkrátka průsak skrz celý notebook. Teď si představte, že by se vám stalo něco podobného při vývoji interní aplikace. Google by vám v tom případě nepomohl. Měli byste jen chybové hlášení od pár uživatelů a vůbec byste mu nevěřili. Vždyť to všem ostatním funguje. Vždyť je to webová stránka, ta je přeci nezávislá i na operačním systému, natož pak na hardware. A ono ne. Ovladač grafické karty, operační systém i prohlížeč jsou jen vrstvy abstrakce. A jelikož jsou netriviální tak občas prosakují.