Рефакторинг. Эпизод IV. Желаемый дизайн кода
Книг и докладов по архитектурным паттернам очень много. Многие из них хорошо известны даже новичкам: о них не рассказывал только ленивый. В этом эпизоде я не буду разгонять очередной хайп на тему «прав ли дядька Боб/Фаулер/etc. или нет». Вместо этого расскажу, как я ищу внутренний покой в этом бушующем «океане страстей»: как не стать заложником одного продвигаемого решения всех проблем, и при этом постоянно не скатываться в аналитический паралич.
Откуда берутся бесконечные споры «как делать хорошо»
Любая форма коммуникации между людьми несовершенна. Как выглядит попытка передать свой опыт:
- Человек сталкивался с каким-то ограниченным набором задач, с какими-то вводными. Решая эти задачи, выработал для них решение, которое в его контексте было признано успешным.
- Он, как смог, «сдампил» свои мысли в книгу/доклад в сжатом виде.
- Другой человек прочитал/посмотрел полученный «дамп», понял его, как смог. Новую информацию натянул, как смог, на свои задачи, свой контекст.
На каждом этапе идёт адовая(sic!) потеря информации. Само по себе чудо, что несмотря на это, мы иногда всё-таки умудряемся идти в одну сторону. В общем, редко кто хочет специально сбивать с толку других людей – люди постоянно ненарочно сбиваются сами.
Поэтому я считаю, что контр продуктивно как оголтело превозносить понравившиеся идеи, так и отмахиваться от того, что вам не близко. Нужно сдерживать себя в желании броситься спорить/поддерживать авторов, нужно вдумчиво понять их точку зрения. В конечном же итоге ваша задача – развивать себя всеми доступными способами. А авторам, (особенно мэтрам индустрии) в общем-то, ехало-болело, что там разгоняет про них очередной хайпожор в интернете.
Много лет назад я зачитывался книгами Эльяху Голдратта. Помимо «Теории ограничений», из них я подчерпнул следующие нетривиальные мысли:
- Не бывает двух разных несовместимых правильных ответов на один вопрос.
- Полезно считать, что при конфликте win/win решение всегда существует.
В обоих случаях ключ к решению проблем лежит в опровержении исходных предпосылок, которые и приводят к конфликту. Нужно сделать шаг назад от конкретных решений и посмотреть на ситуацию с высока. Применяя это к задаче дизайна кода, можно сказать, что на самом деле у нас нет цели найти единый подход к дизайну. На самом деле нам нужно научиться решать конкретные проблемы. Следует накопить как можно больший арсенал инструментов и применять их по необходимости. И помнить, что любое решение есть компромисс и его нужно осознанно разрешать.
Tradeoffs. Tradeoffs everywhere
— Скажите, пожалуйста, куда мне отсюда идти? – спросила Алиса.
— А куда ты хочешь попасть? – ответил Кот.
— Мне все равно… – сказала Алиса.
— Тогда все равно, куда и идти, – заметил Кот.
Льюис Кэрролл – Алиса в стране чудес.
Как вы знаете, рефакторинг – это изменение свойств архитектуры кода без изменения внешнего поведения. Мы можем сколь угодно классно применять техники рефакторинга, но если мы понятия не имеем, в какую сторону стоит «гнуть дизайн», то этот процесс будет хаотичным. Всю дорогу нам рассказывали, что код должен быть и простым, и гибким, и производительным, и …много каким ещё. Реально ли этого достичь?
«Осознанность проектирования». Евгений Кривошеев
И тут я подхожу наконец-то к первой полезняшке, которую рекомендую всем посмотреть. Несмотря на некоторую абстрактность в названии доклада, автор на простых примерах показывает суть возникающих противоречий, когда нам хочется усидеть на всех хороших стульях. В конце он даёт алгоритм принятия решений «что делать, когда не понятно».
Дилогия Нила Форда и Марка Ричардса
А вот эти две книги напихают вам столько вариантов решения типовых задач, сколько вы сможете унести. Здесь всё: от вопросов уровня отдельных кусков кода до микросервисов. Книга учит, как сравнивать разные подходы и выбирать их под задачу. Мой подробный обзор можно почитать тут.
Анатомия сложности
Ещё Фред Брукс в эссе «No Silver Bullet — Essence and Accident in Software Engineering» от 1986 года писал:
«Сложность программного обеспечения является фундаментальным свойством, а не побочным. Поэтому описания программной сущности, абстрагирующиеся от её сложности, зачастую абстрагируются от её сути. Возможно, мы сражались в битве со сложностью — и проиграли».
Программы пишет человек, и он крайне ограничен, чтобы держать в уме большое количество связей. Поэтому ещё с того времени многие умы индустрии пытались описать инструменты, как держать сложность под контролем.
«Phylosophy of Software Design». John Ousterhout
Если Роберта Мартина в «Clean Architecture» больше заботила гибкость решений, то Джон Аустерхаут в своей книге сконцентрировался на борьбе со сложностью кода, которую мы создаём себе сами из-за неудачных решений в дизайне.
Автор рассказывает, почему какой-то код легко читать и менять, а какой-то для осознания требует нечеловеческих усилий. Из новаторств тут введение понятий deep и shallow модулей, и почему сокрытие информации – наше всё.
Но будьте готовы к тому, что такой понятной схемы дизайна, как у Роберта Мартина, уже не будет. Вы рискуете закончить её читать с ещё большим числом вопросов, чем с которым начнёте 😄
«Balancing Coupling in Software Design: Universal Design Principles for Architecting Modular Software Systems». Vlad Khononov
«Большинство книг по дизайну отводят на coupling & cohesion пару параграфов. А тут целая книга про модульный дизайн».
Vaughn Vernon, автор «Domain-Driven Design»
На этой книге я остановлюсь более подробно, хотя она достойна отдельного полноценного обзора.
Эта книга замахивается на ещё более фундментальные открытия в области модульного дизайна и управления сложностью.
В ней говорится о том, что при разработке больших программ мы вынуждены разбивать её на модули, т.к. человек просто не может оперировать большим числом вещей одновременно. Вопрос: как сделать это разбиение. Модули обязаны всё-таки интегрироваться, иначе они не формируют систему, которая нам в итоге нужна для выполнения глобальной цели приложения. Сделав слишком слабые связи, мы теряем контроль, и система становится слишком хрупкой, слишком сильные – теряем гибкость для изменения и расширения.
Под coupling Влад понимает взаимосвязь между модулями. Он не оперирует термином cohesion, как обычным антиподом coupling. Автор считает, что сoupling – это то, что формирует систему. Она наш друг. Убивает проекты не она, убивает сложность. Сложность – это объем мозговых усилий, которое нужно приложить, чтобы понять систему. Когда важные решения непрозрачны и хаотично разбросаны. Когда вроде компоненты есть, но понять, что в итоге происходит, очень сложно. Вы не понимаете, что надо поменять, и на что изменения повлияют.
Автор сравнивает существовавшие ранее критерии оценки coupling-a изStructured Desing (60-ые годы) и Connascence (90-ые). И в итоге проходит к новой модели оценки, которая дополнительно учитывает современные реалии микросервисов и наработки Domain Driven Design. Эта модель буквально показывает, как быстро прикинуть насколько связь компонентов адекватна и не создаст проблем в будущем.
В книге много показательных примеров, развивающих сюжет книги Форда про разработку ПО. Прочитав эту книгу, в том числе становится понятее, почему Роберт Мартин и Фаулер часто предлагали добвлять избыточность в дизайне, за которую их часто хэйтят.
«Как же всё это осознать?»
Тут у меня есть лайфхаки, которыми я активно пользуюсь.
Конспекты
Делайте конспекты или хотя бы заметки прочитанных книг. Это помогает делать передышки, чтобы лучше осознать и запомнить прочитанное. Позже можно вернуться к тонким моментам из книги без необходимости читать её заново.
Нейросети
Пока они ещё учатся писать код без банальных ошибок, с ними уже вполне можно «затереть про архитектуру».
Как я подсветил ещё в начале статьи, иногда бывает сложно понять мысли другого человека. Особенно если с ним нельзя поговорить. Читая Фаулера или Роберта Мартина, у меня возникают вопросы по тонким моментам, с которыми внезапно хорошо справляется, например, DeepSeek. Я ему подкидывал нетривиальные задачи дизайна навигационного кода в веб приложении, отделения UI и бизнес логики, и он достойно мне отвечал. Он стал для меня этаким старшим коллегой, который не только прочитал всю доступную литературу, но и что-то более глубоко осознал. Он помогает мне не сбиваться с пути и правильно интерпретировать известные подходы к дизайну. Такого общения очень не хватает в работе.
Практика
Поверьте, это самое важное. Никакие абстрактные идеи не усваиваются без пропускания через решение реальных задач. В следующем эпизоде я расскрою эту тему более подробно. Stay tuned!