Søg på DotNyt:
Denne blog er flyttet til www.nielsbrinch.com


onsdag den 24. juni 2009

Produktudviklingens kompleksitetsfaktor

skrevet af Niels Brinch

Dette er en tanke jeg har bearbejdet over længere tid. Jeg har bemærket, at et bestemt mønster ser ud til at gøre sig gældende i softwareprodukters levetid. Her tænker jeg ikke på et projekt, som oftest er mere eller mindre planlagt fra start til slut, men på et produkt som udvikler sig løbende i takt med kundebehov og idéer.

I mit simplificerede eksempel, som jeg vil bruge til at beskrive min tanke, antager jeg at produktet opbygges én funktion ad gangen, uden tanke på hvad den næste funktion kunne være. Endvidere antager jeg at alle funktioner i sig selv er lige komplekse.

Det som oftest viser sig at være tilfældet er, at når man skal tilføje den anden funktion til et produkt, tager den alt andet lige lidt længere tid end den første gjorde, fordi den anden skal tage hensyn til den første funktion. Den første funktion behøvede ikke tage hensyn til den anden funktion. Den tredje funktion er endnu mere kompleks at tilføje, fordi den skal tage hensyn til de to første funktioner. Efterhånden som der kommer flere og flere funktioner, bliver de mere og mere komplekse at tilføje – de er ikke i sig selv mere komplekse, men de skal tage hensyn til flere og flere andre funktioner.

Resultatet er en graf som nedenstående, hvor y-aksen er udviklingstimer – d.v.s ressourceforbrug.

image

Grafen illustrerer ret godt, at hvis man fortsætter med at føje flere og flere funktioner til produktet, bliver hver ny funktion dyrere og dyrere – og omkostningerne stiger eksponentielt!

Mange systemudviklere har umiddelbart mere lyst til at starte forfra på et produkt end at bygge videre på et eksisterende. Årsagen ser du herover. En systemudvikler, som så mange andre mennesker, kan godt lide at se resultater – og resultaterne er sværere at fremvise jo stejlere kurven er.

Ovenstående illustration af produktudviklingens kompleksitetsfaktor er min hovedpointe. Alligevel trænger nedenstående spørgsmål sig på – og jeg vil forsøge at svare på det.

Hvad kan der gøres for at undgå at skulle fortsætte ud af den meget stejle kurve vist herover?

1. Fjern unødige funktioner

Hvis den stejle kurve kan synes uoverstigelig, er det formentlig den hurtigste udvej at fjerne unødige funktioner. Kompleksitetsfaktoren vil stadig være den samme så kurven vil fortsætte uændret, men med færre funktioner at tage hensyn til, vil nye funktioner være simplere at tilføje.

Prisen er selvfølgelig, at man er nødt til at fjerne funktioner, selvom de fungerer helt fint.

image

Det kan være svært at få sig selv til at acceptere, at man skal fjerne nogle funktioner som fungerer ligesom de skal, men hvis man stiller det op på den måde, at man må vælge mellem de gamle funktioner som ingen bruger og de nye funktioner som alle brugerne skriger efter, bliver valget pludselig lettere.

2. Følg en kodekonvention

En måde at sænke kompleksitetsfaktoren for udviklingen af et produkt er, at følge en kodekonvention. Når alle dele af produktet følger den samme kodekonvention og dermed er mere ensartet, går det hurtigere med at forstå de eksisterende funktioner når der skal udvikles en ny funktion.

Herunder illustreret med en graf, hvor den blå streg repræsenterer nøjagtig samme slutprodukt som den røde, men ved den blå anvendes samme veldefinerede kodekonvention gennem hele produktets levetid.

image

Der er andre relaterede fordele ved at følge en god kodekonvention, såsom lettere samarbejde mellem forskellige systemudviklere og anvendelse af god praksis som giver et mere langtidsholdbart slutprodukt. Det fantastiske ved at vælge at følge en kodekonvention er, at det ikke tager ekstra tid at gøre det, når de deltagende udviklere først har lært at følge den.

3. Gennemtænkt forarbejde

Selvom man ikke kender den næste funktion i rækken, kan man godt forberede dens ankomst. Det kan man gøre ved at gennemtænke hvordan man opbygger produktet - helst fra starten. Det er forskelligt fra produkt til produkt hvad det vil sige, men basalt set handler det om at bruge noget tid i starten på funktionalitet som forventes at kunne anvendes igen og igen. Det tager lidt længere tid at komme i gang, men til gengæld kan man ved hver ny funktion drage fordel af det arbejde som blev udført i starten.

Det er illustreret herunder med den grønne streg som starter højere oppe, men ikke stiger så hurtigt. Bemærk at den først krydser den røde streg langt henne i produktets udvikling. Hvis man i det tænkte eksempel på forhånd vidste at produktet kun ville komme til at indeholde 5 funktioner, ville tiden som blev forbrugt på det gennemtænkte forarbejde være en dårlig investering. Det er med andre ord ikke altid klogt at tænke tingene igennem før man går i gang. Der kan man bare se.

image

4. Moduler

Selve problemstillingen i hele denne tanke handler om, at et produkt bliver mere komplekst at udvide jo flere funktioner det består af. Hvis man lader være med at bygge videre på sit produkt, men i stedet starter forfra på et modul som skal anvendes i sit produkt, starter kurven fra 0 igen hvor den er næsten helt flad. Modulerne kan indgå direkte i produktet eller være eksterne moduler som anvender nogle veldefinerede grænseflader. Det kræver man har brugt tid på at muliggøre anvendelsen af moduler i sit produkt.

image

Det er selvfølgelig kun særlige typer funktioner man kan lægge ud som et nyt modul, så det er ikke så simpelt som det er illustreret herover. Moduler skal indeholde funktionalitet som er afgrænset og som ikke har for mange interaktioner med selve produktet.

I det hele taget er det ikke så simpelt, at man kan vælge én af ovenstående 4 valgmuligheder. Man bør vælge alle 4 og gerne flere.

Er der nogen der har et femte forslag til, hvad man kan gøre for at sænke produktudviklingens kompleksitetsfaktor?

5 kommentarer

5 Kommentarer:

At 25. juni 2009 kl. 15.37, Blogger Christian H. Nielsen skrev...

Hej Niels,

Du kender mig jo, så selvfølgelig har jeg nogle kommentarer :)

1) at fjerne unødvendige funktioner er helt klart en god ide, også for at gøre brugen af systemet bedre - hvorfor forvirre brugeren med funktionalitet der aldrig anvendes.

2) kode konventioner er helt sikkert et must for mig. At det er gratis vil jeg dog afslå, da man selv med dygtige velintegrerede udviklere er nød til at lave kodereviews. Det mener jeg dog ikke er noget man kommer uden om lige meget hvad, men det giver alt andet lige flere rettelser efterfølgende når man skal følge en konvention.

3) er jeg faktisk lodret uenig i, da det ikke kan lade sig gøre. YAGNI princippet lever jeg efter. Man ender alt for tit med ændrede krav, og har derfor brugt tid på at udvikle noget unødvendigt. Derudover øger det kompleksiteten unødigt fra start og er dermed stik imod din ambition om at gøre det simplere. De gode metodikker du omtaler + flere af slagsen er for mig at se det man ellers omtaler som SOLID principperne. Dem vil jeg gerne anbefale tilgengæld.

4) at bygge ting op i moduler er egentlig også indeholdt i SOLID principperne, og er samtidig en grundlæggende del af DDD - og begge dele vil jeg mene er gode retningslinier for at opnå et fleksibelt og udvidelses venligt design.

En femte oplagt kandidat er TDD/BDD. Ved at have tests på plads har man også større tryghed i forbindelse med refaktorering. Ved at have det kan man både forfine eksisterende funktionalitet (mindske kompleksitet) og man kan udvide med ny funktionalitet og have vished om at man ikke knækker noget eksisterende.

 
At 28. juni 2009 kl. 10.26, Blogger Niels Brinch skrev...

Mange tak for dine kommentarer, Christian. Jeg vil gerne have der er et scenarie som beskriver kurven under punkt 3. Det må kunne lade sig gøre at drage nytte af et forberede sig godt.

Hvad med at bruge god tid på at vælge den rigtige ORM eller en sikkerhedsmodel som er let at vedligeholde og udvide?

Måske kan TDD beskrives sådan, at man bruger lidt længere tid på hver funktion, men til gengæld er kurven fladere?

 
At 28. juni 2009 kl. 11.55, Blogger Christian H. Nielsen skrev...

For mig er der forskel på det du skriver under punkt 3 og det at vælge et godt ORM eller en sikkerhedsmodel.

At vælge et godt ORM er ikke en opgave der hører ét projekt til, men noget der går på tværs af projekter. Derved har det ikke på den måde indflydelse på det enkelte projekt.

Naturligvis skal man sørge for at træffe gode valg og skrive god fleksibel kode/vælge gode komponenter og frameworks. Det er noget andet end at prøve at forudsige hvad der kommer af funktionalitet på det enkelte projekt. Der vil man oftest gætte forkert og ende med at overdesigne alting så systemet bliver dyrt og svært at forstå. Dte er hele essensen i agil udvikling og metodikker som scrum - som jeg tror er vejen frem på langt de fleste projekter. Vandfaldsmodellen har simpelthen så mange dårlige projekter på samvittigheden at det må være bevis i sig selv for at det ikke holder.

Man kan sagtens sige at TDD gør kurven fladere ja - og dermed at den ikke stiger eksponentielt.

 
At 1. juli 2009 kl. 07.57, Blogger Niels Brinch skrev...

Jeg er ikke ude på at forsvare min formulering under punkt 3. Jeg søger bare den rette formulering som kan understøtte kurvens udvikling. På en eller anden måde, må det være muligt at gøre kurven fladere ved at gøre noget godt forarbejde.

 
At 1. juli 2009 kl. 08.35, Blogger Christian H. Nielsen skrev...

Det jeg mener er også bare at man skal passe på med det, da det meget hurtigt kan give den modsatte effekt.

Jeg tror det bedste du kan gøre for gøre kurven fladere igennem planlægning, er at investere i en arkitektur som er fleksibel og simpel. Det er her solid principperne givet er godt udgangspunkt.

Min erfaring er ellers at det er bedre at anvende an agile/lean metodik, sådan at man ikke planlægger ret langt frem. På den måde er man fleksibel overfor ændrede krav, og undgår spildtid.

 

Send en kommentar

<< Tilbage


 
Til forsiden

Niels Brinch