Изменение скорости разработки и интеграции программного кода как один из способов решения проблемы эффективного управления программными проектами

А.В. Юрченко
ЗАО «ЕС-лизинг»,
г. Москва


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

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

- Нестабильность в потоке кода: самые последние версии программного кода не компилируются (не проходят сборку), или не проходят тестирование.

- Слишком затянутый процесс создания версии. На пути создания версии существует довольно много различных барьеров в виде тестирования, анализа кода и т.п., либо используется так называемый маркер интеграции (Integration Token), который обычно означает, что любая версия будет создаваться очень долго.

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

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

Обычно происходит следующее. После разочарований в долгой последовательности неудачных сборок (build), которые происходили из-за длительных тестов и операций контроля при создании версий, команда переориентирует процессы разработки и контроля разработки в направлении более «тяжелых», применяя практику – чем медленнее, тем безопасней и надежней. Проблема заключается в том, что более медленный процесс все еще допускает остановки в работе, что содействует увеличению интервалов интеграции. Эти увеличенные интервалы интеграции сбивают команду разработчиков с ритма ([1]), а так же увеличивают риск возникновения проблем во время интеграции.

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

Качество потока кода увеличивается на некоторое время, и разработчикам нужно тратить меньше времени на работу с неисправным кодом (его исправление), а больше на свое основное занятие – написание кода. Следовательно, увеличивается скорость и объем разработки, разработчики начинают добавлять новые свойства в программный код. В потоке кода становится больше изменений, и эти изменения хорошего качества. Но дальше начинают происходить странные вещи. Разработчик №1 вносит некоторые изменения в код, запускает набор тестов, выполнение которых занимает теперь значительное время (время прохождения тестов может занимать час, два, вплоть до целого рабочего дня). Он делает слишком много изменений в коде из-за увеличенного промежутка времени прохождения тестов, чтобы покрыть тестами сразу как можно больше своих изменений. Поскольку, если запускать набор тестов после внесения каждого изменения, то общее время разработки значительно увеличится, поэтому разработчик делает сразу множество изменений, чтобы протестировать их все за один раз. Независимо от разработчика №1, и, не предупреждая его, разработчик №2 так же начинает вносить некоторые изменения в программный код, и далее тестировать их. У разработчика №1 и разработчика №2 тесты проходят успешно в их индивидуальных рабочих областях, и они создают новые версии в проекте, то есть вносят свои изменения в общую рабочую область, с которой работает вся команда. Разработчик №3 обновляет свою индивидуальную рабочую область из потока кода (общая рабочая область) и обнаруживает, что код не компилируется, а тесты не проходят успешно. Произошло то, что разработчик №1 и разработчик №2 внесли несовместимые изменения в проект. Из-за того, что тестирование занимает слишком много времени, они внесли за один раз слишком много изменений в код, увеличив вероятность того, что изменения начнут конфликтовать.

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

- Стать владельцем маркера интеграции.

- Обновить свою индивидуальную рабочую область из потока кода, разрешив все возможные конфликты.

- Запустить набор тестов.

- Когда набор тестов пройден успешно, создать новую версию проекта.

- Передать маркер интеграции следующему человеку в очереди.

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

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

Возникает вопрос, как решить данную проблему? Ответ – способствовать быстрому движению вперед, и исключать ненужное. Некоторые паттерны конфигурационного управления ПО способствуют решению проблемы.

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

Тяжеловесные среды конфигурационного управления, которые заставляют разработчиков ожидать значительные периоды времени таких вещей, как разрешение на редактирование проекта, получение разрешения на редактирование конкретных файлов в проекте; затянувшиеся циклы сборки и тестирования; ожидание разрешения для использования новой версии проекта и т.д., рассматриваются как убыточные и ущербные для здоровья динамичной среды разработки ([2]).

Чтобы решить основную проблему, необходимо понять, что является целями проекта. Если существует надобность в использовании паттерна Активный поток разработки (ActiveDevelopmentLine) ([2]), это означает, что проект будет представлять собой быстро развивающийся потока кода, который будет в порядке большую часть времени. Поток кода, который никогда не меняется, может быть надежным, но с ним нельзя будет реализовать сколько-нибудь серьезный проект.

Язык паттернов конфигурационного управления ПО предоставляет один из способов разрешения таких проблем. На рисунке показаны соответствующие паттерны и порядок их применения. Создается Активный поток разработки (ActiveDevelopmentLine), при этом разработчики работают в Индивидуальной рабочей области (PrivateWorkspace). Перед тем как вносить свои изменения в поток кода (общую рабочую область), разработчики должны выполнить Индивидуальнуюсборку проекта (PrivateSystemBuild) и запустить т.н. Дымовое тестирование (SmokeTest). Дымовое тестирование должно проходить очень быстро, даже если оно не будет всеобъемлющим ([2]).

На данном этапе может возникнуть вопрос – как это возможно, что менее объемлющее тестирование может привести к некоторой стабильности в проекте? Такой подход будет работать, только если разработчики будут выполнять более комплексное, Модульное тестирование (UnitTest) в процессе разработки, а так же если в проекте будет налажен процесс Интеграционной сборки (IntegrationBuild), в ходе которой будет выполняться всеобъемлющий набор Модульных тестов и Регрессионных тестов (RegressionTest) ([2]).

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

Активный поток разработки (Active Development Line) и вспомогательные паттерны

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

Рассмотрим некоторые проблемы и вопросы, возникающие в динамичных проектах и их решения и ответы.

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

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

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

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

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

Больше чем контроль, в проекте необходима доступность и хороший обзор всех элементов. Паттерн Непрерывная интеграция (ContinuousIntegration) (или на крайний случай – частая интеграция) является превосходной техникой для предоставления обзора состояния всей системы ([3]).

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

Глоссарий

Быстрое (динамичное, подвижное) управление (Agilemanagement) – представляет собой итеративный метод определения требований для ПО и сдачи проектов в гибкой интерактивной манере.

Дымовое тестирование (Smoke Test) – в тестировании ПО представляет собой минимальный набор тестов на явные ошибки кода. Такое тестирование обычно выполняется самим разработчиком. Не проходящую этот тест программу не имеет смысла отдавать на дальнейшее тестирование.

Маркер Интеграции (IntegrationToken) – представляет собой физический объект, который присутствует в программном проекте в единственном экземпляре, и используется для определения того, кто в текущий момент времени имеет полномочия интегрировать свой индивидуальный код в главный поток (сборку) проекта.

Модульное тестирование (UnitTesting) – один из видов тестирования, позволяющий проверять на наличие ошибок отдельные модули исходного кода программы.

Непрерывная интеграция (ContinuousIntegration) – это практика разработки ПО, которая заключается в выполнении частых автоматизированных сборок проекта для скорейшего выявления и решения интеграционных проблем. В обычном проекте, где над разными частями системы разработчики трудятся независимо, стадия интеграции является заключительной. Она может непредсказуемо задержать окончание работ. Переход к непрерывной интеграции позволяет снизить трудоёмкость интеграции и сделать её более предсказуемой за счет наиболее раннего обнаружения и устранения ошибок и противоречий.

Паттерн – это некий проверенный способ решения проблемы, который многократно и успешно применялся ранее для решения похожих проблем.

Поток Кода (Codeline) – множество логических изменений, связанных между собой и выполняемых в течение промежутка времени для файла, группы файлов, либо для всего программного проекта в целом.

Рабочая область (Workspace) – файл или каталог, который позволяет собирать вместе различные файлы с исходным кодом и другие ресурсы проекта, и работать с ними как с единым целым.

Регрессионное тестирование (RegressionTesting) – собирательное название для всех видов тестирования ПО, направленных на обнаружение ошибок в уже протестированных участках исходного кода. Такие ошибки, когда после внесения изменений в программу перестает работать то, что должно было продолжать работать, называют регрессионными ошибками.

Релиз (выпуск) (SoftwareRelease) – поставка (выпуск) программного кода, документации, материалов для сопровождения.

Сборка (SoftwareBuild) – представляет собой процесс преобразования файлов с исходным программным кодом в отдельные программные артефакты, которые могут быть выполнены на компьютере. Обычно представляет собой процесс т.н. компиляции, который преобразует файлы исходного кода в исполнимый код.

Свойство (Feature) – различимые характеристики программных элементов.

Списоклитературы

1. Dikel, D.M., D. Kane, and J.R. Wilson, Software Architecture: Organizational Principles and Patterns. 2001, Upper Saddle River, NJ: Prentice Hall.

2. Appleton, B., S. Berczuk, and R. Cowham, Tasks and Branching Patterns, in Configuration Management Journal. 2004.

3. Berczuk, S., B. Appleton, and S. Konieczka, Continuous Staging: Scaling Continuous Integration to Multiple Component Teams, in CM Crossroads Journal. 2004.

4. Institute of Electrical and Electronics Engineers http://www.ieee. org/index.html 


Назад к списку