Эта статья подготовлена на основе вебинара от одного из авторов курса Архитектура приложений Александра Вагнера. Александр в индустрии уже более восьми лет. В основном занимается проектированием и разработкой бэкенда, но в жизни был и период фронтенда: Александр разрабатывал большое веб-приложение для систем документооборота. В данный момент строит и развивает платформу в качестве архитектора.
Что такое антипаттерны
Обсудим сперва само понятие «антипаттерн». Википедия убеждает нас, что антипаттерн — это неэффективный, рискованный или непродуктивный подход к решению часто встречающихся проблем. Хорошее определение в целом, но чувствуется в нем некая стигматизация. Создается впечатление, что присутствие антипаттерна является следствием чьего-то злого умысла или «недалекости» специалиста, который это допустил. Но давайте условимся: все решения, которые когда-либо были приняты кем-то в вашей системе или проекте, исходили исключительно из положительных намерений. То есть никто не собирался специально вредить. Более того, все решения были приняты в определенном контексте, который сейчас мог просто-напросто измениться. Допустим, были неблагоприятные времена с недостатком компетенций, времени или бюджета.
Поэтому возьмем на себя смелость и приведем другое определение. Антипаттерн — это распространенный подход к решению часто встречающихся проблем, который может нанести больше вреда, чем пользы.
Зачем знать антипаттерны
Чтобы это уяснить, приведем некоторые афоризмы, хорошо описывающие тему. Начнем с английской пословицы: «Лучше дьявол, которого знаешь, чем тот, которого не знаешь». Зная антипаттерны, вы понимаете, чего от них ждать. Можете заранее просчитать риски их использования в отличие от неизвестных вам антипаттернов.
Идем дальше. Как говорил старик Фрейд: «Признание проблемы — это половина успеха в ее разрешении». Диагностировав где-либо антипаттерн, вы уже немало сделали для его устранения.
Наконец, стоит упомянуть: в мире нет ничего совершенно ошибочного — даже сломанные часы дважды в сутки показывают точное время. Это означает, что иногда антипаттерн является единственным доступным инструментом для решения какой-либо проблемы.
Что делать с антипаттернами
Первое — стараться их избегать (привет, кэп). Кроме того, необходимо грамотно оценивать риски от наличия или использования антипаттерна. Будем с вами честны: оценка задач для разработчиков — и так достаточно сложная дисциплина. Если учитывать еще и все риски, это на порядок увеличивает сложность. Далее следует найти причину использования антипаттерна, для чего вспомнить несколько законов архитектуры: а) любое решение является компромиссом; б) вопрос «почему?» важнее вопроса «как?». Последнее — антипаттерны нужно приручить и контролировать.
Тут как в теории разбитых окон: наличие антипаттернов в проекте легитимизирует и поощряет их использование. Там, где есть одно применение, можно допустить и второе. Там, где второе, найдется место и третьему. А три штуки — это уже не 3 антипаттерна, а «особый путь в проектировании или разработке и фишка нашей системы».
Поэтому приручайте и контролируйте антипаттерны. Изолируйте их от остальной части системы, помечайте их, боритесь с ними! Иначе проект рискует стать сборником плохих подходов, или их наличие будет демотивировать вас и ваших коллег. А оно вам надо?
Инверсия абстракции
Обычно это выглядит как попытка реализации низкоуровневых конструкций поверх высокоуровневых. Для примера рассмотрим вот такую вот цепочку вызовов.
Представьте интерфейс, на котором нужно показать какой-нибудь счетчик. Для этого вызовем метод getCount нашей бизнес-логики, который через слой доступа к базе данных вычитывает весь список объектов для счетчика, подсчитывает их количество и возвращает его на пользовательский интерфейс.
Скорее всего, вам сразу показалось странным и неэффективным решением вычитывать весь список объектов для подсчета количества на стороне бизнес-логики. Вам не показалось: это действительно место, где происходит инверсия абстракции. И это пример антипаттерна. Здесь низкоуровневая конструкция количества записей строится поверх высокоуровневой конструкции.
Более приемлемой схемой тут будет сделать метод, который эффективно считает количество записей, на слое доступа к базе данных. Бизнес-логика будет вызывать его и возвращать это значение на UI. Это достаточно простой пример, и здесь главное — уловить суть антипаттерна.
Почему с таким можно столкнуться? Возможной причиной могут стать изменившиеся требования после реализации. Например, в предыдущем примере сама задача выводить на UI какой-то счетчик может появиться уже после реализации всех нижестоящих слоев. Также это может быть использование сторонних компонентов, которые «почти подходят». Обычно это «почти» и приносит в проект антипаттерн, который делает больно. Чаще всего он несет в себе дополнительные расходы, усилия на костыли для реализации нужного функционала и вычислительные ресурсы. Это вы могли наблюдать в предыдущем примере.
Путей решения тут два. Если есть такая возможность, пробросьте требуемые низкоуровневые функции в интерфейс высокоуровневого. Второй вариант — реализовать нужную конструкцию в обход высокоуровневого компонента. Это может быть сложно, и делать надо аккуратно. Вам в этом помогут паттерны проектирования, особенно структурные паттерны.
Следующий антипаттерн называется
Большой ком грязи
Он выглядит, как бесструктурный набор компонентов и связей между ними. Это достаточно частая история.
Возможных причин появления такого антипаттерна хватает.
Причина №1. Недостаток компетенций в проектировании. Хорошая новость в том, что с опытом этот навык развивается (вместе с абстрактным и системным мышлением). Однако путь будет тернист и сложен. Вы наступите на все возможные грабли, но взамен приобретете набор знаний и навыков, как делать не надо, который станет прочным фундаментом для построения стройных, красивых и чистых систем.
Причина №2. Нехватка времени или бюджета, которые зачастую вынуждают проводить связи между компонентами, нарушая все правила чистой архитектуры, все мыслимые и немыслимые архитектурные границы в угоду экономии.
Причина №3. Изменение требований, особенно когда они приходят уже после реализации. Еще острее это чувствуется в системе с негибкой архитектурой.
Причина №4. Текучка кадров. Вместе с человеком обычно уходит часть знаний и контекста, которые вносили хоть какую-то структуру и порядок в вашу систему.
Причина №5. Рост системы и технического долга. Навык управления техническим долгом является очень ценным. И это дорого. Поэтому он есть далеко не в каждой команде.
Причина №6. Унаследованная сложность: если предметная область является большим комом грязи, то ваша архитектура примет такие же очертания. Насколько понятна и прозрачна предметная область, настолько же будет чистая и стройная архитектура.
Что делать с большим комом грязи? Часто встречается подход «а давайте распилим все на микросервисы». После этого ожидают, что все решится как по волшебству. Но правда в том, что монолиты, микросервисы, микрофронтенды и прочее — это в меньшей степени про структуру, а в большей степени про размещение и связанные с этим фишки.
Плохую структуру можно с успехом выразить как в монолите, так и в микросервисах. Но унывать не стоит, потому что это правило действует в обе стороны: если вы сможете сделать монолит с красивой и хорошей структурой, то успешно перенести ее на микросерисы тоже, скорее всего, получится.
Предположим, что микросервисов нет. Что нам поможет? В данном случае действительность хорошо отражает одна японская мудрость: «Делай чище, чем было до тебя». Так как дело предстоит иметь с чем-то не очень чистым, совет номер один — засучить рукава, естественно. Потом нужно постараться выявить хоть какую-нибудь структуру. Ведь если ком грязи работает, значит не все потеряно, и выявление структуры точно не будет бесполезным.
Далее стоит определить части, которые можно рассортировать. Допустим, у вас разные есть компоненты, они между собой связаны. Отсортируйте их хотя бы по зависимости друг от друга. Потом можете распределить их по значимости (если уже понимаете, какой компонент более важен, какой — менее). Можно по чистоте: какие части написаны совсем в дремучее время на старых подходах, а какие созданы недавно, и можно найти хоть кого-то с внятным объяснением.
После этого составьте план и — вперед, рефакторить! Конечно, если согласуют ресурсы. Но это уже совсем другая история.
Vendor lock-in
Следующий антипаттерн, который мы рассмотрим, — это vendor lock-in. Обычно он выглядит, как архитектура, основанная на определенных продуктах. Под продуктом тут имеется в виду определенная база данных, сервис или технология. Vendor-ом называется поставщик таких продуктов. Особенно больно от этого антипаттерна становится, когда вы платите vendor-у немалые деньги.
Здесь есть ремарка для vendor-ов: если вы таковым являетесь, то у вас есть свой антипаттерн — «обратный vendor lock-in», когда уже поставщик начинает зависеть от определенного заказчика. Но в данной статье мы это опустим и вернемся к обычному vendor lock-in.
Наличие подобного антипаттерна может породить в системе следующие проблемы. Во-первых, зависимость от обновлений. Обычно во время них происходит все самое интересное: всевозможные проблемы с совместимостью, доступностью и прочим. Во-вторых, доставка обещанных функций с задержкой. Учтите это при оформлении отношений с vendor-ом и оплате его услуг, а лучше — зафиксируйте все документально, если это возможно. В-третьих, использование специализированного продукта требует специализированных компетенций, которые трудно найти, легко потерять и невозможно оплатить.
Возможные пути решения. Первое: старайтесь использовать реализации открытых и распространенных стандартов. Тогда вы сможете хотя бы выбирать между разными vendor-ами. И, возможно, подстраховаться опенсорсными решениями: их легче находить и поддерживать. Второе: изучите вашу предметную область и постарайтесь изолировать vendor-а за слоем абстракций. Это уменьшит распространение зависимости от vendor-а по вашему проекту. Постарайтесь отдать под vendor lock-in, если он у вас все-таки есть, конкретную локальную часть в системе, чтобы была возможность ее заменить в случае чего.
Важное — определяйтесь с vendor-ом своевременно! Как говорит Роберт Мартин (он же дядя Боб) в своей книге про чистую архитектуру: хорошая архитектура позволяет откладывать принятие ключевых решений. Гибкая архитектура позволяет откладывать выбор vendor-а, инструмента, технологии. А главное — дает возможность их легко заменить в будущем. С этим связан тесно следующий антипаттерн, который называется
Cover your Assets
Данный паттерн выглядит, как предоставление набора вариантов вместо конкретного решения или вовсе избегания принятия решения под любым предлогом. Частенько приходится выбирать между несколькими хорошими вариантами или если нам совсем не повезло, то между несколькими плохими. Здесь можно впасть в состояние, которое называется «аналитический паралич». Это когда мы очень сильно боимся совершить ошибку или отказаться от лучшего решения в угоду какого-то быстрого. Кажется, что легче вообще не делать выбор. Увы, не легче. Что с этим делать?
Если решение зависит не от вас, можно просто ждать. Но следует учесть все риски и проблемы, которые на вас свалятся, когда решение будет все-таки принято, но несвоевременно. Если есть возможность реализовать несколько вариантов и выбрать лучший — вперед. Но этот вариант подходит только для очень богатых.
Скорее всего, придется делать то, что рекомендовано большинству: посоветоваться, принимать решение и постоянно получать обратную связь. Советоваться перед принятием решения полезно, потому что иногда ответ или дополнительные мысли могут прийти прямо в момент формулировки вопроса. Если вы не слышали про метод резиновой уточки, то почитайте, классная штука. Получать обратную связь поможет методология гибкой разработки, ваша вовлеченность и заинтересованность в процессе.
Раз уж мы с вами затронули антипаттерны, связанные с принятием решений, давайте рассмотрим еще один.
CV Driven Development
Это выбор технологий и подходов только ради строчки в резюме. Причиной данного антипаттерна является некий порочный круг.
С одной стороны, есть компании и проекты, которым очень нужны опытные работники. Одним из способов заманить их является предложение классного, хайпового и свежего технологического стека.
С другой стороны, есть разработчики, которым интересно заниматься современным стеком. Они видят, что новые технологии востребованы на рынке труда, потому что компании пытаются поймать их именно на этот крючок. Разработчик вынужден разбираться в новых технологиях и как-то их применять у себя, иначе рискует остаться на обочине рынка труда.
Вот это вот стремление и замыкает порочный круг, подталкивая людей тянуть в свои проекты все, что на слуху.
Как бы грустно ни было, этот круг не получится разорвать. Это наша данность. Чтобы чувствовать себя востребованным и классным без ущерба проекту, нужно уделять время развитию. Поэтому стоит посещать конференции, курсы, делать проекты и другие бла-бла-бла… Вы это все и сами прекрасно знаете.
Бывают ситуации, когда на изучение новых технологий не хватает личного времени. Скорее всего, тогда вы или ваши коллеги будут пытаться втянуть технологии на проект, чтобы применять их на рабочем месте. Если проект находится в вашей зоне ответственности, то надо это контролировать. Пробуйте только то, что уместно в вашем проекте и улучшит его. И только в случае, если на это есть ресурсы. Всегда взвешивайте риски и начинайте с малозначимых и изолированных частей системы, в которых можно баловаться и применять новый стек, не влияющий на production.
Подведем итоги. Антипаттерны надо знать и уметь их контролировать. В этом поможет развитие абстрактного мышления и японская мудрость: «Делай чище, чем было до тебя». Принимайте решения своевременно и будьте открыты новому, в конце-то концов.
Если хотите больше погрузиться в архитектуру
20 февраля стартует второй поток курса «Архитектура приложений». Мы создавали его для разработчиков и всех, кто хочет думать как архитектор. Посмотреть программу можно здесь: slurm.club/3jdGzWA
Комментарии (19)
Aceki
16.12.2022 21:29Какую литературу посоветуете, чтобы укрепить навыки проектирования архитектуры?
zubarek Автор
16.12.2022 21:30+3Вот небольшой список от Александра, автора статьи)
https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164
https://www.amazon.com/Software-Architecture-Trade-Off-Distributed-Architectures/dp/1492086894
https://www.amazon.com/Building-Secure-Reliable-Systems-Implementing/dp/1492083127
https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321
Racheengel
16.12.2022 21:45Есть ещё один прикольный антипаттерн, называется Testing Driven Development. Это когда пишется куча тестов, под них заводиться какой нибудь Continuous Integration сервер, сперва этот зоопарк даже пытаются как-то поддерживать. Но со временем требований становится всё больше, архитектура приложения меняется, заказчик поджимает... в итоге на тесты просто забивают и убирают их с сервера. А когда майлстоун сдан, код уже настолько ушёл вперёд от тестов, что поддерживать их более нет возможности и желания.
PrinceKorwin
17.12.2022 12:05+7Хочется поправить. Сам TDD не является антипатерном. А вот его не верное применение - да.
TDD отлично подходит для покрытия конного, не бизнес функционала. Например у вас есть реализация своего DSL заточенного под вашу предметную область. Поверх этого DSL написано куча бизнес-логики.
Если вы будете пытаться в TDD и на DSL и на бизнес-уровне - будет то, что вы описали
Если покрыть с помощью TDD сам DSL, то это отличное решение. Который позволит добиться большой стабильности и отсутствия регрессий при развитии синтаксиса/логики самого DSL.
Vindicar
16.12.2022 22:59А как же старый-добрый божественный объект? Как же спагетти код (он же рак абстракций)?
А "ком грязи", насколько я понимаю, правильно называется "высокое сцепление".
Racheengel
17.12.2022 13:39+2А ещё есть "луковый код", когда слоёв абстракции over 100500.
Vindicar
17.12.2022 15:54А. Значит, я перепутал название. Я имел ввиду как раз ситуацию, когда адаптер поверх прокси поверх фасада поверх декоратора и всё это приправлено интерфейсами.
Racheengel
17.12.2022 19:11Каша из интерфейсов с луком и спагетти, получается тогда. И как правило, оно всё write only, потому что ни понять, ни дебагать такое не реально.
BitLord
19.12.2022 15:31Каша, лук и спагетти — сами по себе вроде вполне себе нормальные продукты. Но вот всё вместе звучит совсем уж невкусно :)
Racheengel
17.12.2022 00:42+1А ещё есть противоположность божественного объекта - каша из интерфейсов (interface soup). Возникает, когда проектировщик недостаточно знаком с предметной областью и с принципами разработки ПО. В итоге всё вокруг - интерфейсы, сплошные касты туда сюда и "за деревьями не видно леса".
Myclass
17.12.2022 15:07Спасибо за статью. Всё, что прочитал - уже знакомо, но лишний раз не помешает.
Не знаю, как такое обозвать, но у нас в фирме при разработки интерфейсов использовали формат JSON. Да, модерный формат. Но используют его в ETL процессах. Вместо CSV. И всё бы ничего, но объем данных передаваемых с помощью JSON 3-5 гигабайта. И иногда не хватает окна времени, вовремя эти данные прочитать и положить в базу данных. Взяли-бы для этого csv - думаю этих проблем не было-бы.
Racheengel
17.12.2022 16:29Обычно такое получается, когда приложение проектируют "20-летние синьйоры", закончив 3-дневные курсы ВеликихАрхитекторов. Всё в рест, всё пихаем в джсон, всё яваскриптом и электронами. В итоге хелловорлд на 20гб.
Myclass
17.12.2022 18:53Точно. И что интересно, никакая аргументация не помогает. Стоят на своём, что все используют json. Я привожу пример - все люди используют ножи, но ведь не для спиливания дерева.
Racheengel
17.12.2022 22:08Ну так они кроме джсона на своих курсах ничего другого и не видели. Им просто неоткуда альтернативы брать. Тут не аргументы нужны, а кодревью и перформанс тесты.
mvv-rus
18.12.2022 00:22+1Наверное, надо все же поблагодарить авторов курсов за то, что они выпускников JSONу научили, а то ведь могли вместо этого научить и SOAPу ;-)
Racheengel
18.12.2022 17:52+1Сейчас переизбыток веб-макак, к сожалению. А нормальных спецов найти всё труднее и труднее.
mvv-rus
Может, статья и неплохая. Но почему она оказалась в разделе "Новости"?
PS Обращаю внимание @moderator
zubarek Автор
Спасибо, что заметили! Перенесла