09.19.08
Hints for getting JBehave2 up and running
I started fiddling a bit with the new JBehave2 yesterday. It looks quite a bit like RSpec for Java, which sounds great on paper. Since I have a long history with Fitnesse I would be interested to try another approach.
To get started with JBehave, me and my pair programming partner Måns decided to try and get the example from the JBehave home page to work. We had 30 minutes to do it. Three classes and a one text file. We should be able to do it! We failed (of course).
Back home I took a second look at the problem and managed to get a first example working (not exactly the one from the JBehave home page, but more of a Hello World example). It was a bit tricky though so I thought I would share some experiences.
This my (pathetic) little scenario:
Given I have a greeting system
When I give my name as Måns
Then I should see a greeting, ”Hello, Måns!”
Here are some hints to get your first example working:
- Don’t put your code in the default package. We did this. But putting your code into the default package leads to spurious NullPointerException on line 36 of the UnderscoredCamelCaseResolver. (This line looks a little train wreckish, which is ironic in a BDD package.)
String packageDir = scenarioClass.getPackage().getName().replaceAll(DOT_REGEX, SLASH);
- Quotes in the test definition must be escaped by a backslash in the Java annotation to actually work. In the sample scenario text file a message is surrounded by double quotes. The sample code on the start page will not work but \”$message\” will.
- It is mentioned on the JBehave home page, but worth repeating, that some versions of Eclipse (my version of Ganymede at least) cannot run the JBehave scenario out of the box. The reason for this is apperently that the JUnit runner cannot find the @Test annotation inside the JBehave core jar. (This sounds like a bug in Eclipse to me.) To fix it now you need to download and attach the source code for JBehave core to the JAR file. This is done in the project properties Java Build Path editor. Open the jar and indicate to Eclipse the location of the source archive. Now Eclipse should see the @Test annotation and you can run the scenario.
- The example code mentions a ”matcher” method called containsMessage. Matchers are used to create specifications and assertions. This is probably a typo, why would there be a general matcher method with that name? Or maybe I have misunderstood? However, the Hamcrest matchers do contain a matcher method called containsString, which sounds a bit more plausible. Change the name.
- To get the containsString matcher working I had to download the hamcrest-all-1.1.jar and put it on the class path. I believe that the Hamcrest core matchers are part of JUnit 4.4+ but the containsString matcher is part of the hamcrest text matchers. Make a static import of org.hamcrest.Matchers.* to access all matchers.
For non-english speakers you will be happy to know that characters outside the ASCII set works. Now I need to find a way to translate the keywords ”Given”, ”When” and ”Then” to Swedish to get a full Swedish example working. Good luck!
09.10.08
Varför uppstår överdesign i mjukvara?
I mitt förra inlägg pratade jag lite om överdesign och att det faktiskt också är en form av teknisk skuld som vi gör bäst i att hålla borta från våra system. I detta inlägg tänkte jag titta närmare på orsakerna till varför överdesign uppstår.
Jag tänker använda tekniken med ”5 varför” för att försöka bringa litet klarhet i frågan om varför överdesign kan uppstå. Syftet med denna teknik är att försöka hitta rotorsaken till varför något inträffar och inte bara de på ytan synliga. Detta gör man genom att fråga sig själv ”Varför?” fem gånger.
1. Varför uppstår överdesignade system?
- Inte av sig själv. Därför att någon inför element som leder till överdesign, eller ser till att någon annan inför dem.
2. Varför skulle någon införa element som leder till överdesign?
- Första orsaken: Därför att personen tror att designen måste vara hållbar för framtiden, att man måste ”ta höjd” för ditten och datten.
- Andra vanliga: Därför att det är frestande att göra en ”snygg design” om man inte programmerar utan kan ägna mycket tid åt att tänka ut snygga lösningar.
Ok, två vanliga orsaker. Vi får dela upp oss i två spår härifrån. Låt oss börja med framtidsaspekten:
3.A Varför tror en person att design måste vara hållbar för framtiden?
- Därför att denne inte förstår TDD, refaktorisering, evolutionär design och andra moderna arbetssätt för programmerare på djupet. Att man faktiskt kan låta den rätta designen växa fram och inte behöver skapa en perfekt sådan från början.
4.A Varför förstår en person inte moderna, programmerarnära arbetssätt?
- Vanligtvis därför att personen inte har någon egen erfarenhet av dem samt att det inte finns någon erfaren programmerare i närheten som kan denne kan lära av.
5.A Varför designar en person som inte har någon erfarenhet av moderna arbetssätt för programmering och varför finns inte personer som förstår detta på samma plats?
- Därför att ledningen inte har organiserat arbetet på det sättet. De belönar också duktiga programmerare till arkitekter som då inte ska behöva ”hacka kod” längre.
Aha, det kanske i alla fall delvis är en organisationsfråga. Låt oss titta på det andra spåret:
3.B Varför är det frestande att tänka ut en ”snygg design” om man inte programmerar?
- Teknisk orsak: Därför att om man inte programmerar slipper man ta hänsyn till ”implementationsdetaljer”, vilket på papperet förefaller vara detaljer men som kan visa sig påverka hela lösningen.
- Humanpsykologisk orsak: Därför att komplicerade designer förefaller imponerande på andra personer som vi känner behov av att imponera på, våra likar och våra chefer.
Oj oj, återigen får vi förgrena oss i två spår:
4.B.I Varför skulle en person slippa att ta hänsyn till implementationsdetaljer?
- För att slippa undan konsekvenserna av designbeslut måste designern vara avskild från personerna som ska implementera lösningen. Personen är alltså inte medlem i själva utvecklingsteamet.
5.B.I Varför finns inte personer som förstår god design och programmering på samma plats, i samma team?
- Därför att ledningen inte har organiserat arbetet på det sättet. De belönar också duktiga programmerare till arkitekter som då inte ska behöva ”hacka kod” längre.
Aj då, samma svar igen. Låt oss följa den andra delen av det andra spåret:
4.B.II Varför förefaller en komplicerad design att vara mer imponerande?
- Ett ganska mänskligt drag, men också därför att personer inte kopplar designen till det behov den ska lösa. De som har behov förstår inte design och de som kan design är inte så intresserade av behov. Behov och design är frånkopplade.
5.B.II Varför finns inte personer som förstår behov och god design på samma plats, i samma team?
- Därför att ledningen inte har organiserat arbetet på det sättet.
Man kan diskutera denna analys, men enligt den här analysen skulle alltså rotorsakerna gå att finna i hur vi organiserar och belönar våra medarbetare. Det är alltså ett systemproblem som enbart kan lösas av ledningen och inte kan lösas av individuella medarbetare.
Precis som analysen ovan antyder är ett vanligt antimönster att alla erfarna systemarkitekter samlas i en separat arkitekturgrupp som mest dikterar villkoren för teamen och bara sporadiskt besöker dem för att släppa lite nya dekret.
Detta är olyckligt på många plan. Systemarkitekterna skulle behöva fortsätta utvecklas och förstå implementationsdetaljer. Inom programdesign bor djävulen i detaljerna, som man säger. De unga programmerarna, å andra sidan, behöver utvecklas till goda designers. Tyvärr fråntas de möjligheten att se experter i arbete på nära håll då experterna snabbt lyfts ur teamen. Enligt mig är en bättre lösning att göra precis tvärtom: Låt arkitekterna deltaga i teamarbetet och i stället regelbundet samlas och dela erfarenheter med andra systemarkitekter.
Till sist behöver båda dessa roller förstå vad kravtrycket är på systemet och hur detta påverkar designen. Den andra delen av denna analys kokar ner till att systemarkitekter utan kontakt med resonemang kring nytta, värde och behov inte har någon som påminner dem om vad som är viktigt och att saker och ting faktiskt kostar pengar. De blir förälskade i sin makalösa designkreativitet och det finns ingen bredvid som kan ruska om dem och skrika ”Hallå!”. De bevisar sin kunnighet för sina arkitektkollegor med gigantiska utskrifter av komplicerade diagram och det finns ingen där som stampar med foten och kräver nyttig, fungerande mjukvara, nu!
Behov, krav, design, kod, test. Allt hänger ihop och all separation av dessa förefaller mig bidra till sämre kompetensutveckling och sämre system.
09.02.08
Överdesign är teknisk skuld
Det är inte svårt att inse att ett dåligt designat system har problem. Vi brukar kalla detta för teknisk skuld. För att städa upp i röran krävs en återbetalning av skulden i form av designförbättringar. En sak som inte pratas om så mycket är att även ett ”framtidssäkert” (oj, det ordet ger mig fysiska rysningar), väldesignat system som väcker alla IT-arkitekters avund ofta lider av teknisk skuld. Hur då?
Underdesign
Begreppet teknisk skuld myntades av Ward Cunningham som en liknelse över det förfall, den ”kodröta”, som våra mjukvarubaserade produkter och system under utveckling utsätts för. Förfallet finns i många former, till exempel duplicering av kod, kod som är svår att förstå, otestbar kod, kod med defekter samt kod som är bräcklig.
Om vi inte kämpar emot detta förfall kommer vår tekniska skuld att växa i takt med att vårt system blir allt mer komplext. Räntan på skulden betalar vi i form av lägre produktivitet, då utvecklarna tvingas arbeta trots eller runt dessa problem. Det är häpnadsväckande nog inte ovanligt att man ignorerar problemen och låter skulden växa så långt att systemet inte går att vidareutveckla längre. Man kan uttrycka det så att räntan är större än det kapital vi kan sätta in. Systemutvecklare brukar kalla detta för ett ”legacysystem”; ett binärt vrak.
Bästa strategin för att betala av på den tekniska skulden brukar vara att aldrig låta den växa sig för stor. Agila team använder tekniker som refaktorisering, vilket innebär en ständig förbättring och vidareutveckling av vår design och kod utan att ändra dess externa beteende. Med refaktorisering kan vi till exempel se till att kalla saker vid deras rätta namn och att faktorisera koden i lagom stora sjok med tydliga, logiska ansvar.
Ovanstående resonemang brukar de flesta vara med på och förstå. För programmerare är det oftast glasklart: Om vi har många defekter i våra system, dålig kod eller design så har vi en teknisk skuld, punkt slut. Vi kan sammanfatta denna typ av teknisk skuld som underdesign. Med det menar jag att produkten är undermåligt designad. I dålig design lägger jag då även saker som defekter och kodkvalitet.
Överdesign
Det är lätt att tänka så här: Om underdesign leder till teknisk skuld borde jag alltså jobba stenhårt med att få till en generell och flexibel design som tar höjd och klarar alla rimliga framtida krav på produkten? Fel! Absolut inte.
Vad som händer när vi gör detta är nämligen något som vi kan kalla för överdesign. Överdesign innebär att vi har en design som inte bär sin egen vikt. Systemet är för komplicerat eller för generellt designat i relation till de krav som vi har på ett system. Ett system som ska betjäna 500 samtidiga bankkunder har inte samma krav på sin inre design som ett hemmabygge för att katalogisera sina filmer. Ganska självklart. Man kan se det så här: Överdesign innebär att vi har arbetat för hårt på systemets teknik i förhållande till dess nytta och användningssituation.
Ok, här kommer till slut min poäng: Även överdesign är skadlig för vårt system. Skälet till detta är att överdesign alltid innebär en större komplexitet, dvs en design som är generellare eller innehåller fler element och mekanismer än den behöver. Bara två saker kan hända med sådan extra komplexitet: 1. Den behövs, fast senare. Då har vi dragits med komplexitet i onödan under en period. 2. Den behövs aldrig. Då var arbetet dessutom onödigt till att börja med. Inget av fallen är önskvärda.
Låt oss ta ett trivialt exempel för att åskådliggöra. Ett projekt som jag har insyn i valde att kalla en passage som en bil gör vid en vägtull för en ”transaktion” istället för något naturligare, till exempel ”passage”. Visst, ”transaktion” är en generellare term och användbar i flera sammanhang. Men det är också lite svårare att förstå. Varje gång någon läser ordet ”transaktion” måste personen göra en översättning i huvudet till ”passage”. Vi har skapat ett mentalt avstånd mellan namnet och vad det representerar. Det kan låta som en löjlig sak, men multiplicera detta med varje term och varje gång det händer och vi inser att vår förståelse av systemet är skadad.
Vi fortsätter resonemanget: Högre komplexitet betyder i sin tur något lägre produktivitet. Om en del i systemet är mer komplicerat designad så är den också svårare att förstå och arbeta med. Om vi till exempel har överanvänt designmönster så är kanske systemet lite flexiblare men det är också svårare att förstå. När något är svårare att arbeta med än det behöver vara får vi lägre produktivitet än vi behöver ha. Vi kan givetvis förbättra vår situation genom att i det här fallet förenkla vår design, göra den mer naturlig och smidig att arbeta med.
Hm, det här resonemanget känner vi igen. Den kanske något förvånande slutsatsen är att överdesign också är en form av teknisk skuld.
Lagom design
Idealet för en design är varken underdesign eller överdesign utan en balansering på slak lina däremellan. Vi kan kalla det för lagom design. En lagom design är så enkel som möjligt; lätt att förstå, faktoriserad och minimal, men inte enklare. Det är vårt ideal att sträva efter.
En följdsats till detta är att när vi bedömer en design måste vi relatera den till de krav som vi har på systemet. Det finns helt enkelt ingen objektivt bra design, bara bra design relaterat till behoven. Tänk på det nästa gång någon slänger ett komplicerat klassdiagram i ansiktet på dig och väntar på ditt smackande beröm.