Archive for February, 2009

YAGNI:s onda tvilling

You Aint Gonna Need It är en sund princip för mjukvaruutveckling, och är dessutom naturligt integrerad i de agila metodologierna: Vi väljer den feature/story som anses viktigast för affärerna och implementerar den. Det andra får lägre prioritet, och implementeras om det behövs. För att införa ett nytt begrepp, låt oss kalla detta makro-YAGNI och likställa med vanlig verksamhetsdriven prioritering.

Lillebror, som jag självklart kallar mikro-YAGNI, är det vi hittar på Wikipedia när vi söker på YAGNI, alltså att endast implementera den funktionalitet som krävs för att lösa det omedelbara problemet, och inte ge sig på generella, framtidssäkra, gärna ramverksbaserade lösningar (på problem som ändå kommer att se helt annorlunda ut om en månad). YAGNI skyddar således mot överkreativitet och feature creeping. Man kan länge diskutera huruvida jagande att ett ‘lokalt minimum” leder till att vi utvecklar bra system, eftersom vi teoretiskt saknar helikoptervyn, men jag tror att ju bättre vi är på refactoring, desto smidigare går det att förflytta sig från design till design och forma systemet i den riktning man vill. Tror man inte på mig, kan jag dra till med ett citat av Kent Beck (tror jag): By not considering the future of your code, you make your code much more likely to be adaptable in the future”. Genom TDD integrerar vi dessutom principen naturligt i vår utveckling, eftersom ingen vill testdriva fram ett helt ramverk om resultatet bara ska bli “Hello world”.

Allt med YAGNI är såklart inte svartvitt och oproblematiskt, och den intresserade kan googla vidare (det finns ett antal relevanta invändningar), för ämnet för inlägget är tredje medlemmen i familjen: den onda tvillingen.

Den onda tvillingen frammanas så klart av onda människor. Dessa människor menar att vi kan tillämpa YAGNI-principen även på kvaliteten, och inte bara på scope. De har nämligen lärt sig att utvecklare älskar att övergeneralisera problem och gör allt i sin makt för att inte leverera affärsnytta. Verktyget mot detta är således: enhetstester – You Aint Gonna Need It, code reviews – You Aint Gonna Need It, infrastruktur för enkel automatisk deployment … Självklart är en del av kritiken befogad – ingen rök utan eld, men det jag försöker komma åt här är behovet av att utveckla den pedagogiska förmågan som behövs för att förklara vad som verkligen behövs och vad som kan tummas på… (och förstå det själv).

No Comments

Sätt en siffra på kvaliteten

Detta inlägg förtjänar en helt egen kategori, och jag misstänker att det kommer skapas flera i denna kategori. Som vi vet finns det otroligt många definitioner av kvalitet. Det vore kul att diskutera dem, men temat för dagens inlägg är ett mer konkret case.

Som vi också vet finns det projektledare i branschen som någonstans lär sig att kvalitet är en variabel, precis som tid och scope, som kan varieras. Man hör uttalanden som “good enough”, “det behöver inte vara snyggt, det behöver bara funka” m m. Dessa förtäcker egentligen antagnadet att kvaliteten har fått ett värde mellan noll och ett.

Vi kan hjälpa vår projektledare/chef genom att be om ett värde och ett antal parametrar som kommer att anta detta värde. Låt oss exemplifiera med 80%-ig kvalitet. Det kan t ex betyda 20% nertid, 20% felaktiga skrivningar i databasen, 20% slumpmässig korruption av interna data, 20% av beräkningarna blir fel, eller att beräkningarna blir 20% fel. Vi kan således ställa upp en tabell och rita färgglada staplar, som illustrerar vår målkvalitet. Det borde inte vara så många som committar sig till detta.

Argumentet ovan löser 95% av fallen, men det är bra att diskutera de resterande fem procenten.

1) Otroligt sällsynt, men det finns personer/organisationer som kan committa sig bristande kvalitet, speciellt i kvantativa sammanhang. Alltså: Vi ringer 10000 personer och försöker kränga vår pryl. Vi struntar om 2000 telefonnummer är fel.

2) Tid och scope har verkligen tagit slut, och kan inte varieras. Detta är väldigt sällsynt. Scope kan alltid skäras…

3) Det kända engångshacket. Se då till att inte spara filen med hacket och glöm allt som har med versionshantering att göra.

Det är bra att tänka på de tre antiteserna ovan (säker finns det någon till), men det är inte de som är problemet i kvalitetsdiskussionen med den oinsatte.

No Comments

Gör inte det här med equals()

Det var ganska svårt att sätta rubriken för detta inlägg. Jag vet nämligen inte än idag hur man felklassificerar detta case. Litteraturen säger i alla fall att man ska vara försiktig med hur man implementerar equals, och detta praktiska case belyser varför :)

Beroende på hur man är lagd är kontraktet för equals ganska logiskt. Relationen ska vara reflexiv, symmetrisk, transitiv, konsistent och ge false i null-jämförelser (ok, själv brukar jag bara komma ihåg de tre första av dessa fem punkter).

Antag nu att vi har en rad i en databas med utseendet: k1|k2|k3|a1|a2|…|an|. Attributen k1-k3 bildar tillsammans en sammansatt primärnyckel. Vi ämnar nu lyfta in denna rad i ett Java-objekt med vårt favoritpersistens-API, och är dessutom duktiga och definierar en egen klass för den sammansatta nyckeln. Vi låter IDE:n generera equals()och hashCode() för nyckelklassen, och allt är frid och fröjd.

Vi skapar även en klass som innehåller fält för radens övriga attribut, alltså a1-an, samt en instans av primärnyckeln. Denna klass ska alltså avbilda hela databastupeln. Eftersom den sammansatta nyckeln är primärnyckel i databasen skriver vi en equals i den nya klassen så att den delegerar till nyckelklassens equals (och samma förhashCode). Logiskt …?

Nu börjar festen! Eftersom vi arbetar testdrivet skriver vi ett gäng tester som testar en intrikat algoritm som tar en mängd instanser av tupelklassen och returnerar en annan mängd av instanser av samma klass efter att ha trixat och fixat lite. Vi vet dessutom att det snygga sättet att göra detta på är att sätta upp en förväntad mängd i testet, köra algoritmen och jämföra med det förväntade. En enda assertEquals() alltså. Resultatet blir …. en massa falska positiva tester!

Den intrikata algoritmen skapade nämligen mängderna på ett sätt som satte förväntade värden på nyckelobjekten, men olika värden på attributen a1-an. Vi har alltså satt och i en situation där equals-implentationen må vara korrekt i “databasdomänen”, men helt fel Javadomänen, där den inte uppfyller någon av de tre första villkoren för en equals-implentation.

En öppen fråga är: Vad är fel?
1) En primärnyckel används bara för att unikt identifiera en rad i en databas eller colllection och får inte användas för jämförelser.
2) Beslutet att frikoppla primärnyckeln och kapsla in den i en egen klass.
3) Beslutet att delegera equals och hashCode till denna klass implementation.
4) Testerna. De skapar artificiella objekt som inte hade skapats av “den riktiga databasen”.
5) Dålig testimplementation. Sjävklart borde vi ha skrivit en egen matcher för denna specialla klass.
6) Något annat.

I alla fall… Fel vart det. Ett svårrättat fel, som tog lång tid att förstå :(

No Comments

Gillar vi Law of Demeter?

Eftersom bloggen är ny och hemlig när detta skrivs, är det ingen som kommenterar inläggen, men om detta inlägg någonsin läses, så är jag väldigt nyfiken på folks erfarenheter.

I alla år har jag tyckt att Law of Demeter har känts som en olustig designprincip. I teorin låter den vettig, men någonting har alltid tagit emot. För dem som inte kommer ihåg principen och inte orkar googla, så betyder den (i korthet) att man bara får göra anrop på ett objekts metoder, men inte dess ingående eller relaterade objekt. Alltså t ex car.turn() och inte car.getWheel().turn(). Detta får till följd att de “yttre” objektet måste tillhandahålla ett gäng delegatmetoder, som i exemplet ovan void turn() {getWheel().turn();}. Förespråkarna menar att detta är ett billigt pris för att reducera klassers kunskap om varandra och öka inkapslingen.

Mina erfarenheter kring delegatbiten är att delegatmetoder inte ser så bra ut i praktiken som i designböckerna. Man får ett gäng till synes meningslösa metoder, ökar en klass ansvarsområde och följaktligen minskar dess cohesion. Testtäckningen sjunker på klassnivå, eftersom delegatmetoder faller inom ramen för “too simple to break”. Detta är ett kosmetiskt problem, men ändå.

Å andra sidan är tanken bakom Law of Demeter att designen ska växa fram underifrån, vilket rimmar väl med TDD, vilket i sin tur med stor förmodan leder till att man verkligen först hade designat den “yttre” klassen, och sedan refactorat in de inre allteftersom de kom att behövas. I bilexemplet hade man förmodligen enligt YAGNI+TDD verkligen först implementerat turn() i bilklassen, och kanske sedan kommit på att en rattklass också behövs.

Å det tredje kan det kanske också vara bra att ha en princip som uttryckligen förbjuder spagettiprogrammering (här tänker jag främst på program skrivna i VB av någon anledning), speciellt om man lär ut programmering.

Man kan också vända på steken och fråga om trotsande av Law of Demeter är ett problem i praktiken. En någorlunda bra programmerare kommer inte att skriva spagetti, dels för att det är fult, dels för att koden blir svår att testa; det blir svårt att rigga alla relationer i testfixturen…

Enmannajuryn är långt ifrån enig, men det är ganska kul att ha strukturerat sina tankar kring denna princip i skrift. Droppa gärna en eller flera kommentarer om denna designprincip.

1 Comment

Kommentar överflödig

Här bryter jag mot principen att inte skriva i bloggen på arbetstid, men efter en halvtimmes letande efter funktionalitet dold i en misshandlad EJB 2-implementation hittar jag denna hjälpsamma klasskommentar :(

/** 
 * This is a delegate class the encapsulates the calls to the session facades 
 */

No Comments

Trove

Detta bilbiotek kommer inte så ofta till pass om man kodar “vanlig” enterprise Java, men de få gånger man behöver lagra stora mängder siffror i en collection, t ex databasnycklar eller liknande hjälper Trove  (http://trove4j.sourceforge.net) oss att spara minne.

Trove är en implementation av Javas collection framework med endast primitiva datatyper, t ex kommer TShortShortHashMap att mappa en short till en short.

Jag har inte kört bilbioteket de senaste två åren, men det är fortfarande ett projekt under utveckling, och i och med att det fungerade bra för två år sedan, så lär det funka än idag. Om prestandan är så bra som paketets hemsida påstår vet jag inte, men minne sparar man i alla fall.

No Comments

Agila metoder kan inte fungera utan refactoring

Detta kanske är så självklart att det är pinsamt att nämna, men det är bra att ha en stark rubrik för att inte glömma det basala.

En av premisserna bakom agila metoder är att man inte planerar mer än nödvändigt för att klara den kommande iterationen. Detta leder till att man kan hamna i situationen där en planerad modifiering, som verkade vara ett lätt och avgränsat arbetspaket, i själva verket påverkar mer av systemet är man önskar. I detta läge har man två val:

1) Skygglappar på; bygga in sin modifikation “på bredden”, vilket medför lite copy’n paste, lite duplicerad funktionalitet, men också att vi arbetar utanför fungerande delar av systemet och kan i 99% av fallet inte paja något som fungerar.

2) Stanna upp, tänka efter, göra om, göra rätt. Vi stannar till och gör vårt yttersta för att få det existarande systemet att innefatta den nya logiken. Detta gör vi genom refactoring. Är systemet vi underhåller väldigt dåligt och saknar testtäckning, får vi kanske träna en hel del på dependency breaking också. I ett bra system finns redan tillräcklig testtäckning för att på ett säkert sätt genomföra sin refactoring.

Metod 1 brukar användas om vi inte vet någonting om systemet vi modifierar (och det saknar tester), och brukar framför allt förespråkas av okunniga projektledare som tror att de kan det där med “business”, kundnytta, och “good enough software”.

Metod 2 är det enda sättet att få lättrörlighet (och underhåll) att fungera på sikt. Det är nämligen så att byggande på bredden, som jag kallar det, leder till att vi får system med moduler som gör snarlika saker (t ex fyra olika klasser gör nästan samma sak), som per definition får sin konceptuella integritet urholkad, och som blir svåra att dokumentera och underhålla. Läggs detta ihop med faktum att 70+ procent av alla kostnader för ett system är underhållsrelaterade, så behövs inga fler argument.

Hur vi sköter vår oförutsedda refactoring inom iterationen är en stilfråga. Antingen accepterar vi att detta är priset för agila metoder och reviderar tidsestimatet för arbetspaketet, eller så skapar vi en så kallad “tech story” och börjar arbeta med den. Själv förespråkar jag det första alternativet med argumentet att vi inte lyfter fram faktum att vi skriver en for-loop, och vi behöver inte lyfta fram faktum att vi anpassar det existernade systemet för att fungera med vår nya funktionalitet.

Det man absolut inte får göra är att skjuta fram sin refactoring till nästa iteration, för då hamnar man i “metod 1″ och får det aldrig gjort!

No Comments

DbUnit

DbUnit (www.dbunit.org) är ett verktyg som underlättar testning av främst CRUD-operationer. För att göra en halvkort historia kort, så hjälper paketet till med att utifrån XML-filer populera en databas och köra mer avancerade asserts på tabelldata.

Vid första anblicken är paketet enkelt att använda, men det stupar, liksom många små open source-verktyg på detaljerna. Det är lätt att få triviala test att köra, men jag upplevde steget från det, till att skapa fungerande test som skalar och är enkla att underhålla, som stort. En av de mest störande sakerna var att det fanns många olika sätt att utföra grundfunktionaliteten på, vilket ställde till det i mer avancerade test. Till exempel var det bökigt att få till laddning av olika dataset beroende på testmetod, men det verkar finnas hjälppaket för detta. Kring november 2009, fanns det en hel del enkla buggar i paketet, så man fick lägga lite tid på att debugga källkoden för att fatta vad som faktiskt pågick.

Sammanfattningsvis: Verktyget är allt annat än en silver bullet. Att få till triviala fall är enkelt, att få till användbara test är förknippat med en inlärningskurva i storleksordningen timmar/dagar. Det är svårt att sätta siffror på detta, men jag uppskattar att DbUnit är 10-15% effektivare är underhåll av egna dataset i ren SQL. Lägger man dock tiden på att skriva ett bra lättviktsramverk för sin SQL så…

Rekommendationen är “kanske”.

No Comments

Syndaren som använder protected static bekänner!

Vi börjar direkt med ett trivialt inlägg, vars innehåll ändå har ett stort sentimentalt värde för mig.

Eftersom jag gillar detaljer, och ogillar konstiga saker i kod, har jag i många år undrat vad en protected static-deklaration innebär, eller rättare sagt hur den som använder den tänker (för alla vet vi ju vad den innebär ;) ).

Jag har äntligen lyckats haffa en syndare på bar gärning och fått en förklaring: protected static använder man för att man är rädd för default access och tycker att private är för lite och public för mycket. Logiskt.

1 Comment