Код шпагета (шпагети код, резанци) — је заплетена, хаотична структура програма, у којој су логички блокови испреплетени без икаквог реда. Према подацима истраживања TIOBE Index (2024), пројекти са високим нивоом шпагети кода захтевају 2,5 пута више времена за увођење нових функција. Термин је настао у епохи раног програмирања, када је оператор goto омогућавао скакање између било којих тачака програма, стварајући нечитљиве конструкције.
Главно
Код шпагета (шпагети код) — је метафора за опис кода чија структура подсећа на тањир шпагета: појединачни конци (логички блокови) су испреплетени, залепљени и неодвојиви један од другог. У таквом коду није могуће издвојити слојеве, модуле или компоненте — све је помешано у једној великој маси.
За разлику од лошег кода, који може бити једноставно неуредан, код шпагета је фундаментални архитектурни проблем. Чак и савршено форматиран код са добрим називима променљивих може бити шпагети код ако је његова архитектура хаотична. Проблем лежи на нивоу структуре програма, а не стила писања.
Према подацима IEEE (2022), око 35% свих грешака у великим пројектима изазвано је управо заплетеном структуром кода, а не логичким грешкама програмера. Програмер греши не зато што је погрешно разумео задатак, већ зато што није могао да прати ток извршавања у шпагети коду.
Ако је лош код лош код у размери једне функције или датотеке, онда је код шпагета лоша архитектура у размери целог програма. Резанци могу да се састоје од појединачних добро написаних функција, али њихова интеракција је хаотична и непредвидива.
Термин „spaghetti code“ се појавио у годинама 1970. са критиком оператора goto. У раним језицима програмирања (BASIC, FORTRAN, COBOL), goto је био основни начин управљања током извршавања. Програм је представљао секвенцу нумерисаних линија, а goto је омогућавао скок на било коју од њих. То је стварало „клупко“ скокова које је било немогуће расплести.
Године 1968, Edsger Dijkstra је објавио чувени чланак „Go To Statement Considered Harmful“, који је означио почетак ере структурираног програмирања. Dijkstra је доказао да се било који алгоритам може имплементирати без goto, користећи само три конструкције: секвенцу, разгранање (if) и петљу (while). То је постало темељем модерног програмирања.
Структурирано програмирање није потпуно елиминисало проблем. Код шпагета је прешао на нови ниво — уместо физичких goto, програмери су почели да стварају логичке „goto“: глобалне променљиве, callback hell у JavaScript, сложене ланце позива и имплицитне зависности између компонената. Проблем је остао, само се облик променио.
Callback hell у JavaScript, дубоко угнеждени Promise, async/await без обраде грешака, догађаји које се не зна ко и када покреће — све су то савремени варијације кода шпагета. Антиобразац је жив и напредује, само што сада не користи оператор goto.
Недостатак слојева — први и главни знак. У коду шпагета, пословна логика, рад са базом података, HTML шаблон и мрежна комуникација су помешани у једној датотеци или чак у једној методи. Промена упита бази података може да поквари приказ корисничког интерфејса, јер код ових слојева није одвојен.
Глобалне променљиве и синглтони — други јасни знак. Када се стање апликације чува у глобалним објектима, ток извршавања постаје непредвидив. Било која функција може да промени глобално стање, а праћење где и када се то догодило је практично немогуће.
God-класе и god-функције — трећи знак. Класа од 2000+ редова која је одговорна и за пословну логику, и за приказивање, и за рад са подацима — то је типичан код шпагета. Функција која прима 10 параметара и ради 5 различитих ствари — такође.
| Знак | Опис | Пример |
|---|---|---|
| Мешање слојева | SQL упити унутар кода корисничког интерфејса | Controller са директним уписом у базу података |
| Глобалне променљиве | Стање доступно одасвуда | static SessionManager у свакој класи |
| God-класе | Једна класа ради све | OrderManager на 3000 редова |
| Дугачке методе | Функције без поделе | Метода од 200 редова са 5 одговорности |
| Callback hell | Угнеждени callback-и без краја | 6 нивоа угнеждавања у JavaScript |
Ако не можете да напишете јединични тест за функцију без креирања 15 mock објеката — то је код шпагета. Ако тестирање једног модула захтева покретање целокупне инфраструктуре апликације — то је код шпагета. Немогућност тестирања је објективни показатељ заплетене архитектуре.
Недостатак архитектурног пројектовања — најчешћи узрок. Када тим почне да пише код без плана, бирајући архитектуру „у ходу“, резултат се неизбежно претвара у шпагете. Свака нова функција се додаје тамо где „је тренутно згодно“, а не тамо где јој је место према логици.
Еволуциони развој — други узрок. Пројекат почиње као мали скрипт, затим зарасте функцијама, затим постаје апликација, а затим — монолит. Архитектура се притом не преиспитује. Оно што је радило за 100 редова кода постаје катастрофа за 100 000 редова.
Кршење принципа SOLID — трећи узрок. Посебно принципа јединствене одговорности (S) и инверзије зависности (D). Када је класа одговорна за све, зависности су чврсте, а модули чврсто повезани — добија се код шпагета.
Рокови и hotfix култура — катализатори кода шпагета. Када „требало је јуче“, програмери умећу код на прво место које нађу, не размишљајући о архитектури. Десет таквих hotfix-ова — и архитектура апликације је уништена.
Главна последица — губитак контроле над базом кода. Програмери престају да разумеју како апликација функционише у целини. Промена на једном месту квари друго, привидно неповезано. Свака закрпа ствара два нова бага. Тим улази у стање „страха од промена“.
Продуктивност тима опада експоненцијално. Истраживање Microsoft Research (2023) је показало да време додавања нове функције у шпагети коду расте по квадратном закону у односу на величину базе кода. За чисту архитектуру овај раст је линеаран. Разлика постаје критична код 50 000+ редова кода.
Сигурност — још једна жртва. У коду шпагета лако је превидети необрађени изузетак, неправилну валидацију улаза или цурење података. Ревизија сигурности у пројекту са заплетеном архитектуром је практично немогућа — пронаћи сва места где се користи кориснички улаз је нереално.
Флуктуација у пројектима са кодом шпагета је изнад просека. Искусни програмери одлазе јер не желе да раде са „резанцима“. Нови запослени не могу да разумеју код и одлазе у првим месецима. Пројекат губи експертизу, што још више погоршава квалитет кода — зачарани круг.
Прво — почните са издвајањем слојева. Поделите код на три нивоа: presentation (UI, контролери), business logic (сервиси, use cases) и data access (репозиторијуми, DAO). Чак и делимично одвајање одмах побољшава структуру и чини код тестабилним.
Друго — уведите dependency injection. Замените директно креирање зависности прослеђивањем кроз конструктор или параметре. То прекида чврсте везе између компонената и омогућава тестирање сваког модула изоловано.
Треће — издвојите god-класе и god-функције. Поделите их на мале класе и методе са јединственом одговорношћу. Користите образац Facade за поједностављивање сложених подсистема. Запамтите: класа од 20 редова је разумљивија од класе од 2000 редова.
Немојте да покушавате да препишете целу базу кода одједном — то је гарантовани неуспех. Одаберите један модул, напишите за њега тестове (characterization tests) који фиксирају тренутно понашање, и тек онда рефакторишите. Постепено, модул по модул, расплешћете шпагете.
// spaghetti — све у једној методи
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);
}
// чиста архитектура — одвојени слојеви
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);
}
}
Архитектурно планирање — основа превенције. Пре почетка развоја, одобрите архитектурни стил: MVC, MVVM, Clean Architecture, VIPER или други. Напишите ADR (Architecture Decision Record) са образложењем избора. Захтевајте поштовање архитектуре на code review.
Принцип инверзије зависности (DIP) — моћан алат у борби против кода шпагета. Модули високог нивоа не треба да зависе од модула ниског нивоа. Оба треба да зависе од апстракција. Dependency Injection — практична имплементација овог принципа.
Тестирање — најбоља превенција. Ако пишете тестове пре кода (TDD), неизбежно пројектујете слабо повезане компоненте. Тестабилан код — то је добро структуриран код. Нетестабилан код — готово је увек код шпагета.
SonarQube — прати цикломатску сложеност, дубину наслеђивања, величину метода. JDepend (Java) — мери зависности између пакета. PhpMetrics — даје индекс одржавања за PHP пројекте. Пратите метрике у CI/CD — спречите појаву резанаца, а не борите се са њима постфактум.
Често постављана питања
Да, постепено рефакторисање је пожељније. Користите методу Strangler Fig — постепено замењујте старе компоненте новима, не заустављајући рад апликације. Почните са издвајања слоја података или пословне логике. Покријте стари код тестовима пре промена да не изгубите функционалност.
Шпагети код — то је хаотично преплитање свих слојева апликације. Lasagna code — чврста вишеслојна архитектура, али сваки слој је толико изолован да се пренос података између њих претвара у бирократију. Оба антиобрасца су штетна, али је шпагети код опаснији — чини код непредвидивим.
Погледајте зависности: ако модул увози модуле из свих слојева апликације — то је сумњиво. Обратите пажњу на величину метода — више од 30 редова је обично лоше. Проверите да ли функција меша рад са UI, пословну логику и податке. Ако да — то је код шпагета.
Clean Architecture Роберта Мартина и Hexagonal Architecture (Ports & Adapters) — два најбоља приступа. Оба гарантују одвајање слојева, независност пословне логике од фрејмворка и тестабилност. За мобилни развој — MVVM са обрасцем Repository.
Делимично. Метрике као што су цикломатска сложеност (McCabe), повезаност модула (Coupling) и дубина наслеђивања (DIT) указују на потенцијални код шпагета. SonarQube, CodeClimate и PhpMetrics израчунавају ове метрике аутоматски. Ипак, потпуна дијагностика захтева људску анализу архитектуре.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође