13/07/2023

Худшие практики разработки и архитектуры

Я собрал худшее из худшего! Оказалось, что хороших практик — море, и разбираться в них долго, а вот плохих, реально плохих, — считаные единицы.

Понятно, что плохие практики не отвечают на вопрос: «А как делать-то?» — но они помогают быстро разобраться в том, как не делать.

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

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

Это если команда одна. А если разработчики на пятом проекте новые, то начинается самое весёлое — этот сталактит надо ещё прочитать.

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

Поток лавы

Итак, дамы и господа, позвольте представить: первый на нашей сцене — поток лавы! В целом про него я уже примерно рассказал, и вы представляете, откуда он такой берётся и что делает. Обратите внимание: паттерн подкупающе хорош в кратковременной перспективе и становится плох только на дальней дистанции. И смертельно плох на очень дальней. Поэтому на всякий случай давайте формально отмечу основное.

Симптомы:
  • Непонятно откуда взявшиеся переменные.
  • Не относящиеся к задаче фрагменты кода.
  • Очень странное покрытие документацией, много выглядящего важным за её пределами.
  • Архитектура слеплена из предыдущих трёх архитектур.
  • «Да кто её будет смотреть, просто подключайте к проекту!»
  • Устаревшие интерфейсы
Что будет дальше: а дальше либо всё это разбирать, либо разработчики возьмут ветку целиком и унесут дальше, потому что её слишком сложно рефакторить, но она работает. Через три итерации код невозможно будет задокументировать во внятные сроки и в него нельзя будет быстро внести изменения.

В API-driven-подходе + Domain-Driven Development такого в принципе не должно случаться, а мы хотим думать, что придерживаемся этих идей. Точнее, архитекторы уверены, что придерживаемся, а дальше уже как пойдёт в конкретных командах.

Как и в других случаях ниже, тут возможно отдельное развлечение — прогулка по минному полю, когда вы не знаете, какой фрагмент кода как точно работает. И некоторые, казалось бы, очевидные и прямые решения приводят к интересным для отладки последствиям. Если к вам подходит более опытный разработчик и говорит: «Ты так не делай, сейчас расскажу, как обойти, эта функция вообще опасная, с ней лишний раз не связывайся» — это как раз оно.

Blob

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

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

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

Думаю, что несильно ошибусь, если скажу, что у каждого банка из топ-10 в России есть система, которой больше 15 лет и в которой есть штуковина размером с фреймворк для бизнес-логики, вокруг которой аккуратно расставлены обработчики и разные внешние интерфейсы. Сама же система не поддаётся членению и не может быть переписана в ближайшие годы.

Непрерывное устаревание

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

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

Заканчивается это либо тем, что система умирает от старости и больше становится не нужна, либо рефакторингом, больше похожим на переписывание с нуля.

Функциональная декомпозиция

Это когда разделение на классы делается не по функциональному уровню, а по частям бизнес-процесса, что в итоге даёт очень дегенеративную архитектуру.

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

Процедурная версия расчёта кредита для клиента.

«Плохой» вариант:
Объектно-ориентированная версия расчёта кредита для клиента.

«Хороший» вариант:
Распутывание такого кода, когда он на всём проекте, — отличное приключение. Обычно такая практика случается, когда на проекте — несколько джунов, и нет никого, кто выступил бы архитектором. Или когда разработчики быстро клепают MVP, понимая, что завтра этот код полетит в корзину, а в итоге он используется годами. Иногда я вижу такое в коде аутсорс-компаний, которые берегут время разработчиков.

Итог в том, что система строится так, что провести рефакторинг практически нельзя. Приходится с этим жить и постепенно делать из этого блоб.

Лодочный якорь

Это когда вам досталось много кода, и весь этот код бесполезен в рамках задачи. Я такое видел после слияний-поглощений компаний, когда из двух систем компаний в итоге делают одну. Или когда вам достаётся код без базы данных, часть логики реализована в базе — и удачного вам сбора требований и попыток доработки.

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

Золотой молоток

Эту практику я вижу практически постоянно. Когда у вас есть молоток, всё вокруг становится похожим на гвозди.

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

Я встретился с золотым молотком в момент, когда Кафка стала хорошо протестированной шиной. А у нас в проекте везде применялась интеграция через IBM MQ. Пришлось потратить много времени на защиту нового решения интеграции и убедить коллег в её преимуществах для нашей очередной интеграционной задачи.

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

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

Спагетти-код

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

Пример спагетти-кода из Linux SCSI driver (с небольшими изменениями) начала нулевых:
Что надо знать: хотя бы принципы функционального программирования. А в идеале — ООП и архитектурные принципы типа DDD.

Это тот самый код, который имеет очень яркую региональную принадлежность, и если вы аутсорсите, например, в Индию, то встречаться с ним придётся довольно часто.

Копипаста

Сначала были только книги и журналы. Код из них, конечно, можно было перепечатать, но к катастрофам это не приводило. Появился Google (как первый удобный поисковик), и разработчики стали много гуглить. Непонятные куски кода стали появляться в проектах. «Эта штука делает то-то, и не трогайте внутри» стало нормой. Но такими кусками закрывались какие-то дыры, где компетенций не хватало. Появился StackOverflow, и код часто стал состоять из таких скопированных кусков, а разработчики только криво сшивали их между собой. И, наконец, вершиной копипасты стал ChatGPT4 (и аналоги), который умеет сшивать разные фрагменты кода между собой лучше бездумного копирования.

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

И отдельно — про грибы

Это немного не про разработку, но последствия ощущаются и в коде. Есть такая история — «Mushroom management», когда вместо задачи даются требования. Причём частями. По-русски это называется «Сиди в темноте и ешь навоз», что очень точно описывает основные принципы выращивания грибов. Так вот, если так поступать с разработчиками, то вас ждёт море интересного на приёмочных тестах и при интеграциях. Особенно при интеграциях.

Пока всё

В эту статью не попали, например, такие антипаттерны, как Poltergeists, потому что слабо тянет на антипаттерн, Тупик (Dead End), Кладж ввода (Input Kludge) или Слепая вера (Blind faith), потому что он должен автоматически решаться тестами, и другие, которые не относятся напрямую к разработке (или мало распространены с точки зрения автора), но при желании читателей рассмотрим и остальные.

Такие плохие практики называются «антипаттерны» в книге «Банды четырёх» «Шаблоны проектирования», где как раз описываются практики хорошего программирования. Хорошие практики назвали «паттернами», а противоположные — «антипаттернами». Про антипаттерны можно посмотреть ещё вот здесь:
Ну а я пошёл дальше собирать хорошие практики, чтобы однажды выложить сборку с ними.

Автор: Александр Булгаков  

Главный инженер разработки в Отделе системного анализа и проектирования
0%

Банк ГПБ (АО) использует файлы cookie. Подробная информация –
в правилах по обработке персональных данных. Вы можете запретить сохранение cookie в настройках своего браузера.