Цикл статей по основам Software Configuration Management
Пролог
Что такое управление конфигурацией в разработке ПО? Зачем оно нужно? Думаю, немногие способны полностью и внятно ответить на этот вопрос. Большинство обычно вспоминает системы контроля версий, которые сами используют. Кто-то упоминает багтрекинг. Кто-то считает вершиной CM отращивание веток в любимой системе контроля версий. А кто-то вообще уходит в сторону и начинает говорить про ITIL и про то, как он записывает в какую-нибудь базу параметры всего софта, который установлен у него в фирме.
Несколько странно и немного досадно наблюдать за этим. Дело в том, что я проработал в SCM в общем сложности около 5 лет, из них 3 года — интегратором в Motorola, на одном из проектов по разработке софта для сотовых телефонов. По ходу дела прочитал кучу материалов по этой теме и получил большой практический опыт — в том числе по работе с одной из мощнейших систем контроля версий IBM Rational ClearCase (см. linkedin в профиле). В итоге в голове сформировалась некоторая целостная картина того, что же это на самом деле — software configuration management.
А потом увидел статью от камрада altern, в которой он начал рассказывать про СМ. Речь у него пошла несколько в другом ключе — о конкретных инструментах и именовании конфигураций. Поэтому, списавшись с ним, чтобы не пересекаться по тематике наших статей, решил написать об основах того, что называется управлением конфигурацией программных средств.
Сейчас у меня уже написан материал примерно на 50 тысяч знаков — это приблизительно 5-7 среднего размера постов для Хабра. И процесс написания продолжается. Я собираюсь выкладывать написанное с небольшой периодичностью сюда и, по мере исчерпания вопросов и обсуждений, постить новые заметки.
Задача — дать обзор того, чем же вообще является CM, какие задачи он решает и какие техники при этом используются. Речь не будет идти о конкретных системах контроля версий или вообще инструментах — этого добра навалом в сети. Задача — показать универсальные для всех инструментов основы.
Что такое CM и зачем он нужен
Управление конфигурацией
Для начала определимся, что такое configuration, ведь это слово выведено в заголовок. Конфигурация – это совокупность версий рабочих продуктов. Ключевые слова – «версий продуктов».
В любом проекте есть рабочие продукты – это может быть маркетинговая документация, требования к конечному продукту, исходные коды, тесты, вспомогательные инструменты. Что считать рабочим продуктом, зависит от проекта (определение будет дано в следующей заметке). Далее, каждый продукт изменяется во времени (в этом ведь смысл разработки), и эти изменения надо как-то учитывать – кто, когда, что именно внёс и зачем он это сделал. Иными словами, учитывать, как появлялись версии продуктов.
Версия – это состояние рабочего продукта, которое может быть восстановлено в любой момент времени независимо от истории изменения.
Соответственно, управление конфигурацией – это управление наборами рабочих продуктов и их версиями. Этот процесс и есть область действия CM.
В англоязычной литературе используется термин Software Configuration Management, сокращенно SCM. Далее для простоты изложения будет использован термин управление конфигурацией и сокращение CM (читается: «сиэм»).
Схема 1. Элементы, их версии и срезы-конфигурации.
CM является одной из базовых практик любой методологии разработки ПО. Достаточно сказать, что в модели SEI CMM/CMMI (Capability Maturity Model Integration) наличие налаженного процесса управления конфигурацией – необходимое условие для получения организацией сертификата CMM/CMMI Level 2.
Замечу, что Level 2 – это самый минимальный, начальный уровень зрелости, согласно модели CMM. Level 1 получает «автоматом» организация, завершившая успешно хотя бы один проект по разработке. Поэтому и наличие CM – это минимальное требование для сертификации. Кстати, на втором уровне необходимо иметь, в числе прочего, налаженный процесс тестирования и управления требованиями. Это говорит о том, что с точки зрения стандарта CMMI, правильный configuration management так же важен, как грамотное тестирование и управление требованиями.
Так в чем же заключается такая ценность CM?
Направления ответственности CM
Управление конфигурацией работает на всех этапах жизненного цикла проекта. Появился рабочий продукт (например, файл с исходниками) – он попадает в поле деятельности CM’а. Продукт начал изменяться (мы пишем функциональность) – значит CM должен предоставить средства для контроля над изменениями и автоматически провести сам контроль, где это требуется. Потребовалось разбить работу на команду разработчиков, а то и на несколько – проектный CM предоставляет правила и инструменты для работы. Есть, что предложить заказчику – тогда CM определяет правила стабилизации продуктов разработки и их выпуска. Надо откатиться на произвольный релиз – опять в работе CM. Понадобились метрики по изменениям или документированные политики – ну, вы поняли, к кому обратиться.
Итак, в первую очередь, CM отвечает за идентификацию рабочих продуктов, т.е. отвечает за определение того, что же будет в дальнейшем контролироваться. В следующей заметке будет подробнее про это рассказано.
Продукты выделили, дальше команда начинает работу. По ходу работы нужно периодически стабилизировать полученные результаты, подводить некоторую черту под наработками, а также определять тот базис, на основе которого будет идти разработка. Это всё также входит в сферу деятельности CM’а.
Кроме того, CM отвечает за то, что в общем случае называется отслеживанием запросов на изменения. Большинству эта область известна как системы отслеживания ошибок. Ведь никакие изменения не должны проходить спонтанно – каждое из них нужно регистрировать и затем правильным образом назначать и отслеживать – вплоть до попадание в конечный продукт. Вот тут опять остается крайним CM. Изменения в продукты вносим, надо их отслеживать – начинает работать контроль версий. Ничто не будет потеряно – CM на страже.
Средства контроля изменений и обеспечения версионности создают условия для распараллеливания разработки в больших командах. Это достигается благодаря тому, что, описав эти средства, мы даем разработчикам документированные процедуры, позволяющие разделять ответственность и ограничивать области деятельности каждого из разработчиков.
Ну и, как всегда, «нельзя контролировать то, что нельзя измерить» — (с) Де Марко. Метрики – о них тоже будет сказано пару слов. Где измерения – там и формализация. Другими словами, всё, что связано с CM, хорошо бы документировать. Об этом тоже вкратце будет упомянуто.
Итак, каковы задачи управления конфигурацией?
Для начала — достаточно. Следующая часть будет посвящено тому, как же определяются продукты и конфигурации, которыми мы будем управлять. Также коснусь вопроса о компонентной разработке, продуктовых линейках и их связи с СМ.
Что такое конфигурация программного обеспечения
Что именно стоит настраивать.
Таким образом программа привязывается к конкретному JDBC драйверу. Использовать другой драйвер, например заменить мост на RMI-прокси или, в случае Oracle, OCI на Thin без перекомпиляции уже нельзя.
Способы хранения настроек.
Разумное же умолчание для параметров часто просто невозможно представить. Например, что поставить в качестве имени SMTP-сервера? В случае Unix-систем можно попробовать поставить localhost, но для Windows-мира это редко кому подойдёт.
Рассмотрим наиболее распространённые варианты:
Ini-файлы.
В Java нет стандартного класса для чтения ini-файлов, но это не проблема. Т.к. формат очень прост, его легко сделать самому:
Файлы Properties.
XML-файлы.
Этот формат подходит для многих целей, в том числе и для хранения настроек. XML-формат ориентирован на древовидные структуры, что довольно естественым образом отображается на объекты. Пример типичного файла:
Для XML также написано несколько библиотек для универсального сохранения (сериализации) объектов в файлах XML. Такие библиотеки позволяют отделить алгоритм сохранения от самого объекта, а это, как уже упоминалось, имеет много плюсов.
Сериализация.
Базы данных.
Скрипты.
Пример программы с конфигурацией в XML.
В качестве примера можете посмотреть простенькую программы, использующей XML-файл в качестве конфигурационного. Сохраняемые параметры можно редактировать как из программы, так при помощи текстового редактора.
Пример содержимого конфигурационного файла:
В качестве XML-парсера используется Sun-овский парсер в режиме DOM. На таком простом примере не видно особых преимуществ формата XML над теми же файлами properties. Они становятся заметны только в достаточно сложных программах, где становится необходимо хранить списки однотипных параметров или же содержимое объектов с уровнем вложенности два или более.
Software Configuration Management // Конфигурации и baselines
Итак, по горячим следам продолжаю публиковать материалы, касающиеся основ управления конфигурацией программных средств. Прочитайте предыдущую заметку, если вдруг пропустили.
Ниже речь пойдет о следующих вещах:
— Рабочие продукты и конфигурации;
— Компонентная разработка;
— Продуктовые линейки;
— Стабилизация результатов работы;
— Baselines AKA базовые конфигурации;
— Конфигурации при компонентной разработке;
— Конфигурации при наличии продуктовых линеек.
Рабочие продукты и конфигурации
Что же будет являться рабочими продуктами в рамках проекта? Понятно, что для маркетинга и менеджмента продукт будет ровно один – тот, за который компания получит деньги. Ну, или несколько, по числу видов коробок, выдаваемых на рынок. Нас же интересует «нижний уровень» – то, чем будут оперировать постановщики задач, разработчики, тестеры и вообще каждый участник проекта. Задача CM – определить множество тех элементов, которые будут создаваться и изменяться командой. На этом этапе появляется понятие «configuration item» («элемент конфигурации») – это атомарный элемент, которым наиболее удобно управлять в рамках разработки. В дальнейшем будем называть его просто «CI».
К объектам, попадающим под действие CM, относятся и любые объекты, поставляемые вовне (инсталяторы, маркетинговые материалы и т.п.). Хоть их и можно получить из перечисленных выше рабочих продуктов, но конечный продукт, выдаваемый пользователю, также нуждается в идентификации.
Компонентная разработка и продуктовые линейки
Как же эти элементы конфигурации, атомарные единицы учета, организуются внутри проекта?
Складываются они вместе согласно архитектуре самого приложения. Ведь разработчики, как правило, стремятся уменьшить сложность производимых систем. С этой целью они раскладывают создаваемое на взаимосвязанные части – классы, компоненты, библиотеки, подсистемы и т.п. Упростим терминологию и в дальнейшем любые составные части создаваемых систем будем называть компонентами. CM же берёт эту организацию за основу и структурирует рабочие продукты соответствующим образом с помощью своих инструментов и политик.
Компоненты становятся новыми элементами конфигурации. Они становятся самостоятельными рабочими единицами, так же подлежащими единому контролю. Кроме того, они могут устанавливать даже собственный процесс разработки. CM’ные практики в этом случае нужны для того, чтобы эти отдельные блоки контролировать самостоятельным образом, получать промежуточные версии, стабилизировать и выпускать для интеграции в продукт более высокого уровня.
Итак, создаем систему, строим её из кирпичиков-компонентов. И нередка ситуация, когда одна система поставляется сразу в нескольких вариантах. За примерами далеко ходить не надо, взгляните на варианты поставки «Висты». И зачастую всё отличие разных вариантов/версий/редакций продуктов – всего в одном или нескольких компонентах, а то и вовсе в настройках. Как быть? Для этого создается то, что для простоты дальнейшего изложения будем называть продуктовыми линейками. Продуктовая линейка – это ответвление в истории развития продукта, дающее возможность изменять часть компонент независимо от других подобных ответвлений. (Здесь понятие «продукт» употребляется с маркетинговой точки зрения.)
Всё по теории эволюции – одноклеточное остается одноклеточным, но в результате мутаций и цепи случайностей (или же по злому умыслу) дает жизнь многоклеточным. Была линейка человекообразных приматов – от неё отделилась линейка homo sapience, но начальная порода обезьян продолжила жить своей жизнью. «Компоненты» у каждой «линейки» – почти на 99% совпадают. И только несколько процентов (мозги и ещё кое-что по мелочи) разрабатываются эволюцией независимо от родительской линейки и отличают одни виды от других.
Схема 1. Соотношение компонентов, суперкомпонента и продукта.
На схеме 1 образно показан компонентный состав продукта. 1-8 — это компоненты, 4 — это «суперкомпонент», включающий в себя компоненты 5 и 6. В рамках интеграции продукта работа с ним ведется, как с обычным компонентом.
Схема 2. Соотношение компонент и продуктов при использовании продуктовых линеек.
На схеме 2 показано, как одни и те же компоненты могут быть использованы при работе с продуктовыми линейками. Например, имеется Продукт 1, состоящий из нескольких компонентов и суперкомпонента. На его основе производятся продукты 2 и 3.
Продукт 2 берет все те же компоненты, за исключением 1 и 6 — они исключаются из работы (или игнорированием соответствующих директорий, или выключением директив компиляции). В дополнение, изменяется компонент 3 — он становится 3′ (штрих не проглядите). Также в единственный суперкомпонент добавляется новый компонент за номером 9.
Продукт 3 также берет за основу кодовую базу Продукта 1, однако берет в себя ещё и изменения из Продукта 2 — компоненты 9 и 3′. Также изменениям подвергаются компоненты 7 и 8, которые теперь называются 7′ и 8′ соответственно (да, тоже со штрихами).
Что в итоге? В итоге имеем несколько компонентов, интегрируемых одновременно в два-три разных продукта. Возьмем, к примеру, номер 2 – он неизменен во всех трёх продуктах. Напрашивается вывод – выпустить его один раз и просто «вставить» везде, где потребуют. Так и делается – компонентная команда в лице CM-инженера стабилизирует работу и передает на дальнейшую интеграцию трём «продуктовым» командам. Аналогично поступает CM-команда компонента 3’ – после внесения изменений поверх «предка» 3, полученный релиз компонента 3’ отдается в два продукта.
Причем использование одного компонента в разных продуктах – это не копирование исходников из директорий одного продукта в другой. Нет, смысл заключается именно в том, чтобы выпущенная конфигурация компонента находилась в системе контроля версий и все заинтересованные просто обращались к нему по мере включения в свой код.
В технической плоскости CM является связующим звеном между компонентами и линейками. В управленческой плоскости, где принимаются архитектурные решения, рулят менеджеры, тим-лиды, архитекторы, а всю техническую поддержку этого разделения возлагают на CM-инженеров. Именно они дают конечным разработчикам инструкции («политики») о том, в какие системы контроля складывать свой код, как именно его туда складывать, как регистрировать изменения в системах багтрекинга, каков порядок объединения компонент, что в каком виде давать тестерам и как выпускать продукт заказчику. Сами же продукты становятся новыми элементами конфигурации.
Основной вывод: CM помогает определить, из каких кирпичей мы будем складывать продукт и дает цементный раствор для их скрепления. Какими методами определяет и скрепляет – рассмотрим дальше.
Стабилизация результатов работы
Итак, определили рабочие продукты, компоненты, линейки – пора и за дело браться. Начинается цикл разработки. Работа идет, рабочие продукты появляются, изменяются, создаются новые компоненты, разделяются линейки – жизнь кипит. Как всегда, в определенный момент хочется остановиться, оглянуться назад и понять – в какой точке находится продукт, что и как уже сделано, каковы планы. Для того чтобы получить полную картину, нужно привести разработку к какому-то общему знаменателю. С точки зрения менеджмента это может быть сделано по-разному – можно, например, посмотреть прогресс работ, получить срез метрик и т.п. – и далее принять какое-то решение, касающееся распределения задач.
С точки зрения CM’а это означает, что надо стабилизировать конфигурацию рабочих продуктов. Например, имея команду из 20 человек, нужно взять все наработанные разными людьми куски функциональности – документы, код и друге результаты – и свести их воедино.
Стабилизация конфигурации – это процесс получения новой конфигурации из имеющихся промежуточных конфигураций. Для этого процесса также используются также термины «выпуск», «release» или «релиз». Результат стабилизации также может быть назван, в свою очередь, релизом или выпуском.
Например, есть основная конфигурация – версия продукта 1.0. Есть промежуточная конфигурация – разработанная девелопером новая «фича». Есть также 2 другие конфигурации – поправленные ошибки от двух других разработчиков. Стабилизацией в данном случае будет объединение результатов работы всех трех разработчиков и создание из них новой конфигурации, т.е. набора CI, которые образуют готовый продукт.
Полученная конфигурация проверяется на соответствие требованиям к составляющим её рабочим продуктам. Требования могут быть разнообразными, как правило, это количественные критерии качества. Скажем, в приведенном примере с 3 девелоперами, подобное требование к коду – это успешное прохождение 98% регрессионных тестов. Код от всех разработчиков интегрируется, конфигурация стабилизируется, продукт собирается (например, отстраивается) и отдается на тесты.
Для релиза также делаются release notes. На русский этот термин переводится как «заметки о выпуске» или «дополнительные сведения» – так этот термин звучит в глоссарии Microsoft. Также может быть использовано «описание выпуска».
Если конфигурация соответствует требованиям, предъявляемым к стабильным релизам, то конфигурация считается стабильной. Например, если процент пройденных регрессионных тестов – 98%. По выбору менеджмента или CM-инженера, она становится тем, что называется «baseline».
Базовая конфигурация
Baseline – это конфигурация, выбранная и закрепленная на любом этапе жизненного цикла разработки как основа для дальнейшей работы. Переводом термина могут быть фразы «базовая конфигурация», «базовый уровень», «базовая версия» или «стабильная база». В дальнейшем будет преимущественно использован термин «базовая конфигурация».
Если вернуться обратно к нашему примеру про трёх разработчиков, то там стабилизированная конфигурация прошла оценку качества. То же самое обязательно и при выпуске базовой конфигурации. Менеджмент (тим-лид или SQA) смотрит на показатели качества, а также на другие факторы – например, на результаты инспекций кода или что-то ещё, что может вызвать сомнения. После чего принимает решение о том, что релиз должен быть взят за основу для работы всех остальных разработчиков, быть базой для разработки. Далее CM-инженер выполняет разного рода действия (например, навешивает метку и отстраивает код продукта) и выбранная конфигурация становится базовой. При этом она (как минимум, в виде исходников) выкладывается в открытый для всей команды доступ.
Возможен вариант, когда конфигурация не проходит по критериям качества и вообще не может быть использована для сборки конечного продукта. Например, продукт только начал разрабатываться и готов только код отдельных компонентов, да и у тех – заглушки вместо работающих функций. Нужно сделать конфигурацию основой работы для всей команды, но при этом миновать процедуру релиза – просто потому, что пока нельзя ничего собрать воедино. Такая конфигурация также имеет право быть использованной в качестве базовой, главное — четко обозначить имеющиеся ограничения по использованию в заметках о выпуске.
Любой выпуск базовой конфигурации обязательно снабжается заметками о выпуске. Участник команды, берущий подобную конфигурацию для работы, должен знать – от чего именно он будет отталкиваться в работе. Также надо знать, есть ли в новой конфигурации те новые функции или исправления ошибок, от которых может зависеть его работа. Не лишним будет также знать, нужны ли какие-то специальные процедуры апгрейда его экземпляра системы перед использованием новой базы для разработки. Вся перечисленная информация как раз дается в заметках о выпуске.
Во многих командах результаты интеграционной работы (появляющиеся релизы и базовые конфигурации) выкладываются в специально отведенное место – область релизов, или release area. Организация этой области и поддержание её в актуальном виде – задача CM-инженеров.
Схема 3. Связь конфигураций, релизов и базовых конфигураций.
На Схеме 3 показан небольшой пример появления конфигураций во времени. Начальное состояние проекта – конфигурация 1. Она же является первым базисом, от которого будет идти дальнейшая разработка. Предположим, проект на начальной стадии. Через какое-то время появляется обновленная конфигурация 2. Разработка только началась и мы выпустили релиз, чтобы выдать команде хоть какую-то основу для дальнейшей работы. В ходе проверки выяснилось, что базой для работы этот выпуск служить не может – есть непонятные и противоречивые места.
Для их устранения группы разработки делают доработки. В результате них появляются конфигурации 3 и 4 – оба они разработаны на основе 2, но друг с другом они пока не согласуются, поскольку не включают изменения друг от друга. CM-инженер создает итоговую конфигурацию 5, сделанную на основе 2, 3 и 4. После проверки менеджмент дает отмашку – базовой конфигурации быть! По этому сигналу CM-команда выпускает этот релиз как официальную базовую конфигурацию и разработчики берут уже её за основу.
Далее история повторяется – группа разработки вносит изменения – появляется конфигурация 5. Её, в свою очередь, интегрирует CM-инженер и она получает номер 7. Он также становится официальной базой для разработки.
Конфигурации при компонентной разработке
Аналогичный подход используется и при компонентной разработке. Внутри каждого компонента идет работа, в рабочих продуктах и их элементах конфигурации постоянно появляются изменения, надо их периодически, или же по требованию менеджмента, стабилизировать. Каждый компонент делает это в общем случае самостоятельно и с тем графиком, который требуется именно для него. Поэтому, например, для одной команды стабилизация и выпуск релиза делается 5 раз в неделю, для другой – 1 раз в 2 недели.
Поскольку компоненты объединяются в единое целое, должны существовать отдельные процедуры и ресурсы для подобной системной интеграции. В этом случае работа интеграционной команды вышестоящего компонента или всей системы лишь немногим отличается от работы интеграторов компонентов. Отличается только масштаб, а также, возможно, инструменты и критерии оценки зрелости получаемых релизов.
В частности, после интеграции всей системы нужно не просто пройти регрессионное тестирование каждого входящего компонента. Надо ещё прогнать системные тесты, проверяющие взаимодействие разных частей системы между собой – как правило, это не входит в область тестирования каждой отдельной подсистемы. Кроме того, от CM’ной команды всего продукта может потребоваться сбор дополнительных метрик. Всё это требует больших ресурсов и некоторой доработки политик CM-команды вышестоящего компонента.
Конфигурации продуктовых линеек
Как меняются политики CM в случае, когда у нас не один продукт, а целое их множество, т.е. продуктовая линейка? Всё становится гораздо интереснее. Конечно, работа внутри компонентных команд продолжается так же, как и в других случаях. Изменяется их взаимодействие друг с другом.
Во-первых, компонентной команде надо учитывать все возможные зависимости их кода от других компонентов. И учитывать, что от продукта к продукту могут меняться интерфейсы и поведение каких-то функций. Отслеживание зависимостей – отдельная большая тема, так что пока не будем трогать её.
Во-вторых, изменяется порядок интеграции каждого компонента в конечные продукты. Теперь каждая базовая конфигурация должна отдаваться на интеграцию только в те продукты, которые требуют функциональность, разрабатываемую в ней. Или же необходимо проверять, чтобы новая функциональность, предназначенная для одного продукта, не начала вдруг работать в другом и вызывать поломки.
В-третьих, разработчик должен постоянно думать о том, как будут работать его изменения на разных продуктах. Ведь в них могут быть задействованы совершенно разные наборы функциональности – поэтому в коде надо делать соответствующие проверки.
Отсюда следуют две возможные линии поведения компонентных команд:
1. Выпуск стольких линеек компонентов, сколько продуктов сейчас находится в работе и сопровождении. Накладный вариант с точки зрения отслеживания изменений и конфигураций, а также сложно с точки зрения интеграции одних и тех же изменений в разные компонентные линейки.
2. Поддержка всех продуктов и их наборов функциональности одновременно в одной линейке компонента. При этом надо организовать код таким образом, чтобы можно было гибко «включать» и «выключать» функциональность через настройки во время «отстройки» системы или во время её инсталляции и запуска в эксплуатацию. Также появляются накладные расходы для разработчиков, которые, ожидая каждого вносимого изменения, вынуждены учитывать, как это изменение повлияет на работу каждой из фич, затронутых измененным кодом.
Отсюда же следует и поведение команды CM. Надо учитывать то, как идет работа в командах и вести стабилизацию компонентов/продуктов и выпуск их базовых конфигураций соответствующим образом. В целом же тема эта обширна и стоит отдельной статьи с большим числом примеров из жизни. Пока что просто примем за данность следующее обстоятельство — продукты и компоненты имеют свойства разветвляться и политики, а проектная документация по CM должна это учитывать.
Вместо заключения
Следующие заметки будут посвящены более практическим вещам — контролю версий и отслеживанию запросов на изменениями.
