Spagettikód (spaghetti code, tészta) — ez a program zavaros, kaotikus szerkezete, ahol a logikai blokkok mindenféle rend nélkül fonódnak össze. A TIOBE Index (2024) kutatása szerint a magas spagettikód-szintű projektek 2,5-szer több időt igényelnek az új funkciók bevezetéséhez. A kifejezés a korai programozás korszakában keletkezett, amikor a goto utasítás lehetővé tette, hogy a program bármely pontjára átugorjunk, olvashatatlan szerkezeteket hozva létre.
A lényeg
Spagettikód (spaghetti code) — ez egy metafora az olyan kód leírására, amelynek szerkezete egy tányér spagettire emlékeztet: az egyes szálak (logikai blokkok) összekuszálódtak, összeragadtak, és nem választhatók szét egymástól. Az ilyen kódban lehetetlen rétegeket, modulokat vagy komponenseket elkülöníteni — minden egy nagy masszában keveredik.
A szarkódtól eltérően, amely egyszerűen csak igénytelen lehet, a spagettikód alapvető architekturális probléma. Még a tökéletesen formázott, jó változónevekkel ellátott kód is lehet spagettikód, ha az architektúrája kaotikus. A probléma a program szerkezetének szintjén van, nem az írásmódjában.
A IEEE (2022) adatai szerint a nagy projektek hibáinak mintegy 35%-át éppen a kód zavaros szerkezete okozza, nem pedig a fejlesztő logikai hibái. A fejlesztő nem azért hibázik, mert rosszul értette a feladatot, hanem mert nem tudta követni a végrehajtás menetét a spagettikódban.
Ha a szarkód rossz kód egyetlen függvény vagy fájl méretében, akkor a spagettikód rossz architektúra a teljes alkalmazás méretében. A tészta állhat jól megírt egyes függvényekből, de a kölcsönhatásuk kaotikus és kiszámíthatatlan.
A kifejezés, „spagettikód”, az 1970-es években jelent meg a goto utasítás kritikájával együtt. A korai programozási nyelvekben (BASIC, FORTRAN, COBOL) a goto volt a végrehajtási folyamat vezérlésének fő módja. A program megszámozott sorok sorozatából állt, és a goto lehetővé tette, hogy bármelyikre átugorjunk. Ez az átugrásokból olyan „gubancot” hozott létre, amelyet lehetetlen volt kibogozni.
1968-ban Edsger Dijkstra publikálta híres levelét, a „Go To Statement Considered Harmful” címűt, amely a strukturált programozás korszakának kezdetét jelentette. Dijkstra bebizonyította, hogy bármely algoritmus megvalósítható goto nélkül, mindössze három szerkezet használatával: szekvencia, elágazás (if) és ciklus (while). Ez lett a modern programozás alapja.
A strukturált programozás nem szüntette meg teljesen a problémát. A spagettikód új szintre lépett — a fizikai goto helyett a fejlesztők logikai „goto”-kat kezdtek létrehozni: globális változókat, callback hell-t JavaScriptben, bonyolult hívási láncokat és rejtett függőségeket a komponensek között. A probléma megmaradt, csak a formája változott.
A callback hell a JavaScriptben, a mélyen beágyazott Promise-ok, a hibakezelés nélküli async/await, az olyan események, amelyekről nem lehet tudni, hogy ki és mikor indítja el — mindezek a spagettikód modern változatai. Az antipattern él és virul, csak most már nem használja a goto utasítást.
A rétegek hiánya — az első és legfontosabb jel. A spagettikódban az üzleti logika, az adatbázis-kezelés, az HTML-megjelenítés és a hálózati kommunikáció összekeveredve található egy fájlban vagy akár egy metódusban is. Az adatbázis-lekérdezés módosítása elronthatja az UI megjelenítését, mert ezeknek a rétegeknek a kódja nincs szétválasztva.
A globális változók és a szingletonok — a második nyilvánvaló jel. Amikor az alkalmazás állapotát globális objektumokban tárolják, a végrehajtás menete kiszámíthatatlanná válik. Bármely függvény módosíthatja a globális állapotot, és szinte lehetetlen nyomon követni, hol és mikor történt ez.
A god-osztályok és a god-függvények — a harmadik jel. Egy 2000+ soros osztály, amely felel az üzleti logikáért, a megjelenítésért és az adatkezelésért is — ez tipikus spagettikód. Az a függvény, amely 10 paramétert fogad el és 5 különböző dolgot csinál, szintén az.
| Jel | Leírás | Példa |
|---|---|---|
| Rétegek keverése | SQL-lekérdezések az UI-kódon belül | Közvetlen adatbázis-írást végző vezérlő |
| Globális változók | Mindenhonnan elérhető állapot | static SessionManager minden osztályban |
| God-osztályok | Egy osztály mindent csinál | 3000 soros OrderManager |
| Hosszú metódusok | Felosztás nélküli függvények | 200 soros metódus 5 felelősségi körrel |
| Callback hell | Vég nélküli beágyazott callback-ek | 6 szintű beágyazottság JavaScriptben |
Ha nem tud unit-tesztet írni egy függvényhez anélkül, hogy 15 mock-objektumot hozna létre — az spagettikód. Ha egyetlen modul tesztelése az alkalmazás teljes infrastruktúrájának felállítását igényli — az spagettikód. A tesztelhetetlenség a zavaros architektúra objektív mutatója.
Az architekturális tervezés hiánya — a leggyakoribb ok. Amikor a csapat terv nélkül kezd kódot írni, az architektúrát menet közben választva, az eredmény elkerülhetetlenül spagettivé válik. Minden új funkciót oda adnak hozzá, ahol „most kényelmes”, nem oda, ahová logikailag való.
Az evolúciós fejlődés — a második ok. A projekt kis szkriptként indul, majd funkciókkal nő, aztán alkalmazássá válik, végül monolittá. Eközben az architektúrát nem vizsgálják felül. Az, ami 100 sornyi kódnál működött, katasztrófává válik 100 000 sornál.
A SOLID-elvek megsértése — a harmadik ok. Különösen az egyszeri felelősség elvének (S) és a függőségmegfordítás elvének (D) megsértése. Amikor egy osztály mindenért felel, a függőségek merevek, a modulok pedig halálosan összekapcsolódnak — spagettikódot kapunk.
A határidők és a hotfix-kultúra — a spagettikód katalizátorai. Amikor „már tegnap kellett volna”, a fejlesztők a legelső adódó helyre illesztik be a kódot, nem gondolkodva az architektúrán. Tíz ilyen hotfix — és az alkalmazás architektúrája tönkremegy.
A fő következmény — az irányítás elvesztése a kódbázis felett. A fejlesztők nem értik többé, hogyan működik az alkalmazás egésze. Egy változtatás az egyik helyen elront valami mást, ami látszólag nem kapcsolódik hozzá. Minden patch két új hibát hoz létre. A csapat a „változástól való félelem” állapotába kerül.
A csapat termelékenysége exponenciálisan csökken. A Microsoft Research (2023) kutatása kimutatta, hogy egy új funkció hozzáadásának ideje a spagettikódban a kódbázis méretéhez képest négyzetes törvény szerint nő. Tiszta architektúra esetén ez a növekedés lineáris. A különbség kritikussá válik 50 000+ sornyi kódnál.
A biztonság — egy újabb áldozat. A spagettikódban könnyű figyelmen kívül hagyni egy kezeletlen kivételt, a hibás bemeneti validációt vagy egy adatszivárgást. A biztonsági audit egy zavaros architektúrájú projektben gyakorlatilag lehetetlen — irreális megtalálni minden helyet, ahol felhasználói bemenetet használnak.
A fluktuáció a spagettikódos projektekben az átlag felett van. A tapasztalt fejlesztők elmennek, mert nem akarnak „tésztával” dolgozni. Az új munkatársak nem tudnak kiigazodni a kódban, és az első hónapokban elmennek. A projekt elveszíti a szakértelmet, ami tovább rontja a kód minőségét — ez ördögi kör.
Első lépés — kezdje a rétegek elkülönítésével. Oszsza fel a kódot három szintre: presentation (UI, vezérlők), business logic (szolgáltatások, use case-ek) és data access (repository-k, DAO). Már a részleges szétválasztás is azonnal javítja a szerkezetet, és tesztelhetővé teszi a kódot.
Második lépés — vezessen be dependency injection-t. Cserélje ki a függőségek közvetlen létrehozását a konstruktoron vagy paramétereken keresztüli átadásra. Ez megszakítja a merev kapcsolatokat a komponensek között, és lehetővé teszi az egyes modulok izolált tesztelését.
Harmadik lépés — különítse el a god-osztályokat és god-függvényeket. Oszsza fel őket kis, egyetlen felelősséggel rendelkező osztályokra és metódusokra. Használja a Facade mintát a bonyolult alrendszerek egyszerűsítéséhez. Ne feledje: egy 20 soros osztály érthetőbb, mint egy 2000 soros.
// spagetti — minden egy metódusban
function handleRequest(req, res) {
const db = new Database("mysql://...");
const user = db.query("SELECT * FROM users WHERE id =" + req.params.id);
let html = "";
html += ""
+ user.name + "";
html += "Balance: "
+ user.balance + "";
html += "";
res.send(html);
}
// tiszta architektúra — szétválasztott rétegek
class UserController {
constructor(userService) {
this.userService = userService;
}
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(new UserResponse(user));
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async findById(id) {
return await this.userRepository.findById(id);
}
}
Ne próbálja meg egyszerre újraírni a teljes kódbázist — ez garantált kudarc. Válasszon egy modult, írjon hozzá teszteket (characterization tests), amelyek rögzítik az aktuális viselkedést, és csak utána refaktoráljon. Fokozatosan, modulról modulra kibogozza a spagettit.
Az architekturális tervezés — a megelőzés alapja. A fejlesztés megkezdése előtt hagyja jóvá az architekturális stílust: MVC, MVVM, Clean Architecture, VIPER vagy más. Írjon ADR-t (Architecture Decision Record) a választás indoklásával. Követelje meg az architektúra betartását a kód-átvizsgáláson.
A függőségmegfordítás elve (DIP) — hatékony eszköz a spagettikód elleni küzdelemben. A magas szintű modulok nem függhetnek az alacsony szintű moduloktól. Mindkettőnek absztrakcióktól kell függenie. A Dependency Injection ennek az elvnek a gyakorlati megvalósítása.
A tesztelés — a legjobb megelőzés. Ha a kód előtt teszteket ír (TDD), elkerülhetetlenül laza kapcsolódású komponenseket tervez. A tesztelhető kód jól strukturált kód. A nem tesztelhető kód szinte mindig spagettikód.
SonarQube — figyeli a ciklomatikus komplexitást, az öröklődési mélységet, a metódusok méretét. JDepend (Java) — méri a csomagok közötti függőségeket. PhpMetrics — maintainability indexet ad a PHP-projektekhez. Figyelje a metrikákat a CI/CD-ben — előzze meg a tészta megjelenését, ne utólag küzdjön vele.
Gyakran ismételt kérdések
Igen, a fokozatos refaktorálás előnyösebb. Használja a Strangler Fig módszert — fokozatosan cserélje le a régi komponenseket újakra, anélkül hogy leállítaná az alkalmazást. Kezdje az adatréteg vagy az üzleti logika elkülönítésével. Fedje le a régi kódot tesztekkel a módosítások előtt, hogy ne veszítse el a funkcionalitást.
A spagettikód — az alkalmazás összes rétegének kaotikus összefonódása. A lasagna code — szigorú többrétegű architektúra, de minden réteg annyira izolált, hogy a köztük lévő adatátvitel bürokráciává válik. Mindkét antipattern káros, de a spagettikód veszélyesebb — kiszámíthatatlanná teszi a kódot.
Nézze a függőségeket: ha egy modul az alkalmazás összes rétegéből importál modulokat — az gyanús. Figyeljen a metódusok méretére — a 30 sornál több általában rossz. Ellenőrizze, hogy a függvény keveri-e az UI-kezelést, az üzleti logikát és az adatokat. Ha igen — az spagettikód.
Robert C. Martin Clean Architecture-je és a Hexagonal Architecture (Ports & Adapters) — a két legjobb megközelítés. Mindkettő garantálja a rétegek szétválasztását, az üzleti logika függetlenségét a keretrendszerektől és a tesztelhetőséget. Mobilfejlesztéshez — MVVM Repository mintával.
Részben. Az olyan metrikák, mint a ciklomatikus komplexitás (McCabe), a modulok kapcsoltsága (Coupling) és az öröklődési mélység (DIT), potenciális spagettikódra utalnak. A SonarQube, a CodeClimate és a PhpMetrics automatikusan számolja ezeket a metrikákat. A teljes diagnózis azonban az architektúra emberi elemzését igényli.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is