Несколько советов, как избежать спагетти-кода

Для любого приложения существует бесконечное количество способов его реализации. При разработке программного обеспечения всегда можно следовать лучшим практикам и технологиям, которые призваны помочь в достижении ваших целей, но проблема в том, что лучшие методы и доступные технологии постоянно меняются, обычно приводя к гниению систем.

Даже при проведении качественных исследований и выборе инструментов, которые кажутся идеально подходящими, через некоторое время (надеюсь, по крайней мере, несколько лет) обычно происходит неизбежное - программное обеспечение, которое было создано с осторожностью, лучшими инженерами, с использованием лучших технологий на время, станет устаревшим и (что более важно) даже громоздким в работе. Если предположить, что наиболее важные принципы разработки программного обеспечения: такие как принцип единой ответственности, DRY (не повторяйтесь), KISS (сохраняйте простоту) и т. Д. Были последовательно применены, полученный код, вероятно, нельзя назвать «Спагетти», но, тем не менее, кое-что случилось, и результат тоже не был идеальным.

Почему это вообще имеет значение?

Программные системы в таком устаревшем состоянии вредны, потому что их трудно настраивать, и они замедляют общий прогресс. При этом не следует недооценивать важность этих устаревших систем - многие из старых систем, на которые опирается наш мир, имеют эти недостатки, но тем не менее они выполняют свою работу и, таким образом, по-прежнему полезны.

Кодовая база Pipedrive разделена на сотни микросервисов, над которыми работают несколько замечательных инженеров с большим энтузиазмом, но у нас все еще есть несколько сервисов, которые следуют менее благоприятной схеме, описанной выше.

Итак, что же в итоге приводит к созданию устаревшей кодовой базы, над которой сложно работать? Я считаю, что ответ на этот вопрос имеет решающее значение и ведет к практикам, которые помогают избежать повторения этого в будущем.

Как мы здесь оказались?

Подобно тому, как рецепт поможет вам завершить блюдо (спагетти), здесь я перечислю различные шаги, которые привели нас к тому, что я считаю неким гнилым кодом.

1. Гибкая разработка

Хотя гибкая разработка программного обеспечения в настоящее время считается «святым Граалем» (и я склонен согласиться с этим подходом), при написании программного обеспечения в гибкой манере основное внимание уделяется краткосрочному плану, что иногда приводит к созданию работающего продукта. при поддержке кодовой базы спагетти.

В таких ситуациях следующая итерация может исправить некоторые из предыдущих проблем, но чаще всего добавляет еще больше.

2. Преждевременная оптимизация

Можно было бы подумать, что попытка придумать более чистую структуру кода - это именно то, что нужно делать, чтобы избежать кодовой базы, над которой сложно работать, но я должен сказать, что несколько раз видел, как получалось наоборот. Стремление к более чистому коду помогает избежать спагетти, но может создать лазанью или ад микросервисов.

Код с длинной трассировкой стека может быть не только трудным для отслеживания, но также трудно найти правильный уровень для реализации изменений, и рудиментарные части кода, которые широко не используются и не разрабатываются, начинают гнить.

3. Мудрая пассивность

Большая часть сложного в работе кода создается мантрой, которую часто говорят при разработке программного обеспечения… 🥁…: «если он не сломан, не исправляйте его.

Хотя этот подход имеет смысл, когда целью является поддержание стабильности системы, часто он также является причиной того, что код не подвергается рефакторингу, миграции на новые технологии не выполняются и зависимости не обновляются. Все это в конечном итоге может привести к ситуации, когда кодовая база включает в себя - хакерские решения, действительно трудно поддается рефакторингу или тесно связана с древней зависимостью, которую невозможно обновить.

Риски, которые несет обновление, часто перевешивают выгоды, но со временем риск может превратиться в недостижимый, в то время как выгоды станут необходимостью.

Окончательный совет

Итак, что же тогда делать? К сожалению, я еще этого не понял. Возможно, большинство систем с длительным сроком эксплуатации неизбежно уйдут в наследство. Может быть, это слишком сложно изменить, и лучше всего принять это.

Хотя я не могу предвидеть будущее, вот мои [основанные на мнении] предложения по тому, как сделать кодовую базу более «ориентированной на будущее».

Не существует универсального руководства или абсолютной истины, которые помогут вам построить каждый аспект приложения наилучшим образом. То же самое и с предложениями, изложенными в этой статье. Он не предназначен для заполнения недостающих пробелов в процессах, и представленные здесь мысли не должны использоваться для разжигания конфликта или слепо следовать им.

Упростите рефакторинг

Сделайте это приоритетом при написании строчки кода. Обычно при гибкой разработке наиболее важным моментом является создание как можно меньшего количества, чтобы удовлетворить потребности дня, а не чрезмерное проектирование, потому что потребности могут измениться на следующий день.

Я думаю, что это хороший образ мышления, но на самом деле он заставит вас задуматься еще на один шаг дальше - если вы уже знаете, что будет много (все еще неизвестных) изменений, упростите их применение.

Простота рефакторинга кодовой базы часто рассматривается как результат структуры кода. Часто упускается из виду тот факт, что разные технологии и языки имеют разные кривые рефакторинга. Что это значит на практике?
К счастью, у меня есть очень научный (и профессиональный) график:

В настоящее время многим разработчикам не нравятся языки со статической типизацией, и я разделяю некоторые их опасения по поводу добавленных шаблонов и системы типов, ограничивающих скорость разработки. С другой стороны, перемещение и переименование файлов, функций и переменных на статически типизированном языке (например, Java) с помощью компилятора и IDE можно выполнять, не опасаясь нарушения функциональности. Несмотря на то, что IDE развивались, этого нельзя сказать о языках с динамической типизацией.

Вместо того, чтобы пытаться разработать идеальную структуру каталогов репозитория, упростите ее изменение в будущем. Этого можно достичь, используя технологии и методы структурирования кода, поддерживающие эту идею. Хотя на самом деле ни один из них не является статически типизированным, вы все равно можете считать это одним из преимуществ Typescript по сравнению с Javascript.

Чтобы было ясно, я определенно не утверждаю, что языки со статической типизацией лучше, а скорее, что у них есть свои преимущества и недостатки. Все зависит от приложения, команды и того, как это настроено.

Причина писать тесты

Я много раз слышал в своей жизни, что люди говорят, что тесты бесполезны и просто замедляют прогресс. Почти каждый приходит к мысли: «Зачем мне писать код, а затем писать другой тривиальный код для его запуска, совершенно ненужный».
Я сам определенно иногда чувствовал раздражение от создания тестов.

Конечно, необходимость применения изменений в будущем почти всегда оправдывает написание тестов сегодня. Модульные тесты полезны для проверки небольших структурных изменений и функциональных тестов для проверки более крупных рефакторингов. Рефакторинг кода, в котором нет тестов, - все равно что безупречное выполнение ваших новогодних обещаний - вы вряд ли это сделаете.

Самое большое преимущество наличия тестов качества заключается не в том, что тесты проверяют поведение кода, а в том, что тесты позволяют использовать будущие рефакторы 💡 - проверяя поведение.

Привлекайте новых людей

Правда о качестве и удобочитаемости кода по-настоящему раскрывается только при привлечении инженеров, которые раньше не работали с этой кодовой базой. Все, даже если это не так, обретает смысл, если на него смотреть и настраивать в течение длительного периода времени. Но тот, кто смотрит код в первый раз, склонен придерживаться другого взгляда и может быть не удовлетворен решениями.

Излишне добавлять, но новички получают выгоду и улучшают кодовую базу, привнося новые идеи и применяя другое мышление и навыки.

Наличие новых людей не обязательно означает постоянный найм, это можно легко организовать, просто поменяв несколько инженеров, которые готовы выйти из своей зоны комфорта и переключаться между проектами. В Pipedrive нам для этого хорошо послужила используемая структура миссии.

Хотя статья, безусловно, могла бы извлечь из этого пользу, у меня, к сожалению, нет графика для этого, поэтому я вместо этого использовал свои художественные навыки и нарисовал эту картинку, чтобы помочь вам визуализировать:

Юниоры!

Чем более опытен разработчик, тем лучше он понимает сложную реализацию, но редко когда есть веская причина придерживаться очень сложной реализации. Дополнительные 10 уровней абстракции и обобщения не нужны. Умный и крутой лайнер, который затрудняет чтение кода, выглядит красиво, но в конечном итоге от него нужно отказаться. Держите его кратким, легким для понимания, чтения и редактирования.

Если код слишком сложен для понимания младшим, его нужно сделать проще. Эта простая концепция делает юниоров очень важной частью любого проекта. Пусть проверят код! Отсутствие у них опыта иногда может оказаться неожиданно полезным.

Обновить зависимости

Сделайте регулярным обновление зависимостей и фреймворков. В тот момент, когда вы почувствуете функциональную потребность в обновлении, может быть уже слишком поздно переносить вашу службу на новое обновление. Следите за развитием ваших зависимостей, например, подписываясь на их списки объявлений о выпусках.

В последнее время несколько инструментов начали предлагать автоматическое обновление зависимостей, обычно связанных с проблемами безопасности, обнаруженными в библиотеках. Это должен быть шаг в правильном направлении, но не останавливайтесь на достигнутом.

Версия триггера проверяется даже при отсутствии известных проблем с безопасностью. Каждое обновление по-прежнему требует специального просмотра журнала изменений, не применяйте обновление к зависимостям вслепую. Я бы также рекомендовал не торопиться с обновлением зависимостей до последних версий сразу после выпуска - обычно когда что-то недавно выпускается, поступает большой поток поступающих ошибок.

Принять новые технологии

Все новое вызывает постоянный ажиотаж, и обычно неразумно быть первым, кто меняет технологический стек. Чтобы воспользоваться преимуществами всего хорошего, которое приносят новые технологии и методы, потребуется изменение существующих систем.

На мой взгляд, многие идеи, развивающиеся в мире разработки программного обеспечения, связаны с одной и той же темой - как избежать создания наследства. Полезно следить за новинками. Многие из новых идей могут быть успешно перенесены в существующий стек.

Кроме того, регулярно подвергайте сомнению решения, принятые в базе кода. Возможно, что-то, что выглядело хорошо и имело смысл некоторое время назад, больше не актуально. Не так давно следование объектно-ориентированным парадигмам казалось лучшим подходом, но в настоящее время многие согласятся, что расширение класса ограничивает, а возможность переопределения метода только создает путаницу.

Раздели это

И последнее, но не менее важное: старайтесь, чтобы отдельные части вашего приложения были небольшими. Этого можно достичь, просто сделав свой код менее связанным или, возможно, с помощью микросервисов. Могут быть и другие преимущества, но основная причина разделения приложения на более мелкие независимые фрагменты заключается в том, что более мелкие фрагменты, как правило, легче переваривать, и дает возможность перезаписывать части.

Заключение

Есть много полезных принципов разработки программного обеспечения. Продолжайте использовать каждый из них, пока он имеет смысл и служит цели. Одновременно попробуйте пересмотреть эти идеи, поскольку по мере роста вашей кодовой базы или технологий, связанных с изменениями, они могут не иметь такого большого смысла, как раньше.

Гораздо важнее сделать код, который можно отредактировать, чем написать лучший код для первого развертывания.

Тесты включают возможности рефакторинга, поэтому постарайтесь не забыть использовать эти возможности: найдите время, чтобы обновить зависимости и принять новые технологии. Это сохранит ваше приложение в безопасности и прохладном месте. Опять же, приглашайте новых людей и слушайте, что они говорят - это лучший источник новых идей. Наконец, попытайтесь реорганизовать кодовую базу всякий раз, когда увидите в этом необходимость.

Адаптируйте к новому, пока он не устарел, исправьте его, пока он не сломался. Удачи!

Я очень хочу прочитать в комментариях, что вы думаете о том, почему код со временем гниет, и с чем вы согласны или не согласны в статье.