Доброго времени суток, уважаемые читатели Хабра. Представляю вашему вниманию цикл статей «Мамкин архитектор». Этот цикл статей вдохновлен книгой Эрика Эванса «Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем». В нем я постараюсь отразить личное мнение относительно построения гибких архитектур. Попутно объясню, как это пригодится менеджерам и поможет разработчикам укрепить основные понятия.

image

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

  1. Менеджерам, решающим вопросы от разработчиков вроде «давайте все закопаем и переделаем».
  2. Прикладным разработчикам, которым будет интересно заглянуть за броню инкапсуляции тщательно выстроенной (или хаусе) предметной модели в концептуальном ее виде.
  3. Архитекторам или дизайнерам системы будет интересен мой опыт внедрения, поддержки и удержания предметной модели в концептуальных контурах.

Итак, что же такое творческий подъем?


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

Для начала приведем цитату из книги Мартина Фаулера «Рефакторинг. Улучшение существующего кода»:
«Рефакторинг представляет собой процесс такого изменения программной системы, при котором не меняется внешнее поведение кода, но улучшается его внутренняя структура. Это способ систематического приведения кода в порядок, при котором шансы появления новых ошибок минимальны. В сущности, при проведении рефакторинга кода вы улучшаете его дизайн уже после того, как он написан».

Сложно? Тогда разбиваем рефакторинг на три основных подвида, а именно:

  1. Нано рефакторинг, происходящий постоянно. Самый распространенный вид, который используется не всеми разработчиками. Но есть довольно большой процент людей, способных менять программу на очень высоком уровне. Чаще всего нано рефакторинг направлен на изменение методов/функций, не представляющих большой ценности для предметной модели. Пример: разделение крупного метода на более маленькие функции без побочных эффектов.
  2. Микро рефакторинг, направленный на изменение логических конструкций в рамках объекта системы. Чаще применяется с появлением новых знаний о предметной модели, но мотивация порой глубже, чем просто создание читабельного кода. Общий смысл таков: раздробить процесс движения к углубленной, нагруженной смыслом, модели на стадии. Такой рефакторинг удобен тем, что не «замораживает» процесс разработки. Его основная цель — помочь разработчику понять не только принцип работы конкретного объекта, но и то, почему объект делает то, что делает. Данное понимание заодно пробуждается и у специалистов прикладной области, порождая единый язык в команде.
  3. Углубленный рефакторинг — самый сложный, направленный на радикальные изменения предметной модели. Он мотивируется глубинным пониманием предметной модели. Его направление самое опасное, поскольку оказывается радикальное влияние на весь программный продукт. Примером из реальной жизни может служить обычный автомобиль, мотор которого поменяли на ракетный двигатель. Простой машиной такую технику уже не назовешь, а свои основные функции она уже не выполнит.

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

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

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

Творческому подъему предшествует качественный скачок программного продукта. Перед качественным скачком программного продукта имеет место углубленный рефакторинг и т.д., вплоть до нано рефакторинга.

Пример из реального проекта


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

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

Разработанная нами модель была хороша, но…


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

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

Еще больше нас начали беспокоить погрешности в округлениях расчетов. Это могло определить пользователей в группы, с которыми они никак не связанны. Конечно, эти ошибки являлись несущественными с одной точки зрения. Но с ракурса точности «рекомендательной системы» они вызывали определенные вопросы. В этот момент мы начали подозревать, что эти неточности не просто косметические проблемы, а симптомы фундаментальных проблем с архитектурой.

Скачок


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

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

Трезвый взгляд на вещи


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

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

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

Именно в этот момент состоялась встреча команды разработки с руководителем, которую я никогда не забуду. Наш руководитель был человек умный и уверенный. Он задал нам несколько вопросов. Привожу примерный диалог, в котором Р – это руководитель, К – команда, а Т — лидер команды.

Р.: Сколько будет внедряться новая модель, чтобы вернуться к текущему функционалу?
К.: Примерно шесть недель.
Р.: Можно ли решить проблему без таких глобальных изменений?
К.: Можно, но гарантий нет.
Р.: Возможен ли выпуск следующей версии программы, если не внедрить эти изменения сейчас?
К.: Без внедрения изменений движение вперед сильно замедлится и вносить новые корректировки будет все труднее. Движение проекта прекратится после установки всей базы кода.
Р.: Лично вы уверены, что переделать все – это правильно?
Т.: Мы знаем, что сейчас программа рыхлая и ее работоспособность висит на волоске. Если придется, мы справимся по старинке, ведь и сами устали. Но лучше сказать «да», настаивая на более простом решении, лучше соответствующем цели проекта. Если говорить о перспективах развития проекта, то внедрить новую модель — менее рискованно.

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

Воздаяние


В этот момент мы ощутили настоящий подъем. Мой нервный срыв отложился на неопределенный срок. Реализация стала более осмысленной и полной, не было проблем с рекомендательной системой. Со всем остальным – тоже. Все модули работали в одной архитектуре, отражающей предметную модель. Мы выпустили первую версию и были очень тепло приняты пользователями. Мы плотно стояли на ногах, потому последующие версии уже не были проблемой. Развитие модели сопровождалось развитием единого языка. К третьей версии программы все активно применяли термины «нашего» языка. Маркетологи интенсивно использовали его в общении с потенциальными инвесторами, объясняя возможности программы.

Обратная сторона медали


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

В заключение


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

Готовя почву для скачка, сосредоточьтесь на приобретении знаний о предметной модели и культивировании единого языка. «Нащупайте» понятия предметной области и выражайте их в модели в наиболее явном виде. Улучшайте архитектуру программы, делая ее более гибкой. Дистилизуйте знания о модели, подчеркивайте самое важное и оспаривайте. Улучшайте понимание и наглядность. Эти факторы способствуют качественному скачку. Качественный скачок благотворно влияет на все отрасли программной разработки. Разработчики становятся более уверенными в своей программе, менеджеры здраво оценивают поставленные задачи. Мир, дружба жвачка!

Комментарии (9)


  1. darkAlert
    04.04.2018 14:37
    +2

    Я считаю, что каждый имеет право выражать свою точку зрения, поэтому доставайте свои минусовалки и я начну:

    Я заметил одно существенное отличие, между программистами-инженерами и программистами-математиками (и самоучками и т.п).

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

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

    И самое тут страшное — для вышестоящих менеджеров подходы программистов-не-инженеров выглядят как более простые, понятные и быстрые (а значит и эффективные с точки зрения бизнеса), чем подходы инженеров, которые кажутся переусложненными и не понятными.


    1. ncix
      04.04.2018 18:14

      А как быть с программистами-программистами, которые не математики и не инженеры?


  1. Aslamov
    04.04.2018 15:35

    Спасибо за заметку. А можно показать явно, где описан «опыт внедрения, поддержки и удержания предметной модели в концептуальных контурах»? И заодно пояснить, что такое предметная модель — или это модель предметной области?


    1. morkovkin Автор
      04.04.2018 18:04

      Да несомненно, об этом будет итти речь более предметно в следующих частях!


  1. Timmmm
    04.04.2018 17:13

    Странно что вы не упомянули что опубликовали главу книги Эрика Эванса «Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем». Часть III Углубляющий рефакторинг.


    1. morkovkin Автор
      04.04.2018 18:19

      Да действительно часть я брал именно оттуда. Я не совсем публиковал главу так как большую часть статьи старался выражать свое мнение на тот или иной счет. Но вы правы я добавлю в описание! Спасибо!


  1. stanislavmachel
    04.04.2018 17:59

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


    1. morkovkin Автор
      04.04.2018 18:10

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


  1. questor
    04.04.2018 23:49

    Самая важная мысль в подобных тредах: невозможно сдержать рождение новой модели. Как бы ни хотелось'давайте всю старую кодовую базу покроем тестами и только тогда начнём глубокий рефакторинг'- новая парадигма прорывается чуть раньше.