Макаронний код (спагеті-код, локшина) — це заплутана, хаотична структура програми, де логічні блоки переплетені без будь-якого порядку. За даними дослідження TIOBE Index (2024), проєкти з високим рівнем спагеті-коду потребують у 2.5 рази більше часу на впровадження нових функцій. Термін виник в епоху раннього програмування, коли оператор goto дозволяв перестрибувати між будь-якими точками програми, створюючи нечитабельні конструкції.
Головне
Макаронний код (spaghetti code) — це метафора для опису коду, структура якого нагадує тарілку спагеті: окремі нитки (логічні блоки) переплутані, склеєні та невіддільні одна від одної. У такому коді неможливо виділити шари, модулі або компоненти — все змішано в одній великій масі.
На відміну від говнокоду, який може бути просто неакуратним, макаронний код — це фундаментальна архітектурна проблема. Навіть ідеально відформатований код із хорошими іменами змінних може бути спагеті-кодом, якщо його архітектура хаотична. Проблема лежить на рівні структури програми, а не стилю написання.
За даними IEEE (2022), близько 35% усіх помилок у великих проєктах спричинені саме заплутаною структурою коду, а не логічними помилками розробника. Розробник припускається помилки не тому, що неправильно зрозумів задачу, а тому що не зміг прослідкувати потік виконання в спагеті-коді.
Якщо говнокод — це поганий код у масштабі однієї функції або файлу, то макаронний код — погана архітектура в масштабі всього додатка. Локшина може складатися з окремих добре написаних функцій, але їхня взаємодія хаотична та непередбачувана.
Термін «спагеті-код» з'явився в 1970-х роках разом із критикою оператора goto. У ранніх мовах програмування (BASIC, FORTRAN, COBOL) goto був основним способом керування потоком виконання. Програма являла собою послідовність пронумерованих рядків, а goto дозволяв перейти до будь-якого з них. Це створювало «клубок» переходів, який неможливо було розплутати.
У 1968 році Едсгер Дейкстра опублікував знаменитий лист «Go To Statement Considered Harmful», який започаткував епоху структурованого програмування. Дейкстра довів, що будь-який алгоритм можна реалізувати без goto, використовуючи лише три конструкції: послідовність, розгалуження (if) та цикл (while). Це стало фундаментом сучасного програмування.
Структуроване програмування не усунуло проблему повністю. Макаронний код перейшов на новий рівень — замість фізичних goto розробники почали створювати логічні «goto»: глобальні змінні, callback hell у JavaScript, складні ланцюжки викликів і неявні залежності між компонентами. Проблема залишилася, змінилася лише форма.
Callback hell у JavaScript, глибоко вкладені Promise, async/await без обробки помилок, події, які незрозуміло хто й коли ініціює — все це сучасні різновиди спагеті-коду. Антипатерн живе й процвітає, просто тепер він не використовує оператор goto.
Відсутність шарів — перша й головна ознака. У макаронному коді бізнес-логіка, робота з базою даних, HTML-верстка та мережева взаємодія знаходяться вперемішку в одному файлі або навіть в одному методі. Зміна запиту до БД може зламати відображення UI, тому що код цих шарів не розділений.
Глобальні змінні та синглтони — друга явна ознака. Коли стан додатка зберігається в глобальних об'єктах, потік виконання стає непередбачуваним. Будь-яка функція може змінити глобальний стан, і відстежити, де й коли це сталося, практично неможливо.
God-класи та god-функції — третя ознака. Клас на 2000+ рядків, який відповідає і за бізнес-логіку, і за відображення, і за роботу з даними — це типовий спагеті-код. Функція, яка приймає 10 параметрів і робить 5 різних речей — теж.
| Ознака | Опис | Приклад |
|---|---|---|
| Змішування шарів | SQL-запити всередині UI-коду | Контролер із прямим записом у БД |
| Глобальні змінні | Стан, доступний звідусіль | static SessionManager у кожному класі |
| God-класи | Один клас робить все | OrderManager на 3000 рядків |
| Довгі методи | Функції без розбиття | Метод на 200 рядків із 5 відповідальностями |
| Callback hell | Вкладені колбеки без кінця | 6 рівнів вкладеності у JavaScript |
Якщо ви не можете написати unit-тест для функції без створення 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 рядків.
// спагеті — все в одному методі
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);
}
}
Не намагайтеся переписати всю кодову базу одразу — це гарантований провал. Виберіть один модуль, напишіть для нього тести (characterization tests), які фіксують поточну поведінку, і тільки потім рефакторте. Поступово, модуль за модулем, ви розплутаєте спагеті.
Архітектурне планування — основа профілактики. Перед початком розробки затвердьте архітектурний стиль: MVC, MVVM, Clean Architecture, VIPER або інший. Напишіть ADR (Architecture Decision Record) з обґрунтуванням вибору. Вимагайте дотримання архітектури на код-рев'ю.
Принцип інверсії залежностей (DIP) — потужний інструмент боротьби зі спагеті-кодом. Модулі високого рівня не повинні залежати від модулів низького рівня. Обидва мають залежати від абстракцій. Dependency Injection — практична реалізація цього принципу.
Тестування — найкраща профілактика. Якщо ви пишете тести до коду (TDD), ви неминуче проєктуєте слабко зв'язані компоненти. Тестований код — це добре структурований код. Нетестований код — майже завжди спагеті-код.
SonarQube — відстежує цикломатичну складність, глибину наслідування, розмір методів. JDepend (Java) — вимірює залежності між пакетами. PhpMetrics — дає maintainability index для 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також