30. března 2011

Team vyhrává když táhne za jeden provaz

Po svém posledním příspěvku o scrumu jsem měl v hlavě, že napíšu ještě jeden příspěvek, abych se vyjádřil jasně, proč považuji scrum za to nejlepší v metodikách vývoje. Je to právě jeho orientace na team a ne na jednotlivce. Na samoorganizující team (tj. organizace není určena z venku).

Ale pak jsem si poslech czpodcast č. 39 - Operations a Devops (hostem byl Radim Marek z Gooddata) a ten mé myšlenky posunul trochu jiným směrem. Omlouvám se, že podcasty poslouchám v opačném pořadí než vznikaly a navíc hodně zpětně, že si většina již nepamatuje co se v nich řešilo.

Nejprve se pochválím. Praktikujeme cosi podobného jako Devops a myslím, že se nám to osvědčilo. Vždy, když píšu v aplikaci logovací string, tak přemýšlím k čemu jej použiju a co chci, aby v něm bylo za informace. Kontakt se zákazníkem mi pomáhá přemýšlet o našem produktu v kontextu zákazníka nikoliv v mém.

Co mě ale vybudilo napsat tento článeček. Opět zaznělo něco co mi přijde logické. Tj. splynutí developerů a QA. A kritika byla, proč by měl drahý developer dělat testování, když tester je levnější. Argumentace Radima Marka byla do jisté míry správná, developer, který umí napsat automatický test a zárověn udělat performace testování je jistě lepší než ten co to neumí.

Ale proč nad tím přemýšlet na úrovni jednotlivých vývojářů a ne na urovni teamu (jediný důvod, kterému rozumím je ohodnocení členů teamu - výplata se dává jednotlivcům - ale koneckonců proč ji nedat teamu a nechat na něm její rozdělení mezi jeho členy - třeba by to fungovalo). Vývojář sám nic neudělá, team zvládne víc. Je důležité mít schopný team, který je složen tak, aby zvládl analýzu, návrh, vývoj i testování (v celé jeho šíři). Proč je to lepší? Team je na jedné lodi a táhne za jeden provaz. Nepřehazuje si horkou bramboru.

Pokud jeden člen neví jak dál s úkolem, který řeší, zeptá se kolegy, který to již dělal, nebo je zkušenější. Nikomu to nevadí, protože aktivita je stále vynaložena na úkol, který řeší celý team. Tj. je to aktivita pro dobro všech. Navíc se lidé vzájemně vzdělávají a roste jejich kvalita. Pokud ovšem sleduji, úkoluji jednotlivé vývojáře, dříve nebo později (čas je dán okamžikem, kdy se toto sledování využije k represivním krokům) přestane být pro jednotlivce zajímavé spolupracovat, protože neřeší to co mají, ale dělají něco co nemají (pomáhají kolegovi).

Pro mě je to celkem stejné jako rozdělení firmy na oddělení. Funguje to dokud jsou všichni na jedné lodi. Jakmile jsou oddělení dostatečně velká, že si zavedou samostatná účetnictví a "přefakturovávají" si vzájemnou práci, pak je spolupráce ta tam.

Závěrem. Scrum nabízí pohled na vývoj SW skrze team. Samoorganizující team, který je silně kooperující. To je prostředí, kde vznikají rychle dobré věci. V konkurenčním prostředí se rozvoj zpomaluje, protože se všichni ohlíží na konkurenci. Na vývoj SW už dávno nestačí jeden člověk je jich potřeba víc. Takže chceme, aby spolupracovali a ne si konkurovali.

PS. Teda to je pěkná všehochuť, snad z toho někdo něco pochopí ...

21. března 2011

Neoprávněná kritika Scrumu v czpodcastu č.46: Kanban agilní vývoj

Poslouchám nyní zpětně czpodcast a při poslechu 46. dílu s názven "Kanban agilní vývoj" zazněla, dle mého soudu, neoprávněná kritika na adresu scrumu.

Na úvod je nutné si otevřeně říci, že scrum není pro každého. Splnit podmínky pro vývoj pomocí scrumu není úplně triviální, ale pokud se to povede ovoce přinese. Druhá, velmi důležitá věc, je najít kvalitní zdroj, odkud vědomosti čerpat. Existuje spousta knih o scrumu, které jsou zavádějící a nebo špatné. V ideálním případě je vhodný konzultant, který nám poradí, jak scrum správně implementovat v našem konkrétním prostředí. Já žádného dobrého českého konzultanta neznám (rozuměj nemám jej prověřeného), ale účastnil jsem se školení na ScrumMastera s Borisem Glogerem, který mě jednoznačně přesvědčil, že tím schopným konzultantem. Svému řemeslu i scrumu perfektně rozumí.

A nyní již k obsahu podcastu. Prvním jasným signálem, proč scrum nemůže v GoodData fungovat je oddělení QA od vývoje. Scrum si vynucuje existenci tzv. universálních týmů. Tj. z týmu co sprint vypadne funkční plně otestovaný produkt doplněný o nové položky z backlogu. Pokud máme oddělené QA od vývoje, pak z týmu vypadne cosi, ale není to funkční produkt. Kde je probém? Myslím, že si Jaroslav odpověděl sám. Není známa velocita teamu. Team sice doplní produkt o položky (featury) v obtížnosti např. 100, ale v další iteraci je opravuje a spadne mu velocita na 60. Proto musí být universální teamy, aby to co je jejich výsledkem bylo funkční a plně otestované a vědělo se přesně jakou mají velocitu (ta se po několika prvních iteracích ustálí). Velocita pak pomáhá předpovídat, kolik team udělá položek backlogu za definovaný čas.

Dalším problém, který byl zmíněn je vynucování si, aby univerzální teamy nebyly moc veliké, řádově deset členů (i méně). Pokud máme vývojářů víc, pak je rozdělíme do více teamů a je samozřejmě možné udělat, aby pracovali "skoro" nad jedním backlogem.

V podcastu zaznělo, cosi o zarovnávání položek do sprintu a výběr menších položek z backlogu "mimo" prioritu. První jsem slyšel prvně až v podcastu a druhé je naprosto špatně! Team si na začátku sprintu z backlogu vybírá, striktně podle pořadí v jakém jsou uvedeny, položky, které bude realizovat. Chybou je mít položky tak velké, že např. zvolím první, druhou už zvolit nemohu, protože bych ji nestihl, ale první mě zaměstná pouze na 2/3 sprintu. Nikde ovšem není řečeno, že pokud má team velocitu 30, pak si musí vybrat položky v úhrnné složitosti 30, může si zvolit stejně dobře 27, jako 31.

V neposlední řadě zaznělo, že se jim v GoodData položky špatně odhadují, protože dělají nové věci a u nich člověk nikdy neví. Může to být do jisté míry pravda, ale co je na scrumu kouzelné je to, že u položek v backlogu se neodhaduje jejich pracnost, ale pouze jejich složitost a to ještě poměrově mezi sebou. Používá se k tomu technika planning poker, kdy jednotlivý členové ohodnocují položky složitostí pomocí kartiček s hodnotami 1, 2, 3, 5, 8, 13, 20, 40, 100. Na této technice je úžasné, že porovnat dvě věci mezi sebou a říci, která je složitejší a jak moc nám jde velmi dobře a jsme až překvapivě hodně přesní (na rozdíl v odhadování kolik to zabere času).

Z mého pohledu je závěr spíš, že scrum v GoodData neměli vůbec používat a navíc jej používali špatně a proto jim nefungoval. Tedy jednoznačně není chyba na jeho straně. Osobně se domnívám, že scrum je nejlepší metodika, kterou máme v současnosti k dispozici, ale bohužel, není pro každého.

1. března 2011

Inovace bez legrace aneb máme se snažit vymýšlet něco nového a nebo jenom přebírat dané

Dneska se snad poprvé pustím do polemického příspěvku, který na první pohled je o vývoji softwaru ryze okrajově, ale mám takový pocit, že musím ...

Jaká je motivace pro tento příspěvek. Nejprve jsem si přečetl Lukášův Jak nepsat framework, který ve mě zanechal jistou pachuť, ale nevyprovokoval mě k ničemu, ještě jsem nevěděl ...

Pak se mi dostaly do ruky hodnocení prezentací pořádaných na MFF od studentů, kteří je shlédli. A to mě nakoplo, protože jedno hodnocení mé přednášky bylo:

Co se tématu týče, tak na mě to působilo dojmem, že pan Mareš přistupuje k Softwarovému inženýrství dost osobitým způsobem. Přišlo mi, že přednášejícímu je upřímně jedno, jak to či ono realizují v kvalitních společnostech typu NASA, DoD a podobně. Dokonce mi přišlo, že jeho postoj je, že on sám je opravdu chytrý a že to co vymysleli pánové stavějící rakety dvacet let před ním, nestojí za to řešit. Tedy že to všechno on sám už vymyslel a prověřil praxí.

Bezpochyby toto hodnocení ukazuje, že nejsem výborný prezentátor, spíše podprůměrný, protože pokud má přednáška na někom zanechala tento dojem, pak jsem to nezvládl. Naopak myslím, že podporuji iterativní způsob vývoje SW, který se osvědčil právě v NASA a v DoD (Department of Defence) již před více než 30 lety a favorizují iterativní způsoby vývoje na úkor vodopádu (konkrétně favorizuji Scrum).

Ale o tom také nechci psát. Co mají tedy tyto příspěvky společného? Otázku: Máme vymýšlet (vyvíjet) něco vlastního nebo jenom přebírat již existující?

Já si myslím, že není problém vymýšlet něco nového, zkoušet zda to funguje, to je naprosto v pořádku a znamená to MOŽNÝ pokrok. Takže není chyba, pokud někdo napíše vlastní framework nebo vymyslí vlastní způsob práce, to je naprosto v pořádku. Otázkou je, zda je tak geniální, že vymyslí něco prospěšného i pro ostatní a nebo zda to je nějaký nesmysl, který funguje pouze pro jeho zvrácený případ.

Chybná je jiná věc. Chybné je neumět si přiznat, že jsem udělal chybu a držet se dál toho nesmyslu co jsem vymyslel ... a v ideálním případě komplikovat život mnoha dalším lidem. Není špatně vyvinout vlastní framework, pokud se mi podaří někoho přesvědčit, že to je dobrý krok a dostanu na to peníze (nebo to udělám ve svém volném čase). Špatné je, pokud od začátku tvrdím, že se to povede a bude to úspěch a pokud nakonec, když se to nepovede, tak to nepřiznám.

Težko bychom, pokud by se nenašli dostateční "šílenci", dneska měli Hibernate, Spring, Groovy, Tapestry atd. Ovšem kolik je těch projektů, které se nezrealizují a nebo neprosadí? Podstatně více, to je nutno si přiznat. Ale vždyť sama evoluce se řídí přístupem "pokus vs. omyl", úspěšné přežije na úkor neúspěšného, takže proč ne my.

Takže nebojme se vymýšlet nové věci, ale buďme soudní a silní si přiznat, že se nám to nepovedlo. V ideálním případě je veledůležité zpublikovat svůj neúspěch stejně jako (možná víc než) úspěch, aby se i ostatní mohli poučit z naší chyby ...

19. května 2010

Jak na porovnávání Comparable objektů či pomocí Comparatoru

Nemít přetěžování operátorů skutečně považuji za velký problém Javy. Proč? Např. proto, že porovnávání objektů pomocí instance třídy Comparator či porovnání objektů implementující rozhraní Comparable je boj, který pernamentně prohrávám. Mějme např. dva datumy d1 a d2. Pokud chci zjistit, zda platí d1 <= d2, pak mám následující možnosti:

  • !d1.after(d2)
  • d1.compareTo(d2) <= 0
Přiznám se, že z těchto dvou variant mi přijde čitelnější ta první (druhou mi mozek nějak nebere a vždy mě stojí hrozně přemýšlení co to znamená). Ovšem metody before a after má pouze třída java.util.Date nikoliv obecná třída implementující rozhraní Comparable. Pokud budeme objekty porovnávat pomocí instance Comparator pak musíme vystačit s metodou compare.

Z tohoto důvodu jsem si udělal jednoduchý enum:

public enum Relation {
   eq, ne, lt, le, gt, ge;

   public static <C extends Comparable<? super C>> boolean rel(C c1, Relation oper, C c2) {
      switch (oper) {
         case eq:
            return c1.compareTo(c2) == 0;
         case ne:
            return c1.compareTo(c2) != 0;
         case lt:
            return c1.compareTo(c2) < 0;
         case le:
            return c1.compareTo(c2) <= 0;
         case gt:
            return c1.compareTo(c2) > 0;
         case ge:
            return c1.compareTo(c2) >= 0;
      }
      throw new IllegalArgumentException("Unsupported operation " + oper);
   }

   public static <C> boolean rel(Comparator<C> comp, C c1, Relation oper, C c2) {
      switch (oper) {
         case eq:
            return comp.compare(c1, c2) == 0;
         case ne:
            return comp.compare(c1, c2) != 0;
         case lt:
            return comp.compare(c1, c2) < 0;
         case le:
            return comp.compare(c1, c2) <= 0;
         case gt:
            return comp.compare(c1, c2) > 0;
         case ge:
            return comp.compare(c1, c2) >= 0;
      }
      throw new IllegalArgumentException("Unsupported operation " + oper);
   }
}
Díky použití tohoto enumu mohu přepsat výše uvedený příklad porovnávání dvou datumů do podoby:
rel(d1, le, d2)
(pokud použijeme statické importy na metodu rel a instanci enumu le). Protože se mi tento zápis ještě stále nezdál dostatečně výmluvný, použil jsem další vylepšení (stačí jej přidat do výše uvedeného enumu):

   protected static final Map<String, Relation> S2R;

   static {
      Map<String, Relation> tmp = new TreeMap<String, Relation>();
      tmp.put("==", eq);
      tmp.put("!=", ne);
      tmp.put("<=", le);
      tmp.put("<", lt);
      tmp.put(">=", ge);
      tmp.put(">", gt);
      S2R = unmodifiableMap(tmp);
   }

   public static <C extends Comparable<? super C>> boolean rel(C c1, String oper, C c2) {
      return rel(c1, S2R.get(oper), c2);
   }

   public static <C> boolean rel(Comparator<C> comp, C c1, String oper, C c2) {
      return rel(comp, c1, S2R.get(oper), c2);
   }
Nyní dostaneme již poměrně elegantní zápis:
rel(d1, "<=", d2)
Je škoda, že Java nemá přetěžování operátorů, které nás vede k takovýmto vylepšením. Např. Groovy si umí poradit a operace porovnání umí zavolat nad objekty implementující Comparable. Scala je na tom podobně díky možnosti přetížení operátorů.

5. ledna 2010

Lekce ze Scaly: Jak se vyvarovat NullPointerException

Jak se v jazyce Scala brání NullPointerException? V hojné míře se používá třída Option, která zajistí, že nemusí metoda vracet null, ale vrací existující instanci. Jak to celé funguje si ukážeme v Javě:

public abstract class Option<T> {
  public abstract T get();
  public abstract boolean isNone();
  public abstract boolean isSome();
}

Pokud potřebujeme aby nějaká metoda vracela něco a nebo null, použijeme třídu Option. Díky ní metoda vrátí existující instanci, ze které jednoduše zjistíme, zda návratová hodnota má být null (isNone() vrací true) a nebo je to nějaká smyslu plná hodnota (isSome() vrací true) a pak se k této hodnotě dostaneme pomocí metody get().

Teď už to chce jenom nějaký rozumný příklad. Vezněme si např. Map a její metodu get:

V get(T key);
Ta v případě, že klíč v mapě neexistuje vrací null. Takže typicky píšeme něco jako:

String value = map.get(key);
if (value != null) {
  System.out.println(value);
} else {
  System.out.println("Nic");
}

A jak by tedy měla metoda get objektu Map vypadat kdybychom byli ve Scale:

Option<V> get(T key);
A nyní jak by vypadal náš kód pro čtení z mapy:

Option<String> value = map.get(key);
if (value.isSome()) {
  System.out.println(value.get());
} else {
  System.out.println("Nic");
}

A zbavili jsme se ošklivého porovnání na null, což jsme chtěli. Navíc pokud metoda vrací Option a ne přímo hodnotu je podstatně větší pravděpodobnost, že nezapomeneme otestovat zda Option něco obsahuje. Pokud metoda vrací přímo hodnotu nebo null, pak hrozí, že hodnotu rovnu použijeme a neotestujeme ji na null.

A nyní přistupme k implementaci:

public class None<T> extends Option<T> {

  public T get() {
    throw new UnsupportedOperationException("The None had no value");
  }

  public boolean isNone() {
    return true;
  }

  public boolean isSome() {
    return false;
  }

  public boolean equals(Object obj) {
    return ((obj == this) || (obj instanceof None));
  }

 public int hashCode() {
   return 1;
  }

}

public class Some<T> extends Option<T> {
  protected T value;
 
  public Some(T value) {
    super();
    this.value = value;
  }

  public T get() {
    return value;
  }

  public boolean isNone() {
    return false;
  }

  public boolean isSome() {
    return true;
  }

  public boolean equals(Object obj) {
    if (obj == this) return true;
    if (obj instanceof Some) {
      Some other = (Some) obj;
      return value.equals(other.value);
    }
    return false;
  }

  public int hashCode() {
    return value.hashCode();
  }
 
}

A jak to celé funguje ve Scale? Díky pattern matchingu a type inference ještě elegantněji:

val value = map.get(key) 
value match {
  case None => print "Nic"
  case Some(x) => print x
}

1. října 2009

Gradle - druhý krůček

V prvním popisku použití gradlu jsem si ukázali jak na jednoduchý projekt, dneska se podíváme, jak jsme zbuildovali projekt do waru.

Vyjdeme z předcházejícího příkladu. Co musíme změnit, abychom měli jako výsledek projektu war, ve správném layoutu a ne jar? Je toho pekelně málo.


usePlugin "java"
usePlugin "maven"
usePlugin 'eclipse'
usePlugin 'war'

sourceCompatibility = 1.6
group = "cz.svt"
version = "${version}"

manifest.mainAttributes "Implementation-Title": name, ...

configurations {
deployerJars
}

dependencies {
compile fileTree(dir: "lib", includes: [ "util.jar", "JMSMailer.jar"])

compile "org.apache.activemq:activemq-core:5.2.0"
...

providedCompile "javax.servlet:servlet-api:2.4"

runtime "datedFileAppender:datedFileAppender:1.0.2"

deployerJars "org.apache.maven.wagon:wagon-http:1.0-beta-2"
}

repositories {
...
}

uploadArchives {
...
}


Tam kde jsou uvedeny 3 tečky jsem build zkrátil, buď protože obsah je totožný s buildem z minula a nebo výpis nepřináší nic zajímavého. Takže co se fakticky změnilo? Jeden řádek. Přidali jsme na úvod:


usePlugin 'war'


To je celé. Navíc jsme do struktury adresářů přidali adresáře src/main/webapp/META-INF a src/main/webapp/WEB-INF. Mě to přijde úžasně jednoduché a musím říct, že se mi gradle líbí víc a víc.

CZJUG - Hans Dockter - Gradle

Po velmi dlouhém čase jsem se dostal na CZJUG. Nelituju, spíš lituju, že mi to termínově nevychází se tam dostávat častěji. Přednáška o gradle byla hodně zajímavá. Druhou přednášku o MPS jsem nepochopil, jestli to bylo tím pivem nevím. Samozřejmě jsem rád, že jsem mohl potkat staré známé z java komunity.

Hans pojmul přednášku tak jak jsem to nečekal a bylo to hodně zajímavé. V podstatě na úvod nám ukázal jak je gradle inteligentní. Na příkladu přidání source directory do projektu nám ukázal jak na 4 řádcích je možné vyjádřit vše. Závislosti (interní, externí), vytvoření tasků na přeložení, závislostí mezi tasky. Pak přislo na pořad dne základní motivace a tím pádem trochu srovnání s ANTem a mavenem.

Dále se mluvilo o výhodách v podobě vyhodnocování tásků, které se spustí, o multiprojektech atd. Dále jsme se dozvěděli i něco o budoucnosti (především se na nás chystá spolehlivý build bez clean, tj. že gradle bude shopen zaručeně identifikovat co se změnilo a co je díky tomu nutno zbuildovat).

Nosná myšlenka, pro mě, je, že gradle se snaží být takovým vylepšeným mavenem, tj. používá intenzivně standardní layout projektu, standardní lifecycle buildu, ale navíc nabízí neuvěřitelnou flexibilitu, vše je možno změnit.

Pro mě je to cesta správným směrem, byť to vyžaduje kontrolu nad tím, jak je gradle používán, aby se nám build nevymkl z ruky. Navíc je gradle stále mladý a hodně se mění, takže použití v dnešní době není úplně bezproblémové. Ale je to systém hodně perspektivní.

Pokud někoho obsah přednášky zaujal, pak se vše nahrávalo, takže se můžete kouknout na video (odkaz na něj jistě bude na stránkách CZJUGu).