Архитектура костыля

В учебниках и разговорах постоянно про гибкую архитектуру. Гибкость то, гибкость сё. И толком не формализовали, особенно если взглянуть уровнем выше: какую объясняющую формулу (и понятную всем) дать, чтобы она работала для любого софта? Вот чтобы для разлапистых бекендов, для фронтового фреймворка, для мобильного приложения, для десктопной игрушки и т.д.
Для себя я всё же вывел такое и применяю. Правда, потом бывает сложно коллегам пояснить, почему код выглядит так, а не эдак, ну да всяко лучше иметь работающий код, а не довольных коллег при неработающем коде.
Внимайте:

Архитектура является тем более гибкой, чем больше в ней мест, в которые вы можете воткнуть костыль без переписывания остального кода. Костыль — ветвление, хардкод, жесть категории «if user == petrov { kill all }».

И никак иначе. Остальное не работает.


Фигня в том, что реальный мир, с которым соприкасается ваш софт, с его реальными людьми и прочими занятными явлениями является миром костылей. Мы вынуждены компенсировать чужие ошибки, например. Вынуждены учитывать, что в окружении сервиса любой элемент может упасть в любой момент времени. Мы должны учитывать пьяного дядю Васю на буровом тракторе в Кукуевке, испокон веков стоящей на трансгалактическом кабеле связи. Бухгалтеров. Детей с их разрушительными лапами. Распинаться так могу ещё долго.
И вот всё это хоть как надёжно друг с другом интегрируется только потому, что в каждом сервисе есть вёдра if’ов. Даже если вы по наивной прозрачности мозга выпустили в свет хрустальный сервис, уже через год в нём будет вот это вот всё. Окажется, что в стороннем API ошибка (или ошибки, если считать таковыми разработчиков API). Или к вам зайдёт очень грустная помощница очень главного директора с просьбой больше не делать с её директором то, что мы задумали делать со всеми. Или вам очень-очень надо оптимизироваться, ужимаясь в худенькую квоту серверов. Или клиент пишет «отправлять вам запросы могу ТОЛЬКО ТАК, всего вам хорошего, держитесь». Или бага в базе данных (тикет болтается с 2013 года), а вы это обнаружили за минуту до выкатки. Или от вас очень захотели работающего рендеринга сайта на IE под Mac OS образца 2007 года.
Так нафига страдать нервами по факту? Заранее, всё можно предусмотреть заранее. Не упарываться по фабрикам, возвращающим фабрики (привет джавистам). Не унифицировать всё до нечитаемой одной строчки. Не стремиться избавлять код от лишнего (что нередко не лишнее, но те части нужной реальности, что не попадают в вашу чистую модель). Не городить тонну абстракций. Пара лет эксплуатации и на активно живущем сервисе вас проклянут, пытаясь не убиться в попытках сберечь красоту (забьют болт и начнут запросто костылить, что будет гораздо уродливее на фоне ваших абстракций).
Архитектура (и код) должна отражать реальный мир. Реальную задачу и её прогнозируемые вариации. API отдаёт пять видов ошибок (не смогли разработчики в унификацию, потому в одном случае код есть, в другом нет, в третьем список ID хардкодом в тексте сообщения, в четвёртом списка нет аще, в пятом код ошибки в некоторых случаях является кодом удачной операции)? Делайте пять классов на каждый вид. Не, блин, унифицируйте, блин. Завтра они добавят шестую ошибку. Послезавтра заказчик попросит в третьем варианте ошибки дополнять своими данными. И всё, хана вашим красотам.

Чем чаще пробую этот подход на проектах, тем больше нравится. Да, при первом и втором взглядах возникает ощущение избыточности кода. Наивности. Излишней прямоты. Ахаха, вместо фабрики абстрактных ящиков копипаста десяти ящиков, отличающихся… ну… тут породой дерева, тут гвоздиком, тут наклеечкой, а здесь немножко круглый. А могла быть фабрика! Могла бы. И, возможно, будет. Только через год-другой эксплуатации в бою. А то и три. А лучше десять. И то при достаточном экономическом обосновании. Ведь, что смешно, нередко эти десять ящиков ещё и работают лучше фабрик.
Слава костылю! Костылику слава!

Архитектура костыля: 3 комментария

  1. Классный пост! В точку! Архитектура — это обобщение. А для обобщения нужна масса вариантов, случаев. И сначала их нужно накопить. А чтобы накопить эти случаи без ущерба для мозга, чтобы не проклясть всё и вся по ходу накопления случаев и понадобятся все эти if/then/else бесконечные.

    Так же делаю. Разве что имеет смысл для некоторых случаев скрывать кучу if\then\else за фасадами, чтобы было проще использовать. Но без или-или. Можно напрямую задействовать кучу if\then\else с кучей настроек, а можно через какой-то фасадный метод, который собирает часть этой кучи в одно-целое. Исключительно для простоты использования вовне.

  2. Хорошая статья! Но, кажется, здесь описана одна крайность — попытка обобщить то, что не ясно как будет использоваться. По опыту нередко вижу такое, особенно у джавистов. Тут надо помнить общую установку для такого рода обобщений и оптимизаций — не пытайтесь предсказать будущее. Так часто случается, если городить классы/API зная лишь четверть требований, а остальное додумывать. На деле же оставшиеся 3/4 совсем не похожи на первую часть и заставляют вставлять кучу if-ов оставляя фабрики и т.п. еще одним нагромождением «сбоку». Отсюда, кстати, и Agile с его установками на ранний выпуск в прод.

    Не так давно пытался для себя сформулировать определение костылей. Таких, которые больше всего раздражают. Получилось что-то вроде «костыль — использование компонента не по назначению». Это и прямое использование компонента там, где ему не место, и внесение изменений не туда. Последним страдает много программистов — велик соблазн внести if-чик в любимый участок кода, который ты хорошо знаешь. Возможно, ему там не место, возможно такие if-чики уже расставлены в другом, более правильном месте, и расставлены лучше (полиморфизм, например). Но ты все-равно вставляешь их сюда потому что «так быстрее и эту часть я знаю».

    Плохой костыль (да, я за то что есть вынужденные — и они норм) — это костыль который заставляет тебя что-то *запоминать*. Например, явно запоминать что в связке фронтенд-бэкенд почему-то одна из частей форматирования находится (та-дам!) в бэкенде. Находись он во фронте, это бы никого не удивило, это ожидаемо. Даже если бы он был реализован там не очень (нормальным костылем). А так — никто не может объяснить почему в бэкенда и все вынуждены просто помнить.

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

  3. Для приложений костыли – нормально. Даже больше, для них не обязательно ни ООП, ни ФП, и если код будет недостаточно обобщён и нужно будет обработать новый кейс, который не прикладывается к существующим классам – не проблема, заводим тикет, делаем узкоспециализированное техническое решение (==костыль). И солид не столь критичен, например преследовать open-close принцип нет большого смысла, потому что есть умная IDE, автоматическая сборка, тесты и разъярённые пользователи (которые напишут если что не так).

    Но правильная и по возможности наиболее общая архитектура критична для библиотек и тем более для языков программирования. Чем библиотека охватывает большее количество случаев, тем лучше.
    Сортировка работает с коллекциями объектов любого типа, если они упорядочиваемы.
    Поиск работает с коллекциями объектов любого типа, если они сравнимы.
    Сериализация/десериализация в JSON работает для объектов любого типа, если они состоят из подобных объектов.
    Ретраи можно делать для любых сетевых соединений, и причём обобщённым образом.
    Мысль понятна.

Добавить комментарий