Spagettikod (spaghetti code, nudlar) — en förvirrad, kaotisk programstruktur där logiska block är sammanflätade utan någon ordning. Enligt undersökningar från TIOBE Index (2024) kräver projekt med hög förekomst av spagettikod 2,5 gånger mer tid för att införa nya funktioner. Termen uppstod under den tidiga programmeringens era, när goto-operatorn tillät hopp mellan vilka programpunkter som helst och skapade oläsbara konstruktioner.
Det viktigaste
Spagettikod (spaghetti code) — en metafor för att beskriva kod vars struktur påminner om en tallrik spaghetti: de enskilda trådarna (logiska block) är ihoptrasslade, sammanlödda och går inte att skilja åt. I sådan kod är det omöjligt att urskilja lager, moduler eller komponenter — allt är blandat i en enda stor massa.
Till skillnad från dålig kod, som bara kan vara slarvig, är spagettikod ett fundamentalt arkitektoniskt problem. Även perfekt formaterad kod med bra variabelnamn kan vara spagettikod om dess arkitektur är kaotisk. Problemet ligger på programmets strukturnivå, inte på skrivstilsnivå.
Enligt IEEE (2022) orsakas cirka 35 % av alla fel i stora projekt just av förvirrad kodstruktur, inte av utvecklarens logiska misstag. Utvecklaren gör fel inte för att hen missförstått uppgiften, utan för att hen inte kunnat följa exekveringsflödet i spagettikoden.
Om dålig kod är dålig kod i skalan av en funktion eller fil, så är spagettikod dålig arkitektur i skalan av hela applikationen. Nudlar kan bestå av enskilda välskrivna funktioner, men deras interaktion är kaotisk och oförutsägbar.
Termen “spagettikod” uppstod på 1970-talet i samband med kritiken av goto-operatorn. I tidiga programmeringsspråk (BASIC, FORTRAN, COBOL) var goto det huvudsakliga sättet att styra exekveringsflödet. Programmet bestod av en sekvens numrerade rader, och goto tillät hopp till vilken som helst av dem. Detta skapade ett “nystan” av hopp som var omöjligt att reda ut.
År 1968 publicerade Edsger Dijkstra det berömda brevet “Go To Statement Considered Harmful”, som inledde den strukturerade programmeringens era. Dijkstra bevisade att vilken algoritm som helst kan implementeras utan goto, med hjälp av endast tre konstruktioner: sekvens, förgrening (if) och loop (while). Detta blev grunden för modern programmering.
Strukturerad programmering eliminerade inte problemet helt. Spagettikoden övergick till en ny nivå — istället för fysiska goto började utvecklare skapa logiska “goto”: globala variabler, callback hell i JavaScript, komplexa anropskedjor och implicita beroenden mellan komponenter. Problemet finns kvar, bara formen har ändrats.
Callback hell i JavaScript, djupt nästlade Promise, async/await utan felhantering, händelser som ingen vet vem eller när de utlöser — allt detta är moderna varianter av spagettikod. Anti-mönstret lever och frodas, bara att det nu inte använder goto-operatorn.
Avsaknad av lager — det första och viktigaste tecknet. I spagettikod ligger affärslogik, databasarbete, HTML-layout och nätverkskommunikation blandat i en fil eller till och med i en metod. En ändring av en databasfråga kan förstöra UI-visningen, eftersom koden i dessa lager inte är separerad.
Globala variabler och singletons — det andra tydliga tecknet. När applikationens tillstånd lagras i globala objekt blir exekveringsflödet oförutsägbart. Vilken funktion som helst kan ändra det globala tillståndet, och det är praktiskt taget omöjligt att spåra var och när detta skedde.
Gud-klasser och gud-funktioner — det tredje tecknet. En klass på 2000+ rader som ansvarar både för affärslogik, visning och datahantering — det är typisk spagettikod. En funktion som tar 10 parametrar och gör 5 olika saker är det också.
| Tecken | Beskrivning | Exempel |
|---|---|---|
| Lagerblandning | SQL-frågor i UI-kod | Controller med direkt databasinskrivning |
| Globala variabler | Tillstånd tillgängligt överallt | static SessionManager i varje klass |
| Gud-klasser | En klass gör allt | OrderManager på 3000 rader |
| Långa metoder | Funktioner utan uppdelning | Metod på 200 rader med 5 ansvarsområden |
| Callback hell | Oändligt nästlade callbacks | 6 nivåer av nästling i JavaScript |
Om du inte kan skriva ett enhetstest för en funktion utan att skapa 15 mock-objekt — det är spagettikod. Om testning av en modul kräver att hela applikationens infrastruktur startas — det är spagettikod. Otestbarhet är en objektiv indikator på förvirrad arkitektur.
Avsaknad av arkitektonisk planering — den vanligaste orsaken. När teamet börjar skriva kod utan plan, och väljer arkitektur “längs vägen”, blir resultatet oundvikligen spaghetti. Varje ny funktion läggs till där det “är bekvämt just nu”, inte där den hör hemma logiskt.
Evolutionär utveckling — den andra orsaken. Projektet börjar som ett litet skript, växer sedan med funktioner, blir sedan en applikation och därefter en monolit. Samtidigt ses arkitekturen inte över. Det som fungerade för 100 rader kod blir en katastrof för 100 000 rader.
Brott mot SOLID-principerna — den tredje orsaken. Särskilt principen om enda ansvar (S) och principen om beroendeinvertering (D). När en klass ansvarar för allt, beroenden är stela och moduler är hårt kopplade — får du spagettikod.
Deadlines och hotfix-kultur — katalysatorer för spagettikod. När det “skulle ha varit klart igår” stoppar utvecklare in koden på första bästa ställe, utan att tänka på arkitekturen. Tio sådana hotfixar — och applikationens arkitektur är förstörd.
Den viktigaste konsekvensen — förlust av kontroll över kodbasen. Utvecklarna slutar förstå hur applikationen fungerar som helhet. En ändring på en plats förstör en annan, till synes orelaterad. Varje patch skapar två nya buggar. Teamet hamnar i ett tillstånd av “rädsla för förändringar”.
Teamets produktivitet faller exponentiellt. En undersökning från Microsoft Research (2023) visade att tiden för att lägga till en ny funktion i spagettikod växer enligt en kvadratisk lag i förhållande till kodbasens storlek. För ren arkitektur är denna tillväxt linjär. Skillnaden blir kritisk vid 50 000+ rader kod.
Säkerheten — ännu ett offer. I spagettikod är det lätt att missa ett ohanterat undantag, felaktig indata-validering eller en dataläcka. En säkerhetsgranskning i ett projekt med förvirrad arkitektur är praktiskt taget omöjlig — det är orealistiskt att hitta alla ställen där användarinmatning används.
Personalomsättningen i projekt med spagettikod är högre än genomsnittet. Erfarna utvecklare slutar för att de inte vill arbeta med “nudlar”. Nya medarbetare kan inte sätta sig in i koden och slutar inom de första månaderna. Projektet förlorar expertis, vilket ytterligare försämrar kodkvaliteten — en ond cirkel.
För det första — börja med att separera lagren. Dela koden i tre nivåer: presentation (UI, kontrollers), business logic (tjänster, use cases) och data access (repositories, DAO). Även en partiell uppdelning förbättrar genast strukturen och gör koden testbar.
För det andra — inför dependency injection. Ersätt direkt skapande av beroenden med överföring via konstruktor eller parametrar. Detta bryter de stela banden mellan komponenter och gör det möjligt att testa varje modul isolerat.
För det tredje — dela upp gud-klasser och gud-funktioner. Bryt ner dem i små klasser och metoder med ett enda ansvar. Använd Facade-mönstret för att förenkla komplexa delsystem. Kom ihåg: en klass på 20 rader är tydligare än en klass på 2000 rader.
// spaghetti — allt i en metod
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);
}
// ren arkitektur — separerade lager
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);
}
}
Försök inte att skriva om hela kodbasen på en gång — det är ett säkert misslyckande. Välj en modul, skriv tester för den (characterization tests) som fångar nuvarande beteende, och refaktorisera först därefter. Steg för steg, modul för modul, reder du ut spagettin.
Arkitektonisk planering — grunden för förebyggande. Innan utvecklingen startar, fastställ arkitektonisk stil: MVC, MVVM, Clean Architecture, VIPER eller annan. Skriv en ADR (Architecture Decision Record) med motivering för valet. Kräv att arkitekturen följs vid kodgranskning.
Principen om beroendeinvertering (DIP) — ett kraftfullt verktyg mot spagettikod. Moduler på hög nivå ska inte vara beroende av moduler på låg nivå. Båda ska vara beroende av abstraktioner. Dependency Injection är en praktisk implementering av denna princip.
Testning — bästa förebyggande. Om du skriver tester före koden (TDD) designar du oundvikligen löst kopplade komponenter. Testbar kod är välstrukturerad kod. Otestbar kod är nästan alltid spagettikod.
SonarQube — spårar cyklomatisk komplexitet, arvsdjup, metodstorlek. JDepend (Java) — mäter beroenden mellan paket. PhpMetrics — ger maintainability-index för PHP-projekt. Följ mätvärdena i CI/CD — förebygg uppkomsten av nudlar istället för att bekämpa dem i efterhand.
Vanliga frågor
Ja, gradvis refaktorering är att föredra. Använd Strangler Fig-metoden — ersätt gradvis gamla komponenter med nya utan att stoppa applikationen. Börja med att separera datalagret eller affärslogiken. Täck gammal kod med tester innan ändringar, så att funktionaliteten inte går förlorad.
Spagettikod är en kaotisk sammanflätning av alla applikationens lager. Lasagna code — en strikt flerskiktad arkitektur, men varje lager är så isolerat att dataöverföring mellan dem blir byråkrati. Båda anti-mönstren är skadliga, men spagettikod är farligare — den gör koden oförutsägbar.
Titta på beroenden: om en modul importerar moduler från alla applikationens lager — det är misstänkt. Var uppmärksam på metodstorleken — mer än 30 rader är oftast dåligt. Kontrollera om funktionen blandar arbete med UI, affärslogik och data. Om så är fallet — det är spagettikod.
Robert Martins Clean Architecture och Hexagonal Architecture (Ports & Adapters) — två bästa tillvägagångssätt. Båda garanterar separation av lager, affärslogikens oberoende från ramverk och testbarhet. För mobilutveckling — MVVM med Repository-mönstret.
Delvis. Mätvärden som cyklomatisk komplexitet (McCabe), modulkoppling (Coupling) och arvsdjup (DIT) pekar på potentiell spagettikod. SonarQube, CodeClimate och PhpMetrics beräknar dessa mätvärden automatiskt. Men fullständig diagnostik kräver mänsklig analys av arkitekturen.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också