Djupstudie: Big Ball of Mud - Technical debt and large refactorings

Relevanta dokument
HT1 2013, FÖRELÄSNING 14 (INFÖR TENTAN)

JUnit. Ska kompletteras med kodexempel på JUnit. DD2385 Programutvecklingsteknik Några bilder till föreläsning 12 21/5 2012

SOLID är en akronym för fem stycken designprinciper som används vid mjukvaruutveckling. SOLID står för:

Föreläsning 5. När skall man använda implementationsarv? När skall man använda implementationsarv?

UML Objektdiagram. Objektorienterad modellering och design (EDAF25) Föreläsning 3. UML Sekvensdiagram. UML Objektdiagram. Agenda

Objektorienterad programmering

Introduktion. Objekt-orienterad Programmering och Design (TDA551) Alex Gerdes, HT-2016

Kursombud. Objektorienterad modellering och diskreta strukturer / design. Agile? Designprinciper EDAF10 EDA061. Lennart Andersson. Grupper för projekt

Mjukvarudesign. Designprocessen. Teknisk design. Konceptuell design

12 principer of agile practice (rörlig)

Introduktion. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Introduktion. Objekt-orienterad programmering och design (DIT952) Niklas Broberg, 2017

PMM (Process Maturity Metrics) Allmänt. Mätetal för framgångsfaktorer. 1. CM konfigurationsstyrning

Sammanfattning och Tentamensinfo Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Static vs Dynamic binding Polymorfism. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Iterativ mjukvaruutveckling. 1DV404 HT14 Jesper Andersson

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 11 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Nyttomaximering av spikes

Static vs Dynamic binding Polymorfism. Objekt-orienterad programmering och design Alex Gerdes, 2016

Chaos om datorprojekt..

Objektorienterad modellering och diskreta strukturer (EDAF10/EDA061)

Separation of Concern. Objekt-orienterad programmering och design (DIT953) Niklas Broberg / Johannes Åman Pohjola, 2018

Introduktion och OO. Objekt-orienterad Programmering och Design (TDA552) Alex Gerdes, HT-2018

Objekt-orienterad programmering och design. DIT953 Niklas Broberg, 2018

Agil programutveckling

Objektorienterad programmering, allmänt

Viktiga egenskaper hos ett program (Meyer): Objektorienterad programmering, allmänt. Vilka egenskaper vill vi att våra program ska ha?

Objekt-orienterad Programmering och Design. TDA551 Alex Gerdes, HT-2016

Användarcentrerad Systemutveckling

Objekt-orienterad Programmering och Design. TDA552 Alex Gerdes, HT-2018

Linköpings universitet 1 TDP029. Systemutveckling. Systemutveckling. Vanliga faser. Fler faser. Systemutvecklingsmetod

Kristoffer Eriksson Christer Oscarsson Andreas Dahlberg Martin Bengtsson

Abstrakta Klasser 2. Kodning är bara en liten del i programvaruutvecklingen 6% 1% 6% Abstrakta Klasser - deklaration. Programutveckling sker i faser

Separation of Concern. Objekt-orienterad programmering och design (DIT952) Niklas Broberg, 2016 Johannes Åman Pohjola, 2017

Tentamen i EDAF25. 1 juni Skrivtid: Skriv inte med färgpenna enda tillåtna färg är svart/blyerts.

Agil projektmetodik Varför och vad är det?

Chaos om IT-projekt..

D J U P S T U D I E I E D A S I M P L E C O D E A N D D E S I G N

Dependencies High cohesion, low coupling. Objekt-orienterad programmering och design Alex Gerdes, 2018

Projektkaos. Chaos-rapporten. 34% av projekten avslutades i tid och enligt budget % misslyckades!

Cult of Code Quality

Mer OOP. Variation i typ. Medlen repetition. Generiska klasser. Gränssnitt - Interface. Mer om klasser Några exempel UML

HT1 2015, FÖRELÄSNING

Designmönster, introduktion. Vad är det? Varför skall man använda mönster?

Djupstudie i parprogrammering

Scrum + XP samt konsekvensanalys

Principles of subclasses Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

HAND TRACKING MED DJUPKAMERA

Designmönster - EMW. Kent Petersson epost1: kentp@cs.chalmers.se epost2: kent.petersson@emw.ericsson.se URL:

Introduktion. Grundkursen

RUP - Rational Unified Process

Objekt, klasser. Tillstånd Signatur Kommunikation Typ. Fält, parametrar och lokala variabler. Konstruktorer Metoder DAVA15

Objektorienterad programmering

Principles of subclasses. Objekt-orienterad programmering och design Alex Gerdes, 2018

Föreläsning 1. Introduktion Utveckla för förändring

Dependencies High cohesion, low coupling. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Teststrategier och Testcertifiering. Per Strandberg, Maj 2013

UML Objektdiagram. Objektorienterad modellering och design (EDAF25) Föreläsning 3. UML Sekvensdiagram. UML Objektdiagram. Agenda

Kritik av Extrem Programmering

Modulär design. Objekt-orienterad programmering och design (DIT953) Niklas Broberg / Johannes Åman Pohjola, 2018

Djupstudie Code smells / Refaktorisering. Martin Larsson dt08ml5 Stefan Johansson, dt08sj7

PROGRAMMERING. Ämnets syfte. Kurser i ämnet

Kursplanering Objektorienterad programmering

Att införa Extreme Programming genom processförbättring

PROGRAMMERING. Ämnets syfte. Kurser i ämnet

Programmering = modellering

Modulär design Objekt-orienterad programmering och design (DIT952) Niklas Broberg, 2016

Fungerar Agila principer i alla typer av projekt?

Tentamen i Objektorienterad modellering och design Helsingborg

Deluppgift 2 Kravhantering a) (2p) När man diskuterar krav brukar man ange två olika typer av krav. Beskriv dessa och ge exempel.

Vad är. Domändriven design?

Objektorienterad Systemutveckling Period 3

OCTOPUS utvecklingsmetod samt relaterade frågeställningar och diagram

Rune Tennesmed. Oskar Norling 1DV430. Individuellt Mjukvaruutvecklingsprojekt 1DV430 Webbprogrammerare H12 Oskar Norling

Analys och design. Objekt. Klass. med hjälp av CRC. Klassdiagram

SKOLFS. beslutade den XXX 2017.

Programmering. Seminarier i datavetenskap, datorteknik och informationsteknik. Niklas Broberg

CDC en jämförelse mellan superskalära processorer. EDT621 Campus Helsingborg av: Marcus Karlsson IDA

Programmering. Seminarier i datavetenskap, datorteknik och informationsteknik. Niklas Broberg

Filhanterare med AngularJS

Programmering. Seminarier i datavetenskap, datorteknik och informationsteknik. Niklas Broberg niklas.broberg@chalmers.

Scrum + XP = sant. Kristian Björk D06, Lunds Tekniska Högskola dt05kb1@student.lth.se. Frederik Blauenfeldt Jeppsson. dt06fb8@student.lth.

Tentamen i Objektorienterad modellering och design

Tentamen. 2D4135 vt 2005 Objektorienterad programmering, design och analys med Java Lördagen den 28 maj 2005 kl

Effekter av införande av agila metoder. Daniel Sundmark Mälardalens högskola

Objektorienterad Programkonstruktion. Föreläsning 6 23 nov 2015

Föreläsning 2 Datastrukturer (DAT037)

Labrapport över Rumbokningssytemet Grupp:1

Projektuppgift.

Föreläsning 2 Datastrukturer (DAT037)

Föreläsning 2. Objektorienterad analys och design. Analys: att modellera världen. Design: att strukturera program.

TDDI02. Programmeringsprojekt, Föreläsning 2. Filip Strömbäck. Med utgångspunkt i tidigare slides av Jonas Lindgren

Viktiga programmeringsbegrepp: Kontrakt

Utvecklingsm odell och utvecklingsm etod för att skapa god kom m unikation

Kodkomplexitet i agil utveckling. Axel Nilsson Svegard, Patrick Fogwall EDA270 - Djupstudie 2 mars 2010

Introduktionsmöte Innehåll

Linköpings universitet 1

PROGRAMMERINGSMETODIK

Seminarierna Instruktioner. Övningarna Viktiga relationer

Transkript:

Djupstudie: Big Ball of Mud - Technical debt and large refactorings Coaching av programvaruteam Felix Glas Februari 2012

Abstract Technical debt är den skuld utvecklare tar på sig genom att undvika refaktoriseringar. Så småningom måste skulden betalas tillbaka för att fortsatt utveckling skall kunna vara möjlig. För att motverka dessa problem bör utvecklare kontinuerligt refaktorisera programkoden, det vill säga rätta till och förbättra bristande delar av mjukvaran. Ibland kan det emellertid uppstå situationer där det är praktiskt gynnsamt att skapa snabba men bristande lösningar. Big Ball of Mud är ett begrepp som beskriver en situation där mjukvarans struktur befinner sig i ett oordnat och svårhanterligt tillstånd. Det finns flera anledningar till varför detta tillstånd ofta uppstår vid något tillfälle under utvecklingen av en mjukvara. För att lösa omfattande problem med strukturen måste en stor refaktorisering genomföras. Detta innebär ibland att övrig utveckling måste stoppas medan refaktoriseringen pågår. Vid stora refaktoriseringar kan det vara svårt att refaktorisera enligt förutbestämda mönster. Särskilda åtgärder måste vidtas för att kunna strukturera om systemet på ett bra sätt. Eventuellt blir utvecklarna tvungna att återskapa en ny modell av systemet (reverse engineering) för att kunna identifiera brister enligt designprinciperna. Därefter kan en ny modell tas fram (reengineering) som bättre lämpar sig för fortsatt utveckling.

Innehållsförteckning 1 Inledning 1.1 Technical debt 1.2 Metod 1.3 Kort om resultaten 1.4 Språk 2 Utvecklingsmetoder 2.1 Refaktorisering 2.2 Design med agil inriktning 2.3 Äldre utvecklingsmetoder 3 Designprinciper 3.1 Gör rätt från början 3.2 Design smells 3.3 Designprinciper 3.4 Big ball of mud 4 Studie 4.1 Stora refaktoriseringar 4.2 Reverse- and reengineering 4.3 Enkät 4.4 Resultat 5 Slutsats

1 Inledning 1.1 Technical debt All form av mjukvaruutveckling drabbas förr eller senare av komplikationer i form av bristande kvalité på källkod. Bristerna gör det svårt att komma vidare i utvecklingsarbetet om inte reparation av dessa brister genomförs. Små brister ackumuleras med tiden och påverkar allt större delar av mjukvaran. Problemet har liknats med en teknisk skuld i form av ett lån av snabba programmeringslösningar (eng: technical debt) [11][13]. Precis som de flesta andra lån måste detta lån så småningom betalas tillbaka. När den tidspressade utvecklaren väljer att tillämpa en så kallad quick and dirty -lösning skapas ett framtida behov av en bättre lösning. Utvecklarna kan välja att endast betala ränta i form av den extra ansträngning som framtida utveckling innebär när man arbetar med en dålig lösning. Alternativt kan utvecklarna välja att amortera på lånet genom att refaktorisera mjukvaran. Detta medför en lägre framtida ränta eftersom mindre ansträngning krävs för fortsatt utveckling. Ibland är det oundvikligt (och eventuellt fördelaktigt) att ta ut ett sådant lån för att kunna leverera mjukvara vid rätt tidpunkt [11]. Ofta kan det vara viktigt att kunna leverera på utsatt tid och senare genomföra en refaktorisering av en forcerad lösning. Det viktigaste är dock att det emellanåt amorteras på lånet, det vill säga att refaktoriseringar regelbundet utförs. Annars kommer projektet att behöva dras med en allt större räntekostnad i form av extra arbetstimmar i onödan. När ett projekt med en stor skuld inte har refaktoriserats på tillräckligt länge uppstår ett tillstånd där särskilda metoder måste tillämpas för att handskas med återbetalningen. Dessa stora refaktoriseringar (eng: large refactorings) har en tendens att stoppa fortsatt utveckling av alla delar av mjukvaran. 1.2 Metod Under kursen Programvaruutveckling i Grupp får en grupp studenter praktisera utveckling i grupp enligt agila utvecklingmetoder. Varje grupp tillsätts två äldre studenter som coacher. I min roll som coach har jag under 7 veckors tid genomfört en djupstudie i ämnet som den här studien behandlar. I studien har jag undersökt hur stora refaktoriseringar delvis kan undvikas genom användningen av olika designmönster som till exempel The Open-Closed Principle och The Dependency-Inversion Principle [4]. När ett projekt ändå (förmodligen oundvikligen efter lång tid) hamnar i ett läge där en stor refaktorisering måste genomföras, kommer jag att studera om det finns någon eller några bra metoder för att handskas med detta. Studien kommer huvudsakligen att koncentreras på principer som gäller vid tillämpandet av agila utvecklingsmetoder.

1.3 Kort om resultaten Under studien kommer jag att genomföra två enkäter för att undersöka hur designprinciperna tillämpas under projektets gång samt om tendenser finns att det bildas ett så kallat antimönster (eng: antipattern). I det här fallet är jag intresserad av anti-mönstret Big Ball of Mud [9]. 1.4 Språk Eftersom en stor del av litteraturen inom området mjukvaruutveckling och refaktorisering är på engelska återspeglas detta i terminologin. Många av de uttryck som används för att beskriva teorin är svåra att översätta till svenska och jag har därför valt att behålla de flesta på engelska. Eventuellt kan denna språkliga blandning vara till nytta för läsaren eftersom det är lättare för en oinvigd att förstå programmeringslitteratur om man kan de engelska facktermerna. 2 Utvecklingsmetoder 2.1 Refaktorisering Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure. Martin Fowler, Refactoring, Addison-Wesley, 1999 Refaktorisering innebär att ändra och skriva om delar av ett program för att förbättra den inbördes strukturen utan att förändra dess funktionalitet ur ett yttre perspektiv [1]. Med detta menas att koden kan ändras utan att det sker en förändring i vad koden utför. Syftet med en sådan ändring är oftast att förbättra läsbarheten för programmerare, förenkla strukturen och underlätta underhållandet av kod [8]. En välstrukturerad och lätthanterlig kod är lättare att underhålla och minskar risken för uppkomsten av buggar. Att refaktorisera kod är ett sätt att betala tillbaka den tekniska skulden (eng: technical debt) [11][13]. Genom att regelbundet genomföra mindre refaktoriseringar under programmets utveckling kan mängden degraderad kod begränsas. Vid programvaruutveckling är det nästan alltid utvecklarna som utgör den största ekonomiska kostnaden. Det har även gjorts undersökningar som visar att underhåll av kod kostar mer än själva utvecklingen [2]. Vid stora utvecklingsprojekt kan kostnaden av underhållet mångdubbelt överstiga kostnaden av utvecklingsarbetet. Av denna anledning kan man dra slutsatsen att kod som är enkel att underhålla har stort värde. Alltså bör det i utvecklingsprojekt läggas stor vikt vid att producera kod som är lätt att underhålla i ett senare skede. En annan aspekt som går hand i hand med underhåll är

läsbarhet. Eftersom framtida underhåll kan ske i olika former och med utbytt personal är det viktigt att nytillkomna utvecklare snabbt kan sätta sig in i projektet och förstå hur programmet fungerar. Inom agil utveckling förespråkas rutinmässig refaktorisering i samband med att ny kod skrivs [1][4]. Detta görs för att kontinuerligt ta hand om den växande mängden svårhanterlig kod och struktur som oundvikligen produceras vid iterativa utvecklingsmetoder. Vid iterativ utveckling implementeras funktionalitet allt eftersom då nya krav på funktioner kan tillkomma vid varje iteration [4]. När en sådan utvecklingsmetod tillämpas är det viktigt att organisera en rutinmässig kontroll och korrigering av den snabbt tillkommande nya strukturen så att programmet vidhåller en bra design enligt konstens regler. 2.2 Design med agil inriktning Vid agil utveckling har leveransen av programvara högsta prioritet [3][4]. Ny programvara bör kunna levereras tidigt och kunna följas upp med regelbundna uppdateringar. Det finns ett starkt samband mellan mjukvarukvalitet och frekventa leveranser av uppdaterad mjukvara [4]. Genom ett nära samarbete med kunden och möjligheten att tidigt granska produkten kan brister upptäckas och korrigeras kontinuerligt [3]. Till skillnad från vid äldre vattenfallsmetoder välkomnas förändring även sent under utvecklingsfasen. Mjukvarukoden anpassas för att vara flexibel och modulär för att underlätta den snabba förändringstakten. Agil utveckling använder sig av iterativa utvecklingsmetoder för att på ett flexibelt sätt kunna hantera snabbt växlande förutsättningar [4][5]. Metoden är utvecklad för att bättre kunna klara av de påfrestningar som modern mjukvaruutveckling utsätts för. Till exempel prioriteras ofta tidsomfattning och planering först för att ett utvecklingsprojekt garanterat skall kunna leverera en release till utsatt tid [5]. Eftersom teknikutvecklingen går snabbare och teknikförutsättningarna kan ändras på kort tid måste ny mjukvara snabbt kunna anpassas och uppdateras. Under sådana omständigheter gynnas ofta inte uppkomsten av kodkvalitet av sig själv utan måste aktivt utvärderas och förbättras. Därför är det särskilt viktigt att organisera rutiner för granskning och refaktorisering vid tillämpandet av agila metoder. 2.3 Äldre utvecklingsmetoder Äldre utvecklingsmodeller baseras ofta på en noga utformad ursprungsplan som förverkligas stegvis. Exempel på en sådan modell är vattenfallsmodellen [5]. Vid tillämpandet av vattenfallsmodellen skall varje steg vara klart och bedömas innan nästa steg kan tas. Modellen kan liknas vid de processer som följs vid konstruktion av hus och inom industrin. Arbetssättet gör att ju längre processen framskrider, desto svårare

blir det att hantera förändring. Med en låg förändringstakt är behovet av upprepade refaktoriseringar inte lika stort och mjukvaran kan anpassas i större utsträckning efter en färdig arkitektur. Nackdelen blir dessvärre att svårigheten att hantera förändring leder till att många utvecklingsprojekt måste börjas om eller läggas ned. Moderna metoder ställer högre krav på förmågan att kontinuerligt kunna hantera förändring och att fortlöpande kunna förbereda mjukvaran för fortsatt förändring. 3 Designprinciper 3.1 Gör rätt från början För att minimera behovet av refaktoriseringar är det viktigt att man utformar programkoden rätt från början. Det finns ett antal hjälpmedel för att kunna förutse vad som ofta utvecklas till dåliga lösningar. Till exempelvis har forskning och lång erfarenhet lett fram till de fem designprinciperna som tillsammans utgör grunden för god objektorienterad design [4][6]. Den som följer principerna har goda möjligheter att kunna minimera andelen tid som läggs på refaktoriseringar. Emellertid så går det inte helt att undvika refaktoriseringar eftersom det alltid kommer att finnas plats för förbättringar i en design. I alla utvecklingsprojekt uppstår brister oavsett hur duktiga utvecklarna är. Ofta sker detta för att kompromisser tvingas fram under tidspressade förhållanden. För att utvecklingsprojektet skall ha en fortsatt gynnsam framtid måste refaktoriseringar genomföras. En refaktorisering skall dock inte bara ses som en städningsåtgärd, utan som en naturlig del av utvecklingsarbetet. 3.2 Design smells När man talar om design i mjukvarusammanhang menar man mer än bara klasstrukturen representerad i till exempelvis ett UML-diagram. Designen är ett abstrakt koncept som omfattar allt från den översiktliga formen och strukturen till strukturen på varje enskild klass och metod. Den slutgiltiga representationen av design är därför naturligtvis källkod. Om en design anses vara dålig kan detta uttryckas med termen luktande kod - eller design (eng: code and design smells) [1]. Följande anses vara symptom på luktande kod: 1. Rigidity (rigiditet) - Det är svårt att ändra i koden eftersom ändringar tvingar fram ytterligare ändringar i andra delar av koden. 2. Fragility (skörhet) - Ändringar gör att andra delar av koden slutar fungera.

3. Immobility (orubblighet) - Systemet är inte modulärt och delar av koden kan inte återanvändas på andra ställen. 4. Viscosity (tröghet) - Det är svårare att göra rätt än att göra fel. 5. Needless comlexity (onödig komplexitet) - Delar av koden är onödigt komplex utan att tillföra mervärde. 6. Needless repetition (onödigt repetition) - Delar av koden är identisk med andra delar och borde kunna enas med en abstraktion. 7. Opacity (svårbegriplighet) - Delar av koden är svåra att förstå och uttrycker inte sina syften. Till exempelvis skall klass- och metodnamn representera vad de är till för. Luktande kod är tecken på att en refaktorisering måste genomföras för att fortsatt utveckling och underhåll av koden skall kunna genomföras i en rimlig takt. Om problemen tillåts kvarstå kommer mjukvaran att bli mer och mer svårhanterlig. Luktande kod har en tendens att fortplanta sig genom mjukvaran vid fortsatt utveckling och måste åtgärdas eller i varje fall avgränsas. Luktande kod kan uppstå av sig själv, oavsett om utvecklarna är duktiga eller inte, genom att kraven på mjukvaran förändras med tiden och på ett sätt som inte den ursprungliga designen kan förutsäga. För att undvika stora refaktoriseringar bör en god programmerare alltid akta sig för att skriva kod som kan börja lukta. 3.3 Design principles På en högre designnivå tillämpas ett antal principer för att strukturera mjukvaran på ett optimalt sätt [4]. Syftet är att hålla designen så enkel, ren och förståelig som möjligt. Dessa principer bör inte uteslutande användas vid den initiala utformningen av systemets design, utan skall tillämpas kontinuerligt under varje iteration för att i så stor mån som möjligt undvika onödiga omstruktureringar. Genom följande steg hanteras problem i designen [4]: 1. Problemet identifieras genom att det uppvisar symptom på luktande kod. 2. Problemet diagnostiseras genom tillämpning av designprinciper. 3. Utvecklarna löser problemet genom att använda sig av lämpliga designmönster. SRP (The Single Responsibility Principle) - En klass bör ha EN anledning till förändring Principen säger att varje klass bör bara ha ansvaret för att utföra en sak. Varje ny uppgift som klassen utför är ytterligare en anledning till förändring. För många ansvarsuppgifter gör klassen känslig för förändringar och designen blir skör. En skör design medför att enstaka förändringar skapar oförutsedda problem i andra delar av mjukvaran. Principen utgör en del av basen för all mjukvarudesign: att identifiera och

separera ansvarsuppgifter. OCP (The Open-Closed Principle) - Mjukvarukomponenter, som till exempel klasser, skall vara öppna för användning i form av arv men stängda för inre modifikation. När en förändring i en klass orsakar en dominoeffekt av fler förändringar i programmet är designen allt för rigid. Principen råder utvecklare att refaktorisera på ett sätt så att efterföljande förändringar inte medför en kaskad av ytterligare förändring. Efterföljande förändringar bör ske genom skapandet av ny kod och inte genom modifikation av existerande komponenter. LSP (The Liskov Subtitution Principle) - Subtyper skall vara substitut för bastyper. Principen säger att en subtyp bör kunna användas endast med kännedom om hur dess bastyp fungerar. Om en implementation behöver kännedom om vad som skiljer specifika subtyper åt bryter detta dessutom mot OCP genom att införandet av nya subtyper kräver modifikation av implementationen. DIP (The Dependency-Inversion Principle) - Beroenden bör peka på abstraktioner Implementation skall ej bero av konkreta klasser, de skall bero av gränssnitt (eng: interface) och abstrakta klasser. För att frikoppla delar av koden från varandra och modularisera designen är det viktigt att klasser på en högre nivå inte är beroende av implementationsdetaljer på en lägre nivå. En bra design kan delas in i lager där beroendena är omvända: de undre lagren beror av de högre. Högre lager använder sig av gränssnitt och abstrakta klasser för att komma åt de underliggande lagren. ISP (The Interface-Segregation Principle) - Implementationen bör inte tvingas bero av metoder som den inte använder. När en klass beror av ett gränssnitt måste den implementera alla metoder som definieras i gränssnittet. Gränssnitt bör därför delas upp om användingen av olika delar av gränssnittet skiljer sig mellan implementerande klasser. Detta för att undvika onödiga beroenden och en rigid design. 3.4 Big ball of mud Big ball of mud är ett uttryck som myntades 1999 av Brian Foote och Joseph Yoder [9] och som beskriver ett designmönster (eller snarare ett antimönster) där strukturen är bristande. När programkoden inte följer designprinciperna och mer liknar en ohanterlig röra kan den liknas med en gyttjeboll. De flesta utvecklare anser att en sådan röra bör undvikas och att en stor refaktorisering måste genomföras. Trots detta är fenomenet vanligt och många projekt utvecklas ofta till att bli ostrukturerade och svårhanterliga.

Anledningen till att många mjukvaruprojekt följer detta mönster diskuteras nedan under ett antal punkter. Ofta sker en oundviklig degenerering av programstruktur och design vid tillkomsten av ny kod. Därför måste detta ses som en del av utvecklingen och inte som misslyckande. De punkter som diskuteras nedan är anledningar som kan bidra till uppkomsten av dålig programstruktur [9]. Behovet att leverera kvalitativ mjukvara under utsatt tid och budget Ledningen ser det som levereras, inte de tekniska detaljerna. Ur ett kortsiktigt perspektiv spelar design och programstruktur mindre roll och hamnar lätt på efterkälken. När en bra struktur inte belönas av ledningen bildas en ond cirkel. Fokus främst på funktionalitet och i andra hand på arkitektur och prestanda Även ett resultat av en kortsiktig planering där kund och ledning endast har insikt i programmets yttre. Om ingen hänsyn tas till långsiktig utveckling utan endast ligger på mängden funktionalitet som implementeras faller en bra design samman ganska fort. Snabba lösningar på små problem Snabba lösningar för att snabbt kunna gå vidare är inte alltid den bästa lösningen. Speciellt inte om det ej finns utrymme för refaktorisering i efterhand. Agil utveckling talar för enkelhet och att lösa aktuella problem, inte framtida. Snabba lösningar tenderar dock att bli beroende av refaktoriseringar i längden. Återanvändning av gammal kod Under tidspressade förhållanden vill man hellre återanvända färdig kod än att skriva ny. Ofta innebär återanvändandet av gammal kod att en del kompromisser måste göras vad gäller designprinciperna. Användningen av en oflexibel designplan En designplan som är allt för stel och inte kan förändras i den takt som behövs utgör en dålig grund för programmets struktur. Refaktorisering är riskabelt Refaktoriseringar innebär att kod kanske måste skrivas om. Många utvecklare kan känna att detta är riskabelt eftersom den nya koden kanske inte fungerar direkt. En refaktorisering är även ett åtagande som måste slutföras innan utvecklingen kan gå vidare.

Programmet skall fortsätta fungera Om det viktigaste är att mjukvaran fortsätter att fungera på kort sikt kan resultatet bli att man upprepat reparerar något som egentligen inte är bra. När produktionen håller ett högt tempo finns det mindre och mindre tid till att refaktorisera bristande struktur. 4 Studie 4.1 Stora refaktoriseringar Små refaktoriseringar är en viktig del av det agila utvecklingsarbetet. De är oftast överblickbara och kan göras enligt förutbestämda mönster. I större projekt (och även i mindre) är det emellertid vanligt att större refaktoriseringar blir nödvändiga. Refaktoriseringar som är tillräckligt stora och komplicerade går inte att genomföra enligt förutbestämda mönster. De är för omfattande och berör för stor del av strukturen för att kunna genomföras parallellt med annan utveckling [7]. En genomgående refaktorisering kan eventuellt beröra alla utvecklare som jobbar med projektet. Detta kan skapa förvirring för de utvecklare som inte känner till detaljerna av refaktoriseringen. En stor och komplex refaktorisering kan innebära att utvecklarna tappar kontrollen under genomförandet, och att visa delar av mjukvaran tillfogas ofärdiga refaktoriseringar. Systemet består nu av en blandning av ny och gammal struktur, vilket eventuellt leder till en ännu sämre struktur än från början. Stora refaktoriseringar kan även vara svåra att genomföra eftersom de måste utföras på strukturer som konstant kan förändras. Detta eftersom det kanske inte går att stoppa övriga utvecklingsspår när refaktoriseringen skall genomföras. Den enda lösningen vid stora refaktoriseringar blir ofta att refaktoriseringen måste planeras noga och delas upp i mindre delar [7][10]. Det måste på förhand bestämmas vilka delar av refaktoriseringen som kommer att påverka specifika delar av mjukvaran så att en prioriteringsplan kan konstrueras. Genom att iterativt genomföra refaktoriseringarna kan även effekterna av förändringen hela tiden observeras. 4.2 Reverse- and reengineering För att hantera stora refaktoriseringar av ett mjukvaruprojekt är utvecklarna ibland tvungna att från den konkreta implementationen återkonstruera en abstrakt representation av designen för att kunna överblicka vad som behöver ändras [12] [14]. Ofta är behovet av en stor refaktorisering resultatet av en mängd olika ändringar i källkoden under en lång tid. Risken är då stor att utvecklarna tappat förmågan att överblicka hela designen och det blir svårt att applicera designprinciperna för att hitta brister. Ibland kan även dokumentationen ha hamnat på efterkälken och upphöra att

beskriva systemet korrekt. Vad som då måste göras är en återskapning av modellen eller en så kallad reverse engineering (omvänd ingenjörskonst), det vill säga att högnivådesignen måste återskapas från källkoden [12][14]. För att konstruera en abstrakt modell av ett system måste utvecklarna analysera och förstå hur koden fungerar. Reverse engineering bör inte blandas ihop med begreppet reenginering (omskapande ingenjörskonst), som handlar om att omstrukturera modellen [12][14]. Reverse engineering respektive reenginering definieras så här: Reverse Engineering is the process of analyzing a subject system to identify the system s components and their interrelationships and create representations of the system in another form or at a higher level of abstraction. Reengineering... is the examination and alteration of a subject system to reconstitute it in a new form and the subsequent implementation of the new form. Chikofsky and Cross, Reverse engineering and design recovery, Computer Society Press 1992 Reverse engineering handlar alltså mer om analys och förståelse medan reenginering handlar om strukturering för att laga brister. Eftersom en omstrukturering vid en stor refaktorisering innebär att strukturen måste ändras på alla nivåer i mjukvaran ingår båda begreppen i processen. Först analyseras och återskapas en modell, sedan omstruktureras den. Processen innebär även att information utöver källkoden kan omstruktureras och förbättras, som till exempelvis dokumentation och testfall. Figur 1 illustrerar processen.

. Figur 1. Processen vid en sk. reverse- och reenginering enligt [14]. 4.3 Enkät Del 1: Under djupstudiens gång har jag följt upp den praktiska delen av kursen där en grupp studenter har praktiserat programvaruutveckling i grupp. I mitten av praktikperioden utförde jag en enkätundersökning för att få en bild av vad studenterna ansåg om det program som utvecklades gällande design och behovet av refaktoriseringar. Om man antar att gruppen består av 8 utvecklare som utvecklat ett godtyckligt program under 3 tillfällen, motsvarande 3 * 8 timmar utvecklingstid samt extra planeringstid, blev resultatet enligt följande frågeställningar och svar. På en skala 1-5, där 1 är mycket bra och 5 är mycket dåligt, skulle gruppen svara på hur bra översikt de hade över programmets design/arkitektur. Svar i snitt: 1,9 På en skala 1-5, där 1 är mycket bra och 5 är mycket dåligt, skulle gruppen svara på hur bra kvalitet de ansåg att programmets design/arkitektur höll. Svar i snitt: 2,1 På frågan om hur mycket tid som lades ner på refaktoriseringar per tillfälle svarade 75% 1-2 timmar, och resterande 25% svarade under 1 timme.

Ingen i gruppen ansåg att för mycket tid lades ner på refaktoriseringar per tillfälle. Bara en i gruppen ansåg att det behövdes en grundläggande omstrukturering av designen/arkitekturen. Del 2: Under slutet av praktikperioden genomförde jag en andra enkätundersökning som inriktades mer på användningen av designprinciper. Ingen i gruppen ansåg att en grundläggande omstrukturering av designen/arkitekturen behövdes vid det sista labbtillfället. På en skala 1-5, där 1 är mycket liten utsträckning och 5 är mycket stor utsträckning, skulle gruppen svara på i vilken utsträckning som projektet vid något tillfälle drabbats av rigiditet och skörhet. Svar i snitt: 2,6 På en skala 1-5, där 1 är mycket liten utsträckning och 5 är mycket stor utsträckning, skulle gruppen svara på i vilken utsträckning som projektet vid något tillfälle drabbats av överdriven komplexitet. Svar i snitt: 2,6 På en skala 1-5, där 1 är mycket liten utsträckning och 5 är mycket stor utsträckning, skulle gruppen svara på i vilken utsträckning som projektet vid något tillfälle drabbats av onödig repetition av källkod. Svar i snitt: 2,3 På en skala 1-5, där 1 är mycket liten utsträckning och 5 är mycket stor utsträckning, skulle gruppen svara på i vilken utsträckning som projektet vid något tillfälle drabbats av svårbegriplig källkod. Svar i snitt: 2,3 På en skala 1-5, där 1 är mycket liten grad och 5 är mycket stor grad, skulle gruppen svara på i vilken grad som ovanstående problem hade refaktoriserats bort. Svar i snitt: 4,1 På frågan om projektet vid något tillfälle passerat en flaskhals på grund av refaktoriseringar, så att fortsatt utveckling saktat av eller avstannat, svarade 50% ja. Fem av åtta ansåg att det var oundvikligt att problem med stora refaktoriseringar uppstod och att det var svårt att förutse och förebygga designproblem. Två av åtta ansåg att det behövdes en bättre strukturerad process för stora refaktoriseringar.

4.4 Resultat Från enkäten kan man dra slutsatsen att de flesta ansåg att programmets struktur höll en tillräckligt bra kvalité och att det inte behövdes en grundläggande ändring av strukturen efter tre utvecklingstillfällen. De flesta ansåg att ca en timme inte var för mycket tid att lägga på refaktoriseringar. Överlag indikerade gruppen under slutet av praktikperioden att det inte förekommit några större designproblem enligt designprinciperna. Gruppen ansåg även att de få designproblem som existerat har refaktoriserats bort i stor grad. Trots detta upplevde många att projektet stannat av på grund av stora refaktoriseringar och majoriteten ansåg att detta var oundvikligt. Av detta kan man enligt mig dra slutsatsen att små problem oundvikligen ackumuleras och bildar större brister i designen efter en tid. 5 Slutsats Vid programvaruutveckling ackumuleras små brister och bilder på sikt större problem som måste refaktoriseras bort. Därför är det viktigt att rutinmässigt genomföra refaktoriseringar för att förhindra att problemen bildar en så kallad Big Ball of Mud. Det optimala sättet att hålla nere den tekniska skulden vore om all källkod följde designprinciperna till punkt och pricka. I praktiken är detta dessvärre svårt att följa och ofta tvingas utvecklare kompromissa i designvalen föra att kunna leverera programvara till utsatt tid. Ofta infinner sig ett tillstånd av oordning förr eller senare i utvecklingsprojekt och fortsatt utveckling hindras till dess att en stor refaktorisering genomförts. Stora refaktoriseringar ställer andra krav på utvecklarna vad gäller uppdelning och planering. För att hitta en bättre lämpad design är det ibland nödvändigt att analysera och återkonstruera en designmässig översikt av programvaran. Då kan man använda sig av en så kallad reverse- och reengineering-process. Lämpligtvis så läggs extra mycket kraft på att slutföra en stor refaktorisering då ett projekt blir svårhanterligt och fortsatt utveckling har saktat av till följd av brister enligt designprinciperna. Det är viktigt att kunna identifiera och motivera behovet av en stor refaktorisering då det ofta kommer smygande utan att någon märker det. Det kan också vara svårt att tidsuppskatta omfattningen av en stor refaktorisering och ibland kan den innebära ett utvecklingsstopp för övriga utvecklingslinjer. Det är emellertid av stor vikt att programvaran håller god kvalitet för att underlätta fortsatt utveckling och i det långa loppet brukar refaktoriseringen betala sig själv i både tid och pengar.

Referenser [1] M. Fowler, Refactoring: improving the design of existing code, Addison-Wesley, 1999. [2] I. Sommerville, I. Software Engineering. Addison-Wesley, 6th edition, 2001. [3] Chromatic, Extreme Programming Pocket Guide - Team-Based Software Development, O Reilly, 2003. [4] R. Martin, Agile Software Development - Principles, Patterns and Practices, Prentice-Hall, 2003. [5] B. Hughes, Software Project Management, McGraw-Hill, 2009. [6] E. Koffman, Data Structures - Objects, Abstraction and Design using Java, John Wiley and Sons Ltd, 2004. [7] M. Lippert, Large Refactorings in Agile Software Development, Position Paper for the Workshop on Beyond Green-Field Software Development: Strategies for Reengineering and Evolution at OOPSLA, University of Hamburg & it-wps, Ltd, 2003. [8] S. Koopmanschap, Refactoring Your Application, April 2009. http://techportal.ibuildings.com/2009/04/28/refactoring-your-application/, 2012-02-28 [9] B. Foote & J. Yoder, Big Ball of Mud, Technical report, University of Illinois at Urbana-Champaign, Dept. of Computer Science, 1999. Technical Report WUCS-97-34. [10] S. Gorts, Refactoring in the Large, November 2004. http://www.refactoring.be/thumbnails/large/large.html, 2012-02-28 [11] J. Atwood, Coding Horror - Paying down your technical debt, February 2009. http://www.codinghorror.com/blog/2009/02/paying-down-your-technical-debt.html, 2012-02-28 [12] E. Chikofsky and J. Cross II, Reverse engineering and design recovery: A taxonomy, In Robert S. Arnold, editor, Software Reengineering, pages 54 58, IEEE Computer Society Press, 1992.

[13] W. Cunningham, Ward Explains Debt Metaphor, Februari 2009 http://c2.com/cgi/wiki?wardexplainsdebtmetaphor, 2012-02-28 [14] S. Demeyer, Object Oriented Reengineering Patterns, Square Bracket Associates, 2009