Разработка через интеграцию

автор: Александр Якима

В статье мы рассмотрим основные принципы и техники Integration Driven Development (IDD), апеллируя к реальным примерам разработки на Agile проекте.

Под Integration Driven мы будем понимать разработку, в основе которой лежит быстрая разработка мокапов основных модулей, их моментальная интеграция и постепенное наращивание функциональности вплоть до полной реализации системы. При этом предполагается изменение интерфейсов между модулями, содержания модулей, исчезновение или появление новых модулей в системе.
Итак, как это работает? Приведем типичный workflow:


В продолжении статьи мы будем обращаться к примеру IDD-разработки на основе реального проекта - кластерной системы с интенсивной обработкой данных. Мы рассмотрим этап перехода на новую архитектуру как пример эффективного применения IDD.

Up-front design vs. Emerging design.

Не бывает в разработке информационных систем возможности предусмотрения всех существенных рисков. Принимая это во внимание, возникают сомнения, что big up-front дизайн (будучи неотъемлемым атрибутом водопадной модели) является эффективным. Но и на 100% emerging design вряд ли приведет к оптимальному результату. Если сравнивать две этих парадигмы с ежедневным походом на работу, то получается что-то следующее:

Up-front. Встаю утром, выхожу на балкон. Вдали виднеется здание моего офиса. Запоминаю путь к нему и вперед! Все время в течение пути неуверенно осматриваюсь и, сравнивая реальность с перспективой из балкона, задаю себе вопрос - "где я?".
Emerging. Просыпаюсь. Квартира без балкона - он мне не нужен! Выхожу из подъезда, закрываю глаза и начинаю на ощупь двигаться, каждые пять минут переспрашивая прохожих, далеко ли еще до Голосеевской, 7?

Как это ни банально, но на работу мы идем (или едем), зная, куда нам нужно и, самое главное, правильно пользуясь зрением. Мы не видим сквозь стены, но перспективу до следующего поворота эффективно используем.
Итак, используя правильно зрение, мы определяем основные составные части продукта. Эти модули должны нести ответственность за логически целостные действия. Естественно, мы определяем модули вместе с их взаимодействием. На основании поверхностного описания взаимодействий определяем интерфейсы между модулями. Принципиально важно, что определение модулей и интерфейсов - задача команды, а не архитектора. Это ни в коей мере не исключает роль архитектора как такового. Это просто означает, что:
  • У каждого члена команды есть адекватное понимание архитектуры
  • Каждый член команды привносит вклад в разработку дизайна архитектуры
В нашем примере использовались однонедельные итерации. Переход на новую архитектуру был запланирован на период из четырех итераций. План выглядел следующим образом:


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

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

Спонтанное парное программирование

Дальнейшее уточнение интерфейсов происходит по парам и по времени совпадает с началом разработки мокапов. Каждый член команды комитится на определенный модуль или модули. И, собственно, если модули A и B имеют общий интерфейс, то люди, ответственные за эти модули, работают в паре. Это не означает, что имеет место парное программирование. Это на самом деле есть спонтанное парное программирование, целью которого является:
  • Совместное обсуждение интерфейсов
  • Обмен знаниями об архитектуре системы
  • Совместная отладка / решение текущих проблем реализации
Такое спонтанное парное программирование предполагает приблизительно 10%-15% рабочего времени, проведенного в парах. Спонтанным мы его называем поскольку люди работают в парах по мере надобности - спонтанно. Поскольку обычно модуль имеет интерфейсы к более, чем одному модулю, то пары перегруппировываются сами по себе. Это поможет каждому разработчику в команде владеть пониманием всей архитектуры.
Ротация

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

Изменения интерфейсов

Как мы уже отмечали, IDD не есть big up-front design. Поэтому, в IDD мы заведомо предполагаем эволюцию интерфейсов в процессе разработки. Что, тем не менее, не мешает их планированию в рамках разумного и, по-этому, отличает IDD от парадигмы "emerging design". Приведем основные причины и условия изменения интерфейсов:

Новая user story. Это нормально для Agile. Не просто нормально, но и в некоторой мере необходимо. Изменение scop'а поддерживает и команду и код в форме.

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

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

Все эти изменения интерфейсов приводят к качественным изменениям архитектуры или качественным инкрементам, если угодно. Мы называем их срезами (cuts).

Запланированная эволюция

В нашем подходе мы рассматриваем два аспекта плана реализации архитектуры - кратко- и долгосрочный. Краткосрочный - это видение на одну-две итерации вперед. Это - "вид до следующего поворота". Это цель, достижение которой не будет требовать существенного рефакторинга в процессе краткосрочной реализации, так как является вполне обозримой. Долгосрочный - это "весь путь до самого офиса". Долгосрочный план дизайна - это "размытый" план. Он предполагает достижение некоторых high-level целей, но не определяет архитектуру в деталях, в отличие от краткосрочного плана. Важно отметить, что определение дизайна архитектуры (кратко- или долгосрочное) не подразумевает каких либо CASE-артефактов и т.д. На самом деле это все прекрасно осуществимо с помощью свободных рисунков на whiteboard'ах, например.
Долгосрочный план предполагает рефакторинг двух видов:
  • Непредусмотренный. Мы рассмотрели его выше.
  • Запланированный. Мы предполагаем на высоком уровне, как система должна эволюционировать и также предполагаем, что на некоторых этапах будет необходим рефакторинг, цель которого - переход на качественно новый уровень архитектуры.

Рассмотрим примеры кратко- и долгосрочного планирования. В нашем примере краткосрочным планом был план архитектуры на итерации 1 и 2. Итерация 1 - определение модулей и интерфейсов, 2 - наполнение модулей функциональностью с целью реализации части функций системы. Тех функций, которые позволят выявить на раннем этапе большую часть рисков и получить по большему счету функционирующую систему. Текущий (краткосрочный) план предполагал односерверную архитектуру. Долгосрочная архитектура - кластерная система. Таким образом, у нас два вида рефакторинга: непредусмотренный (ошибки в интерфейсах и т.д.) и запланированный (изначально система разрабатывается для одного сервера, соответственно, модули - обычные C++ libraries; в 3-ей и 4-ой итерациях происходит рефакторинг, целью которого является превратить C++ libs в сетевые сервисы с соответствующими политиками конкурентного доступа и т.д. ).
Краткосрочный план создается на начале каждой итерации. И, естественно, построение очередного краткосрочного плана может внести изменения в долгосрочный план.

Преимущества подхода

Одно из наиболее важных преимуществ IDD - скорость разработки. Достигается в силу того, что мы не ждем, пока дизайн архитектуры "появится сам". Насколько єто нам позволяет наше "зрение", мы пытаемся предусмотреть. Опять же, не более, чем это разумно. Быстрая валидация архитектуры через интеграцию (начинающуюся с интеграции мокапов) минимизирует ошибки архитектурного характера.
Другое преимущество - подход сработает в случаях, где и up-front, и emerging дизайн окажутся с большой вероятностью бессильными. Это, прежде всего, системы, где performance очень принципиален и является ключевым требованием к системе.

В нашем случае performance - основной фокус системы; высокоуровневой целью системы есть быстрая обработка документов суммарным объемом 3-5 TB. IDD себя полностью оправдал. Без краткосрочного и долгосрочного планирования дизайна, как это было показано выше, реализовать систему вряд ли удалось бы.
В конце отметим, что IDD прекрасно живет в рамках Scrum или близких к нему средах. Поскольку IDD является только подходом к планированию и реализации архитектуры продукта, то он не накладывает существенных ограничений на техники самого программирования.

No comments:

Post a Comment

 
Powered by Blogger