YAGNI, avagy hogyan kerüljük el a túltervezést a szoftverfejlesztésben?
A legtöbb fejlesztő különleges elégedettséget érez egy elegánsan megtervezett algoritmus vagy rendszer elkészítése közben, amivel önmagával természetesen az égvilágon semmi baj sincs. A gondok akkor kezdődnek, amikor az ilyen megoldások öncélúvá válnak, és inkább csak hátráltatják, hogy működő terméket tegyünk az asztalra. Ezt hívjuk túltervezésnek (szép magyar szóval overengineering), amely mai posztunk témája lesz.
Az extrém programozás metodológiája ezt a YAGNI (You Aren't Gonna Need It) elvvel fogja meg: "válaszd mindig a lehető legegyszerűbb, de még működő megoldást". A túltervezés legtöbbször abból ered, hogy egy konkrét problémára egy általánosabb megoldást szeretnénk adni, főleg a jövőben esetlegesen felmerülő (de még a láthatáron sem lévő) igények miatt. Persze a legtöbben most felszisszennek, hiszen óvodás fejlesztő korunk óta verik a fejünkbe, hogy mindent kiterjeszthetőre, könnyen bővíthetőre kell tervezni, ez azonban a való életben nem mindig praktikus. És nem kizárólag külső tényezők (határidők, pénz) miatt nem az; ha egy megoldást túlságosan az esetleges szceniárókra tervezünk, az zavarossá teheti az egészet, és sokszor akkora járulékos munkával és kódbázissal jár, hogy gyakorilatilag "eltakarja" az eredeti célt. Ha ilyesmivel találjuk szembe magunkat, szinte biztosan nem végzünk jó munkát, legyen az akármilyen elegáns és logikus is.
A YAGNI elv szerint az ilyen, "jelenleg nem szükséges, de a jövő szempontjából hasznos" megoldások bevezetése számos hátránnyal jár:
- Az erre fordított idő és energia a megvalósítandó alapfunkció rovására megy.
- A járulékos megoldásokat is debuggolni, dokumentálni és akár supportálni is kell.
- Könnyen code bloat jöhet létre, amely által a szoftverünk felesleges válik nagyobbá és bonyolultabbá.
- Külön specifikáció nélkül a járulékosan hozzáadott dolgokról más fejlesztők akár nem is tudhatnak, így nem használják majd azt, ha mégis szükség lenne rá (rosszabb esetben újra lekódolják).
- Amikor ilyen új absztrakciókat adunk a rendszerünkhöz, azok tervezése közben további új, kiegészítő megoldások juthatnak eszünkbe, és nem nehéz látni, hogy ez könnyen egyfajta hógolyó-effektushoz vezethet (ezt angolul feature creep-nek is nevezik).
Persze azt sem nehéz látni, hogy a spontán, mindig csak a minimum követelményt teljesítő kódrészletek használata számos csapdát is rejt. Az extrém programozás ezért kifejezetten hangsúlyozza, hogy a YAGNI kizárólag állandó refaktorálással, állandó unit tesztekkel és integrációval együtt használható. Ha nem refaktoráljuk a kódot folyamatosan, akkor a szoftverünk egy szempillantás alatt egy rendezetlen masszává változik, ami csak óriási újraírási munkákkal lenne helyrehozható. Viszont a különböző agilis metodológiákból már tudjuk, hogy a hatékony refaktorálás egyik nagyon fontos eszköze az automatizált unit tesztelés, amely egyfajta védőhálót ad, hiszen mindig biztosak lehetünk benne, hogy a szoftver refaktorálás után is ugyanazt csinálja, mint előtte.
Végezetül érdemes megnézni néhány konkrét, gyakran előforduló példát is a túltervezésre, hogy már csírájában elfolythassuk, ha magunk is szembetalálkozunk vele:
- Üres absztrakciós rétegek, amely jó esetben teljesen szükségtelenek, rosszabb esetben viszont az alatta lévő API-nak csak egy szűk keresztmetszetét "engedik át", ami korlátozza azok használatát.
- Különböző "kiterjesztési pontokkal" teletűzdelt funkciók, amelyek valójában mégis csak nyűgként jelentkeznek, amikor tényleg ki kell terjeszteni őket.
- Minden funkció konfigurálhatóvá tétele a hard coding elkerülése érdekében, amelynek folytán a konfigurációért felelős kód végül nagyobb lesz, mint maga a megvalósítandó funkció.
Természetesen számos másikat is lehetne még említeni. A lényeg, hogy a jövőben felmerülő igényekre legjobban reagáló design általában egyben a lehető legegyszerűbb is — érdemes ezt észben tartani. A témában érdekes olvasmány még Joel Spolsky klasszikus bejegyzése, a The Duct Tape Programmer.