Учёный Эдсгер Дейкстра ввёл в оборот термин «spaghetti-code». В 1968 году он опубликовал статью «Аргументы против оператора GOTO», в которой высмеивал использование этого оператора в коде.
В то время программисты часто работали на языках Fortran, ассемблер и COBOL. Стандартов и чётких правил написания кода не существовало, поэтому программы получались запутанными и трудными для понимания.
Спагетти-код можно встретить в старых программах, но современные программисты стараются писать чистый и понятный код.
Причины появления спагетти-кода
Существует несколько причин, которые могут привести к появлению спагетти-кода:
- Спешка и сжатые сроки. Когда разработчику нужно срочно выпустить программу, он может не обращать внимания на качество кода. Главное — запустить программу, а потом уже разбираться. Например, стартапу нужно быстро добавить новую функцию, и программист может использовать временные решения. В результате была добавлена логика, но если её немного изменить, всё пойдёт не так. Нет чёткого плана.
- Если программист начинает писать код без чёткой структуры, это приводит к хаосу. Например, бэкенд-разработчик пишет API, но вместо чёткой архитектуры просто добавляет обработчики в один файл. В итоге логика становится запутанной, и некоторые условия повторяются. Лучше использовать роутер, который будет направлять запросы в нужное место. А контроллеры помогают не смешивать логику в кучу — так код легче читать и поддерживать.
- GOTO и бесконечные условия. GOTO — это оператор, который заставляет программу перескакивать с одной части кода на другую, игнорируя логические условия. Хотя такие логические прыжки уже почти не используются, эта команда всё ещё встречается. Если фронтенд-разработчик обрабатывает формы и вместо нормального кода пишет цепочку if-else, логика становится запутанной.
В результате программа работает нестабильно и не всегда корректно.
- Когда код не организован, в нём могут быть ошибки. Из-за этого программа может работать неправильно или вообще не запускаться. Например, если в коде есть ошибка, то программа может выдать некорректный результат. Или же она может вообще не запуститься, и пользователи не смогут воспользоваться программой.
- Чем сложнее программный код, тем больше вероятность возникновения ошибок. В коде, который похож на спагетти, любое изменение может повлиять на разные части программы. Например, разработчик исправил одну ошибку, а через неделю из-за этого перестала работать другая часть системы. Допустим, программист добавил новую проверку при входе в систему. Но из-за того, что код был плохо организован, эта проверка стала блокировать часть пользователей без причины. В результате пользователи начали жаловаться, служба поддержки не могла понять, что происходит, а разработчик тратил дни на поиск проблемы.
- Чужой код, написанный в стиле спагетти, — это настоящий кошмар для любого программиста. Представьте, что вам нужно внести небольшое изменение в механизм отправки уведомлений. Вы открываете код и понимаете, что ничего не понимаете. В одном месте код делает одно, в другом — другое, одни и те же проверки встречаются несколько раз, а при попытке что-то изменить ломается всё. В итоге вы тратите много времени, пытаясь разобраться в этом хаосе. Или просто добавляете ещё одно временное условие, чтобы программа хоть как-то работала. Через несколько месяцев таких изменений код превращается в настоящую свалку, и вряд ли кто-то решится его исправить.
Как избежать спагетти в коде?
Давайте рассмотрим четыре основных принципа, которые помогут вам поддерживать порядок в коде.
- Принцип инверсии зависимостей. Ошибка, которую часто допускают разработчики, заключается в том, что они заставляют модули высокого уровня напрямую зависеть от модулей низкого уровня. Однако это неверное решение, поскольку оба уровня должны зависеть от абстракции. Рассмотрим пример интернет-магазина, который работает с конкретной платёжной системой. Если эту систему заменить, то потребуется переписать весь код. Чтобы избежать такой ситуации, лучше использовать абстракции. Необходимо напрямую привязать логику к общим интерфейсам, а не к конкретным реализациям. Тогда бизнес-логика будет опираться на общий интерфейс, что позволит менять отдельные части кода без необходимости изменения всей системы.
- Принцип открытости и закрытости. Код должен быть открыт для расширения функциональности, но закрыт для изменений, которые могут привести к ошибкам или поломке программы. Если постоянно вносить изменения в старый код, чтобы добавить новые функции, это может привести к проблемам. Лучше заранее спроектировать систему так, чтобы можно было добавлять новые функции без необходимости изменения существующего кода. Например, если система уведомлений изначально была разработана только для отправки электронных писем, но позже возникла необходимость в отправке уведомлений через push-уведомления или SMS. При правильной архитектуре можно просто добавить новый модуль, не изменяя существующую структуру.
- Принцип разделение интерфейса. В программировании существует проблема, когда в одном интерфейсе или классе содержится слишком много ненужных функций. Например, в системе учёта сотрудников может быть общий интерфейс для всех специалистов, и каждый из них имеет доступ ко всем методам. Однако бухгалтеру не нужно знать, как писать код, а программисту — как рассчитывать зарплаты. Чтобы избежать этой проблемы, интерфейсы следует делать небольшими и специализированными. Каждый класс должен содержать только те методы, которые необходимы конкретной группе пользователей.
- Принцип единственной ответственности (Single Responsibility Principle) В соответствии с этим принципом, каждый модуль должен выполнять только одну функцию. Например, в интернет-магазине один модуль отвечает за оформление заказа, другой — за оплату, а третий — за уведомления. Если внести изменения в систему оплаты, это может привести к сбою всего процесса оформления заказа. Для того чтобы избежать подобных проблем, лучше разделить логику на отдельные части. Это позволит каждому разделу выполнять только свою задачу, что упростит тестирование и понимание кода. Специалист сможет вносить изменения, не опасаясь повредить систему.