Refaktorointi on pelottavaa ja vaarallista

Viimeaikoina projekteissamme on ollut keskustelua refaktoroinnista, ja siihen liittyen on esiintynyt joitakin väärinkäsityksiä mitä refaktorointi tarkoittaa. Refaktorointi (Refactoring) on yksi neljästäkymmenestä Extreme Programming-käytännöstä, joita harjoitetaan PHZ Full Stack:n tekemissä projekteissa. Se on yksi keskeisimmistä ja toisaalta vaikeimmista käytännöistä kestävän kehityksen projektien toteuttamiseksi. Kestävän kehityksen periaatteita noudattaen PHZ Full Stack säästää vuosittain miljoonia euroja asiakkaiden rahoja välttämällä olemassaolevien järjestelmien turhan uudelleentoteuttamisen. Pystymme jatkuvasti mukautua muuttuvaan liiketoimintaympäristöön (rektorointia hyödyntämällä). Samanaikaisesti käytämme uusinta teknologiaa uusien ominaisuuksien tekemiseen.

Mitä refaktorointi on?

Martin Fowler (Gang of Four) on esitellyt alunperin refaktoroinnin käsitteen ja kirjoittanut aiheesta oivan perusteoksen, joka löytyy myös PHZ Full Stack:n kirjahyllystä (M. Fowler, K. Beck. Refactoring. Improving the Design of Existing Code. 1999. Addison-Wesley). Refaktoroinnilla tarkoitetaan olemassaolevan koodin sisäisen rakenteen muuttamista ilman, että sen ulkoinen käyttäytyminen muuttuu. Toisin sanoen refaktoroinnin seurauksena järjestelmään ei koskaan tule uusia ominaisuuksia. Refaktorointia tehdessä on tärkeää toisaalta tietää miten järjestelmä tai rajapinta käyttäytyy, ennen ja jälkeen sen suorittamisen.

Refaktorointi on pelottavaa, vaikeaa ja vaarallista

Refaktorointia on usein vaikeaa tehdä puutteellisen dokumentaation ja olemattomien testisuunnitelmien takia. Tämän takia ohjelmoijat kokevat tavallisesti olemassaolevan, jonkun muun kirjoittaman koodin muuttamisen tai edes siihen tutustumisen pelottavaksi ja vaikeaksi, ja siten välttävät sitä. Käytäntöön vihkiytymättömien kehittäjien normaali ratkaisu onkin siis aina keksiä pyörä uudestaan ja kirjoittaa olemassaoleva koodi tai järjestelmä alusta alkaen itse uudestaan, koska vanhaan koodiin koskeminen on liian pelottavaa. Tämän takia jonkun muun aiemmin kirjoittamaa koodia kutsutaan vanhentuneeksi Legacy-koodiksi. Usein kuitenkin uusi koodi ei ole juuri sen parempaa kuin vanha, siitä puuttuu vanhan koodin sisältämiä ominaisuuksia ja siinä on enemmän bugeja kuin vanhassa tuotantotestatussa koodissa, jota se yrittää korvata.

Tavoitteena refaktoroinnilla on Extreme Programming:ssa mahdollistaa uuden ominaisuuden lisääminen, mutta se edeltää erillisenä työvaiheena varsinaista ominaisuuden lisäämistä. Koska Extreme Programming:ssa keskeinen suunnittelukäytäntö on ns. Simple Design, jonka periaatteena on että kussakin iteraatiossa tehdään vain yksinkertaisin mahdollinen toteutus toivotusta ominaisuudesta ajan säästämiseksi ja projektin tuottavuuden maksimoimiseksi, koodaajien ei koskaan tulisi XP:ssä varautua tulevaisuuteen kehittämällä järjestelmään sellaista monimutkaisuutta jota juuri tällä hetkellä ei tarvita. Toisin sanoen tyypillinen uutta ominaisuutta edeltävä refaktorointi on muuttaa yksinkertainen suunnitteluratkaisu (esim. 1-1 relaatio tai kovakoodattu taulukko) monimutkaiseksi (yhden-suhde-moneen -relaatioksi tai tietokantapohjaiseksi ratkaisuksi). Toinen refaktoroinnin tavoite voi olla poistaa koodihajuja, huonoja ratkaisuja ja kuollutta koodia, sekä parantaa koodin luettavuutta.

Työkalut

Ilman kunnollisia työkaluja refaktoroinnin tekeminen on vaikeaa, pelottavaa ja käytännössä myös vaarallista. Jos refaktoroinnin tavoitteena oli kehittää järjestelmän sisäistä rakennetta ilman että ulkoinen käyttäytyminen muuttuu, todellisuudessa refaktoroinnin seurauksena jokin ominaisuus voi lakata toimimasta ja usein sen seurauksena järjestelmään syntyy uusia odottamattomia bugeja. Tyypillinen tilanne monissa projekteissa on, että korjaamalla yhden bugin koodaajat saavat aikaan kaksi tai kolme uutta. Jos järjestelmällä on kriittistä liiketoiminta-arvoa, huonosti hallitun refaktoroinnin kustannukset voivat olla kymmeniä tai , satoja tuhansia euroja, tai jopa miljoonia. Tämän takia koodaajia ja asiakkaita usein houkuttaa koko järjestelmän uudelleenkirjoitus (rewrite) refaktoroinnin sijaan.

Käytännön refaktoroinnin avustamiseksi on kehittyneemmissä IDE-editoreissa (kuten IntelliJ Idea, Visual Studio, Eclipse, Netbeans jne) valmiita refaktorointityökaluja, jotka suorittavat mekaanisia matalan tason refaktorointeja. Tyypillisiä ja helpoimpia automaattirefaktorointi ovat muuttujan tai funktion nimen vaihtaminen sekä funktion muodostaminen (extract method). Editori tekee tällöin mekaanisen ja tarkkuutta vaativan työn koodaajan puolesta, mikä vähentää virheitä ja nopeuttaa työntekoa. Kuitenkin aina koodityökalujen automaattirefaktorointeihin ei voi täysin luottaa, vaan dynaamisen koodin tapauksessa ja muutenkin automaattirefaktorointien lopputulokset kannattaa aina katselmoida ihmisten toimesta ja eristää ne omiin versionhallintamuutoksiinsa (commit).

Keskeisin refaktoroinnin mahdollistava työkalu on kuitenkin testiautomaatio ja testilähtöinen kehitys (TDD/BDD). Laatimalla järjestelmään jo toteutusvaiheessa automaattiset yksikkö- ja hyväksymistestit mahdollisestaan sen jatkuva elinkaari ja muutoksien tekeminen. Koodajien kokema pelko olemassaolevan järjestelmän muokkaamiseen vastaamaan muuttuvaa liiketoimintaympäristöä hälvenee, ja olemassaolevien järjestelmien elinkaaria voidaan jatkaa jopa vuosikymmeniä, säästäen miljoonia kehitysbudjeteista. Samaan aikaan ilman testiautomaatiota ja refaktorointia ylläpidettyjä järjestelmiä toteutetaan uudestaan 3-5 vuoden välein suurilla kustannuksilla ja useinkaan saavuttamatta tavoiteltuja liiketoimintahyötyjä ainakaan aikataulussa.

Valerefaktorointi

Tyypillinen tahaton tai tahallinen väärinkäsitys refaktoroinnista on, että sillä käytännössä tarkoitetaan jonkun pelottavan järjestelmän uudelleentoteutusta (rewrite). Koodaajat saattavat puhua refaktoroinnista, mutta todellisuudessa ajatella tekevänsä esim. bugin korjaamisen toteuttamalla järjestelmä nollasta uudestaan.

Refaktorointi vaatii osaamista ja työkaluja

Refaktorointi ei ole helppoa kokemattomille kehittäjille, vaan se vaatii uusien työkalujen ja työskentelytapojen, kuten yksikkötestauksen, opettelemista. IDE-editorit ja niihin integroidut debuggerit mahdollistavat olemassaolevan koodin rakenteen ymmärtämisen, kun koodin suoritusta ja muuttujien tilaa on helppo seurata rivi kerrallaan. Monimutkaisimmankin frameworkin sisäinen rakenne selviää muutaman tusinan rivi-riviltä käydyn debuggeri-läpikäynnin avulla. Test Driven Development – käytäntöjen puuttuessa ennestään usein ensimmäinen yksikkötesti kannattaa tehdä korjaamalla ja testaamalla bugin esiintyminen. Taitavalla ja systemaattisella refaktoroinnilla hyödyntäen oikeita työkaluhja saavutetaan kuitenkin valtavia hyötyjä etenkin kun tarkastetaan järjestelmän koko elinkaarta. Tämän takia PHZ Full Stack:n omat sisäiset projektit sekä asiakkaille toteutetut projektit saavuttavat pitkällä aikavälillä jopa 90% kustannussäästöt verrattuna perinteiseen kehitystapaan, keskittymällä laatuun määrän sijasta.