Что такое конфигурация в программировании
Что такое конфигурация в программировании
Что именно стоит настраивать.
Таким образом программа привязывается к конкретному JDBC драйверу. Использовать другой драйвер, например заменить мост на RMI-прокси или, в случае Oracle, OCI на Thin без перекомпиляции уже нельзя.
Способы хранения настроек.
Разумное же умолчание для параметров часто просто невозможно представить. Например, что поставить в качестве имени SMTP-сервера? В случае Unix-систем можно попробовать поставить localhost, но для Windows-мира это редко кому подойдёт.
Рассмотрим наиболее распространённые варианты:
Ini-файлы.
В Java нет стандартного класса для чтения ini-файлов, но это не проблема. Т.к. формат очень прост, его легко сделать самому:
Файлы Properties.
XML-файлы.
Этот формат подходит для многих целей, в том числе и для хранения настроек. XML-формат ориентирован на древовидные структуры, что довольно естественым образом отображается на объекты. Пример типичного файла:
Для XML также написано несколько библиотек для универсального сохранения (сериализации) объектов в файлах XML. Такие библиотеки позволяют отделить алгоритм сохранения от самого объекта, а это, как уже упоминалось, имеет много плюсов.
Сериализация.
Базы данных.
Скрипты.
Пример программы с конфигурацией в XML.
В качестве примера можете посмотреть простенькую программы, использующей XML-файл в качестве конфигурационного. Сохраняемые параметры можно редактировать как из программы, так при помощи текстового редактора.
Пример содержимого конфигурационного файла:
В качестве XML-парсера используется Sun-овский парсер в режиме DOM. На таком простом примере не видно особых преимуществ формата XML над теми же файлами properties. Они становятся заметны только в достаточно сложных программах, где становится необходимо хранить списки однотипных параметров или же содержимое объектов с уровнем вложенности два или более.
От новичка в эксперты: пишем файл конфигурации для вашего Python приложения
Относитесь к файлу конфигурации, использующемуся в приложении, как к разрабатываемому вами коду.
Когда мы разрабатываем программное обеспечение, то всегда прикладываем много усилий для написания качественного и производительного кода. Однако зачастую этого недостаточно.
Разработка качественного программного обеспечения, включает заботу о разработке своей экосистемы, например, для организации процессов тестирования, развертывания, сетевого обмена данными и т.д. Одним из наиболее важных аспектов, который необходимо при этом учитывать, является реализация гибкого механизма управления конфигурацией (настройками) программного обеспечения.
Правильная реализация управления конфигурацией по сути должна позволять запускать программное обеспечение в любой среде без внесения изменений в его исходный код. Этот подход обеспечивает эффективное управление проблемными настройками вашего приложения со стороны администраторов Ops, обеспечивает представление информации о том, что может произойти во время его функционирования, а также позволяет изменять его поведение во время выполнения.
Наиболее распространенные конфигурации включают в себя учетные данные для базы данных или внешней службы, имя хоста сервера, а также любые динамические параметры и т.д.
В этой статье я хочу поделиться с вами несколькими зарекомендовавшими себя практиками управления конфигурациями, а также как мы можем реализовать их в приложениях, написанных на Python.
Когда необходим файл конфигурации приложения?
Перед разработкой конфигурационного файла сначала необходимо спросить себя, нужен ли вообще какой-либо внешний файл с данными? Разве мы не можем просто поместить их в виде константных значений прямо в исходном коде? Собственно, достаточно известная концепция The Twelve-Factor App давно отвечает на этот вопрос:
Лакмусовой бумажкой для проверки правильности решения о выделении config всей конфигурационной информации приложения из кода, является рассмотрение возможности о публикации в любой момент кодовой базы вашего приложения, то есть можно ли сделать его исходный код открытым, без нарушения конфиденциальности чьих-либо учетных данных.
Обратите внимание, что это определение config не включает внутреннюю конфигурацию приложения, такую как, например, как config/routes.rb в Rails, или способ подключения модулей в Spring. Перечисленные выше примеры способов конфигурации не меняются в зависимости от среды развертывания, и поэтому это лучше всего реализовать их в коде.
Подходы рекомендованные этой концепцией предписывают, чтобы любые параметры, зависящие от среды, такие как учетные данные базы данных, находились во внешнем файле. В противном случае их реализуют просто обычными константами в коде. Другой вариант использования, который я часто вижу, – это хранение динамических переменных (данных) во внешнем файле (базе данных), например, черный blacklist или белый whitelist список пользователей. Ими могут быть числа в заданном диапазоне (например, длительность тайм-аута) или любые текстовые файлы с произвольным содержимым. Отметим, что эти динамические переменные (данные) остаются неизменными вне зависимости от особенностей исполняемой среды.
В свою очередь файл конфигурации делает программное обеспечение более гибким и легким для редактирования его функциональных возможностей. Однако, если он слишком сильно разрастается, рациональнее все таки перенести его в базу данных.
Какой формат файла конфигурации использовать?
С практической точки зрения, на формат файла конфигурации нет никаких технических ограничений, если код приложения может его прочитать и анализировать. Но есть и более рациональные практики для выбора формата файла с настройками. Так наиболее распространенными, стандартизованными форматами являются YAML, JSON, TOML и INI. Самый подходящий формата для файл конфигурации должен соответствовать как минимум трем критериям:
Возможно вам пока не ясно какой из форматов файла лучше использовать. Но если вы подумаете об этом в контексте программирования на языке Python, то наиболее очевидным ответом будет YAML или INI. Форматы YAML и INI хорошо понятны большинству программ и пакетов Python.
INI файл, вероятно, является наиболее простым решением для сохранения настроек приложения, имеющих только один уровень иерархии (вложенности) параметров. Однако формат INI не поддерживает других типов данных, кроме строк: в нем все данные имеют строковое представление.
Та же конфигурация настроек в YAML выглядит следующим образом.
Как видите, YAML изначально поддерживает использование вложенные структуры (также как и JSON) с помощью отступов. Кроме того, YAML, в отличие от формата INI файлов, поддерживает некоторые другие типы данных такие как целые и с плавающей запятой числа, логические значения, списки, словари и т.д.
Формат файлов JSON по сути очень похож на YAML и тоже чрезвычайно популярен, однако в JSON файлы нельзя добавлять комментарии. JSON, как текстовый формат содержащий структурированные данные, часто используется для хранения внутренней конфигурации внутри программы, но совершенно не предназначен для того, чтобы делиться конфигурацией приложения с другими людьми (в особенности с далекими от вопросов разработки ПО).
Формат TOML, с другой стороны, похож на INI, но поддерживает гораздо больше типов данных, а также специальный синтаксис для хранения вложенных структур. Его часто используют менеджеры пакетов Python такие, например, pip или poetry. Но если в файле конфигурации присутствует слишком много вложенных структур, то YAML в этом отношении, с моей точки зрения, наилучший выбор. Следующий ниже фрагмент файла выглядит как INI, но в отличие от него каждое строковое значение имеет кавычки.
Пока что мы выяснили ЧТО из себя представляют форматы файлов YAML, JSON, TOML и INI, далее мы рассмотрим КАК они могут быть использованы.
YAML/JSON — простое чтение внешнего файла
Как обычно, мы начнем с самого простого, то есть создадим внешний файл с настройками, а затем прочитаем его. Python имеет в своем составе встроенные пакеты для чтения и анализа файлов YAML и JSON. И как видно из приведенного ниже кода, они фактически возвращают один и тот же объект типа dict, поэтому доступ к его атрибутам будет одинаковым для обоих файлов.
Чтение
Валидация
Пакет Cofigureparser из состава стандартной библиотеки Python
В этом разделе рассмотрим пакеты, предназначенные непосредственно для управления конфигурацией приложения. И начнем со встроенного в стандартную библиотеку Python пакета: Configureparser.
Configureparser в большинстве случаев используется для чтения и записи INI файлов, и поддерживает чтение входных данных из файла сразу в виде словаря или итерируемого iterable файлоподобного объекта. Как известно, каждый файл INI состоит из нескольких секций, содержащих настройки в виде пар ключ-значение. Ниже приведен простой пример кода для доступа к полям настроек.
Чтение
Валидация
Валидация данных с Configureparser не так проста, как для пакетов, работающих с форматами YAML и JSON. Во-первых, он не возбуждает исключения FileNotFoundError если файла настроек не существует, а вместо этого вызывает исключение типа KeyError, как при попытке доступа к отсутствующему ключу.
Тем не менее, Configureparser может возбуждать исключение ParserError при наличии нескольких ошибок (см. пример кода с тестами ниже). И в большинстве случаев этого достаточно для определения проблемных мест в самом файле настроек.
Python-dotenv — считываем конфигурацию приложения из переменных среды
Чтение
Валидация
Dynaconf — мощный конфигуратор настроек для приложений Python
Чтение
Валидация
Одна из наиболее интересных, в частности для меня, возможностей dynaconf – это его настраиваемый валидатор. Как упоминалось ранее, Configureparser недостаточно строго проверяет корректность INI файлов настроек, но это можно легко реализовать в dynaconf. В следующем примере мы проверяем, существуют ли определенные ключи в файле с настройками и имеется ли в нем конкретный ключ с корректным значением. Если вы читаете настройки из файла YAML или TOML, которые как мы говорили ранее, поддерживают несколько типов данных, то вы даже можете проверить, находится ли значение настройки, например, число в заданном диапазоне.
Интеграция с Pytest
Обновляем конфигурацию приложения во время его выполнения
Hydra — упрощаем разработку, динамически создавая иерархическую структуру конфигурации приложения
Рассмотрим последним, в этой статье, способ создания и поддержки конфигурации для вашего приложения, который по сути является гораздо большим, чем просто загрузчик и парсер файлов с настройками.
Hydra – это платформа, разработанная Facebook для гибкой и элегантной настройки самых сложных приложений. Которая помимо чтения, записи и валидации корректности файлов конфигурации, реализовывает свою достаточно рациональную стратегию упрощения управления несколькими конфигурационными файлами, переопределения (перезаписи) их с использованием интерфейса командной строки, создания snapshot снимка состояния приложения перед каждым его запуском (между перезапусками) и т.д.
Чтение
Валидация
Hydra прекрасно интегрируется с декоратором @dataclass для выполнения основных проверок корректности, таких как проверка типов или значения полей. Однако у нее нет поддержки __post_init__ метода расширенной проверки значений, как это описано в моей предыдущей статье.
Группа конфигураций
Когда конфигурация приложения станет более сложной, то в вашей программе она может иметь следующую структуру (пример из документации Hydra).
Далее…
В этом случае в основном потоке запускаются 6 задач одновременно:
Вывод
В этой статье мы рассмотрели несколько способов управления конфигурацией приложений в Python. Независимо от того какой из них вы выберете, всегда необходимо думать о удобочитаемости файлов конфигурации, дальнейшей их поддержки, а также способах обнаружения ошибок для случаев их некорректного использования. Таким образом, конфигурационный файл – это по сути еще один тип кода.
Надеюсь, вам понравится эта статья, не стесняйтесь оставлять свои комментарии ниже.
Цикл статей по основам 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, хорошо бы документировать. Об этом тоже вкратце будет упомянуто.
Итак, каковы задачи управления конфигурацией?
Для начала — достаточно. Следующая часть будет посвящено тому, как же определяются продукты и конфигурации, которыми мы будем управлять. Также коснусь вопроса о компонентной разработке, продуктовых линейках и их связи с СМ.
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 должна это учитывать.
Вместо заключения
Следующие заметки будут посвящены более практическим вещам — контролю версий и отслеживанию запросов на изменениями.