Разделы портала

Онлайн-тренинги

.
Построение программного обеспечения при каждом изменении
09.12.2008 16:33

Глава из книги ''Непрерывная интеграция: улучшение качества программного обеспечения и снижение риска''

Авторы: Поль М. Дюваль
Стивен М. Матиас III
Эндрю Гловер

Источник: Непрерывная интеграция: улучшение качества программного обеспечения и снижение риска
Материал предоставил: RSDN Magazine #1-2008
Автоматизируйте построения
Запускайте построение одной командой
Отделяйте сценарии построения от IDE
Централизуйте элементы программного обеспечения
Создайте строгую структуру каталога
Ранний сбой построения
Осуществляйте построение для каждой среды
Типы и механизмы построения
Используйте выделенную машину для интеграционного построения
Используйте сервер CI
Выполняйте интеграционное построение вручную
Выполняйте быстрое построение
Поэтапное построение
Как это будет работать у вас?
Резюме
Вопросы

Вся проклятая вселенная должна быть разобрана кирпич за кирпичом, а затем восстановлена.
Генри Миллер (Henry Miller), американский писатель и живописец (1891-1980)

В начале ХХ века рабочие на конвейере Форда собирали автомобили вручную. Сборка модели T занимала несколько дней. Нынешние автомобили во сто крат сложнее модели T, но их сборка происходит быстрее. Почему? Ответ прост: автоматизация. В автомобилестроении автоматизация освободила людей от выполнения повторяемых задач, поручив их роботам. Точно так же, используя автоматизированное построение, можно механизировать трудоемкие задачи процесса разработки программного обеспечения. Фактически, в обеих отраслях промышленности прогресс был обусловлен ростом спроса. Когда рабочий монотонно трудится по восемь часов в день в основном руками, у него совершенно нет времени ни на усовершенствование продукта и процесса его производства, ни на планирование развития.

Иногда разработчики оказываются сапожниками без сапог, они создают приложения для автоматизации труда пользователей, но не автоматизируют собственные процессы разработки программного обеспечения. Анализ, проведенный в 2003 году, показал, что порядка 27% групп разработки осуществляют ежедневное построение. По аналогии с автомобилестроением можно сказать, что мы все еще используем на конвейере старую добрую ручную сборку.

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

Кнопка <Integrate> (как видно на рисунке 4.1) представляет собой "автоматизированную линию сборки", которая воплощает множество практик, составляющих высокоуровневую практику CI. Автоматизированное построение представляет современную автоматизированную линию сборки, использующую "роботов" для интеграции программного обеспечения.

Рисунок 4.1. Построение программного обеспечения способно улучшить его качество и снизить риск

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

Автоматизируйте построения

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

Что такое построение (build) программного обеспечения? Только ли это компиляция (compiling) его компонентов? Или построение - это компиляция компонентов и запуск автоматизированных проверок? Должно ли построение включать инспекции (inspection)? Построение может включать любой из этих процессов, что эффективно снижает риски; однако чем больше процессов добавлено в построение, тем медленней обратная связь. Следовательно, вы должны решить, какие процессы следует включить в автоматизированное построение. Например, в главе, 2, "Введение в непрерывную интеграцию", мы описали практику закрытого построения (private build), состоящего из интеграции изменений группы и полного построения (которое может включать компиляцию, тестирование, инспекции и т.д.), выполняемого на вашей рабочей станции до передачи кода в хранилище с контролем версий, что предотвращает сбойные построения. С другой стороны, если вы хотите лишь опробовать пару изменений и не предполагаете передавать их, вы можете осуществить облегченное построение, т.е. выполнить только компиляцию и несколько проверок модуля.

ПРИМЕЧАНИЕ

"Ant великий?"

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

Существует множество инструментов построения. К наиболее популярным относятся Ant для Java и NAnt для .NET. Использование инструментов выполнения сценариев, разработанных специально для построения программного обеспечения, вместо собственного набора пакетных файлов оболочки, является более эффективным способом создания однозначного и воспроизводимого решения построения.

Помните: построение должно осуществляться нажатием одной кнопки. Когда вы нажимаете кнопку <Integrate>, как показано на рис. 4.1, на сборочном конвейере запускается процесс, который создает работоспособное программное обеспечение. Иногда организации не способны задействовать CI потому, что они не могут реально автоматизировать свое построение. В некоторых случаях такая неспособность обусловлена жесткой связью и зависимостями, например с библиотеками сторонних производителей и жестко заданными ссылками. Я не раз видел проекты с яркими примерами следующего.

  • Жесткая зависимость от совместно используемых дисков. Например, сценарии построения жестко привязаны к диску K:\ (когда на машине разработчика диск "K" недоступен, возникает проблема).
  • Существование жестко заданных ссылок на положение (диск C:\) некоторых инструментов, отсутствующих на машине разработчика.

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

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

Запускайте построение одной командой

Мартин Фаулер советует: "Получайте все необходимое из хранилища с контролем версий, чтобы можно было построить целую систему, отдав одну команду". Концепция кнопки <Integrate> реализуема только тогда, когда вы можете запускать построение одной командой. Например, ввод в командной строке команды nant integrate, как представлено в листинге 4.1, является примером единой команды, инициализирующей интеграционное построение.

> nant integrate
Buildfile: file:///C:/dev/projects/acme/project.build
clean:
svn-update:
all:
compile-src:
compile-tests:
integrate-database:
run-tests:
run-inspections:
package:
deploy:
BUILD SUCCEEDED
Total time: 3 minutes 13 seconds

Для работы в автоматизированном режиме сервер CI нуждается в "безголовом" процессе, например в едином сценарии команды. При выполнении интеграционного построения на отдельной машине нельзя полагаться на IDE. Кроме того, для осуществления интеграционного построения единой командой необходимо получить доступ ко всем элементам программного обеспечения (из хранилища с контролем версий).

Автоматизированное построение - это как кнопка <Integrate>: нажмите кнопку, и ваше программное обеспечение будет построено (и развернуто). Это означает, что все элементы последнего будут связаны, а их функционирование проверено. Рис. 4.2 иллюстрирует действия, обычно выполняемые сценарием построения.


Рисунок 4.2. Логические процессы сценария построения.

В общих чертах построение программного обеспечения подразумевает примерно следующие этапы.

  1. Выполняйте построение, используя такие инструменты, как NAnt, Rake, Ant или Maven. Начните с простых сценариев; впоследствии в них можно будет добавить больше процессов.
  2. Ведите внутрь сценария построения все процессы (очистка, компиляция и т.д.), которые нужно присвоить кнопке <Integrate>.
  3. Чтобы построить программное обеспечение, запустите сценарий из IDE или командной строки.

Листинги 4.2-4.6 демонстрируют примеры использования инструмента построения NAnt для платформы .NET; но вы вполне можете получить тот же результат при помощи других средств сценариев построения, таких как Ant или Maven для Java, MSBuild для .NET, Rake для Ruby и т.д. Вопрос не в том, какой инструмент выбрать; главное - использовать существующий инструмент, а не создавать собственное решение.

Листинг 4.2 демонстрирует сценарий NAnt, использующий задачу delete для удаления всех каталогов и файлов перед новым построением. Это уменьшает вероятность того, что файлы предыдущего построения неблагоприятно повлияют на новое построение.

Листинг 4.2

<target name="clean">
<delete dir="${build.dir}" verbose="true" failonerror="false"/>
<delete dir="${dist.dir}" verbose="true" failonerror="false"/>
<delete dir="${reports.dir}" verbose="true" failonerror="false"/>
</target>

Листинг 4.3 демонстрирует компиляцию кода C# с использованием задачи csc, которая компилирует все файлы в некотором каталоге и перемещает полученный файл .dll в другой каталог. Вторая часть данного примера демонстрирует запуск сценария SQL, выполняющего определения данных и создающего таблицы в базе данных.

Листинг 4.3

<target name="build">
<csc target="library" debug="${build.debug}"
output="${build.dir}\bin\${config}\${nant.project.name}.dll">
<sources failonempty="true">
<include name="${project.localpath}/**/*.cs" />
</sources>
</csc>
</target>
<target name="integrate-database">
<sql connstring="${project.db.conn}"
delimiter=";"
delimstyle="Normal"
print="true"
source="${data-definitions}"/>
</target>

Листинг 4.4 содержит пример выполнения задачи nunit2 в NAnt, выполняющей комплекс проверок NUnit. Обратите внимание, что при сбое любой из проверок происходит общий сбой построения (как можно заметить, атрибуту failonerror задачи nunit2 присвоено значение true). Как упоминалось в главе 2, "Введение в непрерывную интеграцию", для корректности построения все тесты и инспекции должны быть пройдены.

Листинг 4.4

<target name="run-tests" depends="compile-src">
<nunit2 failonerror="true">
<formatter type="Xml"
usefile="true"
extension=".xml"
outputdir="${build.dir}/results"/>
<test assemblyname="${build.dir}\bin\${config}\${project}.Test.dll"
appconfig="mydefaulttest.config"/>
</nunit2>
</target>

Листинг 4.5 демонстрирует выполнение задачи fxcop, запускающей FxCop, бесплатный инструмент для платформы .NET, который инспектирует и оповещает о предопределенных нарушениях кода, связанных с производительностью, защитой, соглашениями именования и т.д.

Листинг 4.5

<target name="fxcop">
<fxcop>
<targets>
<include
name="${build.dir}\bin\${config}\${project}.dll"/>
</targets>
<arg value="/out:${build.dir}\bin\${config}\fxcop.xml"/>
</fxcop>
</target>

Последнее действие построения, представленного на рис. 4.2, - развертывание. Листинг 4.6 иллюстрирует использование задачи NAnt для простого развертывания на сервере FTP.

Листинг 4.6

<target name="deploy">
<connection id="staging"
server="devqa.ib.com"
username="helloworld"
password="myftppwd" />
<ftp connection="staging"
remotedir="incoming"
localdir="c:\dev\project\acme">
<put type="bin">
<include
name="${build.dir}\bin\${config}\${project}.dll" />
</put>
</ftp>
</target>

Если сценарий построения выполнен разработчиком безо всякой обратной связи, то он даже не будет знать, прошло ли построение успешно. Очень простой пример уведомления об отказе включен в листинг 4.4. Если любая из проверок, запущенных задачей nunit2, закончится неудачей, то все построение будет считаться сбойным. Фактически, NAnt прекратит работу с сообщением BUILD FAILED, так что никаких сомнений не будет.

Это ни в коем случае не исчерпывающий пример сценария построения. Сценарий построения, который полностью имитирует кнопку <Integrate>, должен был бы включать намного больше процессов.

Отделяйте сценарии построения от IDE

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


Рисунок 4.3. Отсоединение сценария построения от IDE

Создание отдельного сценария построения важно по двум причинам.

  1. Разные разработчики могут использовать разные IDE, поэтому может оказаться весьма сложно соотнести конфигурационные особенности каждого IDE.
  2. Сервер CI должен работать автоматически, осуществляя построение без человеческого вмешательства. Следовательно, сервером CI может и должен быть использован тот же автоматизированный сценарий построения, что и разработчиками.

Централизуйте элементы программного обеспечения

Для эффективного построения программного обеспечения все его элементы следует централизовать. Централизуя элементы программного обеспечения в системе контроля версий, вы способствуете построению единой командой, описанному ранее в этой главе. Кроме того, централизация помогает предотвратить проблему "но на моей машине это работает", когда разработчик не способен воспроизвести дефект, который произошел в некой другой системе, например на проверочной машине или машине пользователя. В этом разделе мы рассмотрим различные методы централизации элементов программного обеспечения.

Один из подходов централизации элементов программного обеспечения подразумевает использование для хранения всех файлов хранилища с контролем версий. В книге Стивена Беркзука (Stephen Berczuk) и Бреда Апплетона (Brad Appleton) Software Configuration Management Patterns это называется "Repository pattern" (схема хранилища) и подразумевает, что "рабочее пространство состоит не только из кода", а включает следующее:

  • компоненты в виде исходных файлов или библиотек;
  • компоненты от сторонних производителей, такие как файлы JAR, библиотеки, файлы DLL и другие файлы, в зависимости от языка и платформы;
  • файлы конфигурации;
  • файлы данных для инициализации приложения;
  • сценарии построения и параметры среды построения;
  • сценарии установки для некоторых компонентов.

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

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

Создайте строгую структуру каталога

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

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

  • implementation
  • requirements
  • design
  • management
  • deployment
  • testing
  • tools

Безусловно, все каталоги верхнего уровня могут содержать множество вложенных каталогов. Каталог implementation должен содержать только файлы исходного кода и может выступать в роли первичного каталога для построения.

Ранний сбой построения

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

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

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

Осуществляйте построение для каждой среды

Зачастую проект предусматривает развертывание в различных средах. Для этого может пригодиться наличие в хранилище различных файлов конфигурации, предназначенных для настройки разных систем (разработка, интеграция, тестирование, QA и работа), включая файлы .properties, .xml или .ini. Каждой платформе, языку и инструменту сценариев будет соответствовать собственный вариант конфигурации. Возможность перенастройки построения основана на использовании сценариев, выбирающих предопределенные конфигурации, доступные в программном обеспечении, не изменяя базовые функции сценариев построения. В большинстве случаев вы можете обеспечить такое поведение, изменяя конфигурационные файлы, используемые приложением. Можно также настроить среды выполнения и API, с которыми взаимодействует приложение. Конкретная методика зависит от платформы и принятых соглашений. Ниже приведен список перестраиваемых значений конфигурации, присущих большинству сред.

  • Достоверность регистрации.
  • Настройка сервера приложений.
  • Информация для подключения к базе данных.
  • Конфигурация среды выполнения.

Хотя среды, в которых осуществляются тестирование или развертывание, могут различаться, сценарии построения не должны быть разными. Файлы конфигурации (типа .properties или .include) позволяют перебирать варианты без необходимости копировать и вставлять значения, подходящие для каждой среды, внутрь сценариев построения. Это еще одна область, где, как и в исходном коде, дублирование ведет к большим проблемам и снижению надежности. Для гарантии возможности создания работоспособного программного обеспечения в любой среде следует улучшить способность сценария построения к настройке за счет применения параметров. Как показано на рисунке 4.4, вы можете запускать один и тот же сценарий построения, предоставляя ему соответствующий файл свойств, настраивающий построение для каждой среды. Например, при развертывании в среде QA вы можете вызывать сценарий построения так:

ant -f build.xml -D environment=qa

Здесь environment - свойство, которое было определено в сценарии Ant. Параметр -D означает, что это системный параметр, передаваемый сценарию Ant.


Рисунок 4.4. Настраиваемое построение для разных сред

Типы и механизмы построения

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

Типы построения

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

Закрытое построение

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

  1. Проверьте код, полученный из хранилища.
  2. Внесите в него изменения.
  3. Получите последние системные изменения из хранилища.
  4. Запустите построение, включающее выполнение всех ваших юнит-тестов.
  5. Передайте свои изменения кода в хранилище.

Интеграционное построение

Интеграционное построение (integration build) интегрирует изменения, внесенные в хранилище группой с общей линией (mainline) (называемой также головой (head) или магистралью (trunk)). В идеале оно должно осуществляться на выделенной машине.

Фаулер рассматривает различные типы построения, которые могут быть выполнены в ходе интеграционного построения. Он называет это "поэтапным построением" ("staged builds"), которое включает "передающее построение" ("commit build") и "последующие построения" ("secondary builds"). Передающее построение - это ваше самое быстрое интеграционное построение (меньше десяти минут), включающее компиляцию и юнит-тесты. Последующее построение - это интеграционное построение, которое запускает более медленные тесты, например тестирование компонентов, системы и производительности. Здесь могут также осуществляться автоматизированные инспекции соблюдения стандартов программирования и сложности кода.

Финальное построение

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

Механизмы построения

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

  • По требованию (on-demand). Это управляемый пользователем процесс, в котором некто вручную инициализирует интеграционное построение.
  • По расписанию (scheduled). Процессы запускаются по расписанию, например, ежечасно, независимо о того, произошли ли изменения. Расписание действий может пригодиться для повторяемых процессов, таких как запуск исчерпывающего набора проверок защиты или загруженности программного обеспечения. Для расписания можно использовать задачу cron, хотя большинство серверов CI самостоятельно поддерживают расписание.
  • Опрос изменений (poll for changes). Регулярно инициируемый процесс проверки хранилища с контролем версий на предмет изменений. Если таковые обнаружены, он запускает интеграционное построение. Все серверы CI поддерживают некий механизм "опроса изменений".
  • Управляемое событиями (event-driven). Управление событиями подобно опросу изменений, но вместо инструмента CI построение запускает хранилище с контролем версий на основании предопределенного события (изменения). Если хранилище с контролем версий обнаруживает изменение, оно инициализирует сценарий построения.

Запуск построения

Таблица 4.1 демонстрирует взаимосвязь между типом построения и способом его запуска.

Таблица 4.1.

Тип построенияМеханизм построения
ЗакрытоеПо требованию
ИнтеграционноеПо требованию, опрос изменений, по расписанию, управляемое событиями
ФинальноеПо требованию, по расписанию

Используйте выделенную машину для интеграционного построения

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

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

Питер (технический руководитель). Я хотел бы приобрести для нашего проекта Logistics выделенную машину интеграционного построения.

Билл (руководитель проекта). Зачем вам отдельная машина?

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

Билл. Звучит заманчиво, но, Питер, у нас действительно нет денег на это. Я полагаю, что вам потребуется по крайней мере 1 000 долларов, не так ли?

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

Билл. Ладно, но все, что мы можем сделать сейчас, это задействовать одну из дополнительных машин в серверной. Вы можете удалить с нее все и сделать машиной интеграционного построения.

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

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

  • Рекомендуемые системные ресурсы. Использование правильных средств позволяет добиться многого. Чем лучше аппаратные ресурсы, тем меньше продолжительность построения (обсуждается далее в этой главе). Как правило, цена увеличения аппаратных ресурсов машины интеграционного построения окупается за счет экономии времени.
  • В хранилище с контролем версий находятся все элементы программного обеспечения. Все, что имеет отношение к его разработке, должно быть передано в хранилище с контролем версий. Сюда относится исходный код, сценарии построения, файлы конфигурации, инструменты (сервер приложений, сервер баз данных и инструменты статического анализа), сценарии проверки кода и файлы базы данных (см. раздел "Централизуйте элементы программного обеспечения" ранее в этой главе).
  • Чистая среда. Перед выполнением интеграционного построения сценарий CI должен удалять любые зависимости кода от среды интеграции. Следует гарантировать удаление всего исходного кода и бинарных файлов предыдущего интеграционного построения. Удостоверитесь также, что система CI устанавливает проверочные данные и любые другие элементы конфигурации в определенное состояние. Этот подход уменьшает количество предположений, ликвидирует зависимости и организует построение программного обеспечения как будто на новой машине.

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

ПРИМЕЧАНИЕ

Волшебная машина

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

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

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

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

Используйте сервер CI

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

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

И так далее. В большинстве серверов CI эти средства уже реализованы. Уверен, что можно без проблем подобрать инструмент, который полностью удовлетворяет вашим потребностям и подходит к среде разработки. CruiseControl, Luntbuild, Continuum, Pulse, и Gauntlet - вот лишь некоторые из инструментов, которые можно использовать для реализации CI. В приложении Б,"Обсуждение инструментальных средств CI", рассматриваются и оцениваются различные инструменты CI, присутствующие на рынке на момент написания этой книги.

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

Выполняйте интеграционное построение вручную

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

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

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

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

Выполняйте быстрое построение

Остановка работы на время ожидания результата построения замедляет ритм разработки проекта. Следовательно, если построение занимает слишком много времени, это бросает тень на практику CI. Быстрый отклик критичен для CI. Чем короче продолжительность интеграционного построения, тем быстрее вы получите результат.

ПРИМЕЧАНИЕ

Масштабируемость и производительность интеграционного построения

Масштабируемость построения (build scalability) определяет, насколько ваша система построения пригодна к увеличению количества интегрируемого и анализируемого кода. Производительность построения (build performance) определяет его продолжительность. В идеале, по мере увеличения объема базового кода ваша система CI должна оставаться способной обрабатывать его без существенного снижения производительности.

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

На высоком уровне подход к диагностике и уменьшению продолжительности построения можно описать так:

  1. Соберите показатели построения.
  2. Проанализируйте их.
  3. Выберите и осуществите усовершенствования.
  4. Снова оцените построение и при необходимости продолжите улучшения.
ПРИМЕЧАНИЕ

Десятиминутное построение

Во втором издании книги Extreme Programming Explained Кент Бек (Kent Beck) приводит хорошее эмпирическое правило, согласно которому построение (интеграция) должно занимать не более десяти минут. Большинство разработчиков, использующих CI, не переходят к следующей задаче, пока их последняя интеграция не увенчается успехом. Следовательно, построение, занимающее больше десяти минут, может существенно замедлить их работу. Данная рекомендация может подойти для большинства проектов. Ваше десятиминутное передающее построение не должно включать все типы тестов и проверок. Вы можете сэкономить время, последовательно применяя разные типы построения (как уже упоминалось, Фаулер называет это "поэтапным построением").

Сбор показателей построения

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

Таблица 4.2

Показатель интеграционного построенияОписание
Время компиляцииВремя, необходимое для компиляции программного обеспечения. Подлежит сравнению с прошлым временем компиляции
Количество строк исходного кода (Source Lines Of Code - SLOC)Обозначает размер системы или ориентировочный объем кода, подлежащего компиляции
Количество и типы инспекцийКоличество выполняемых инспекций разных типов. Выявите и удалите избыточные
Среднее время создания сборокВремя, необходимое для создания сборки, архива или упаковки программного обеспечения
Время проверки (по категориям)Время, необходимое для выполнения проверок на каждом уровне: модуль, компонент и система (они описаны в главе 6, "Непрерывная проверка")
Соотношение успешных и неудачных построенийЧтобы определить соотношение успешных и неудачных построений, разделите число последних на общее количество построений
Время инспекцииВремя, необходимое для выполнения всех автоматизированных инспекций
Время развертыванияВремя, необходимое для развертывания программного обеспечения в среде назначения после интеграционного построения
Время перепостроения базы данныхВремя, необходимое для перепостроения базы данных
Системные ресурсы машины интеграционного построения и их использованиеУвеличивая память, скорость диска и процессора, можно улучшить производительность интеграционных построений. Это поможет выяснить, имеет ли машина интеграционного построения сервер приложений, сервер баз данных и некоторые другие процессы, которые растрачивают память и скорость процессора
Загрузка системы контроля версийПомогает выявить пиковую загруженность системы с контролем версий, продолжительность проверки и загрузки проекта с машины интеграционного построения и адекватность пропускной способности сети, процессора, памяти и дисков

Анализ показателей построения

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


Рисунок 4.5. Снижение продолжительности интеграционного построения

Выбор и реализация усовершенствований

Вооружившись стратегией усовершенствований, вы можете приступать к их реализации.

Использование выделенной машины для интеграционного построения

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

Увеличение аппаратных возможностей машины (машин) интеграционного построения

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

ПРИМЕЧАНИЕ

Покупка машины интеграционного построения

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

  • Какова текущая скорость процессора? Имеется ли возможность повысить скорость машины? Допускает ли она переход на более быстрый процессор или конфигурацию симметричной многопроцессорной обработки (Symmetric Multiprocessing - SMP)?
  • Какой процент доступной машине памяти используется?
  • Всю ли доступную пропускную способность сети использует система?

В зависимости от ответов на эти вопросы имеются несколько способов повышения производительности и масштабируемости построения.

  • Осуществить модернизацию процессора, диска или памяти.
  • Перегрузить процессы на другие системы.
  • Устранить из системы ненужные процессы.

Повышение производительности тестов

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

  • Время автоматизированного тестирования. Исследуйте средства хронометража, предоставляемые фреймворком тестирования.
  • Для анализа некоторых областей кода проверки используйте инструмент измерения производительности. Кроме того, большинство xUnit-фреймворков предоставляют утилиту, отображающую время выполнения каждого теста.
  • Используйте инструменты инспекции для анализа кода тестов и его сложности.
  • Удостоверьтесь, что ваши юнит-тесты на самом деле являются таковыми, а не тестами компонентов или системы. Это можно выяснить очень быстро, достаточно отключить сетевой кабель, завершить работу базы данных и запустить тестирование. Юнит-тесты пройдут без проблем, а остальные - нет.

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

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

  • Проведите рефакторинг тестов на основании результатов работы инструментов инспекции.
  • Используйте заглушки (stub) или ложные объекты (mock object) для компонентов, слишком сложных для юнит тестирования. Например, ложный объект обычно реализуют для имитации интерфейса доступа к данным.
  • Выделите продолжительные комплексные тесты в отдельные специализированные наборы тестов.
  • Выполняйте тесты параллельно.
  • Запускайте различные виды тестов в соответствии с типом построения: передающее построение, последующие построения, полное интеграционное построение или финальное построение.

Поэтапное построение

Как уже упоминалось, еще один подход снижения продолжительности построения заключается в выполнении облегченного построения, сопровождаемого "тяжеловесным" построением (Фаулер называет это поэтапным построением (staged build): передающее построение, сопровождаемое последующим построением). Рисунок 4.6 иллюстрирует данный подход. При поэтапных построениях вы сначала запускаете "передающее", или облегченное интеграционное построение, в ходе которого интегрируются компоненты программного обеспечения и запускаются проверки модуля, позволяющие обнаружить наиболее очевидные проблемы. Если облегченное построение прошло успешно, запускается более полное интеграционное построение, включающее проверки компонентов и системы, инспекции, а также развертывание. Так реализуется описанная ранее в этой главе практика "ранний сбой построения".


Рисунок 4.6. Процесс поэтапного построения

Исследование инфраструктуры

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

Оптимизация процесса построения

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

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

Инкрементное построение можно применить в нескольких областях. Например, при наличии системы Java с базовыми библиотеками DLL или совместно используемой библиотекой объектных модулей, которая редко изменяется, вполне приемлемо перестраивать такие библиотеки только один раз в день. Фактически, некоторые могли бы оспорить данный подход, заявив, что такие нечасто изменяемые библиотеки DLL и совместно используемые объекты считаются отдельным проектом CI и обращаться к ним стоит как к зависимым элементам проекта.

Раздельное построение компонентов системы

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

Для индивидуального построения компонентов системы создайте отдельные проекты для каждого модуля, который может быть изолирован. Это можно осуществить внутри системы CI, достаточно сделать одну из подсистем главным проектом. Если в один из проектов внесены какие-либо изменения, то на основании зависимостей другие проекты тоже перестраиваются. Рисунок 4.7 демонстрирует типичную компоновку проекта при разделении на дискретные компоненты для ускорения построения.


Рисунок 4.7. Раздельное построение компонентов системы

Повышение производительности инспекции программного обеспечения

Аналогично низкой производительности тестирвания низкая производительность инспекции также может замедлить систему CI. Чтобы выяснить, не тормозит ли инспекция интеграционное построение, используйте следующий список вопросов.

  • Какие показатели используются? Каждый ли показатель важен?
  • Не предоставляют ли два или более инструментов одни и те же показатели? Это может существенно снизить производительность построения.
  • Стоит ли запускать все автоматизированные инспекции при каждом построении? Возможно, некоторые инспекции можно запускать в составе вторичных или периодических построений?
  • Имеются ли инспекции, которые можно запускать лишь для определенных подсистем, а не для всего кода?

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

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

Осуществление распределенного интеграционного построения

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

Существуют инструменты интеграционного построения, которые позволяют объединить мощности нескольких машин. Такие инструменты, как BuildForge и ParaBuild, предоставляют средства для распределения интеграционных построений. Данными способностями обладают и другие серверы CI, например CruiseControl, однако распределенные интеграционные построения - это сложная проблема с еще более сложным решением. Перенос части процесса построения на другую машину может подразумевать копирование больших файлов, что может даже больше замедлить построение. Перед тем как пытаться реализовать этого решение, попробуйте принять все другие меры по уменьшению продолжительности построения.

Переоценка

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

Как это будет работать у вас?

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

"Мой проект насчитывает семь миллиардов строк кода. Как это может сработать у меня?"

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

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

"У меня устаревшая система, что это может мне дать?"

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

"Что, если наш исходный код находится в нескольких хранилищах с контролем версий?"

Этот вопрос зачастую связан с вопросом о распределенной разработке. Давайте предположим, что вы имеете один проект в хранилище Subversion Project Management System (система управления проектом) и другой - в хранилище Financial Management System (система управления финансами). Если изменения внесены в хранилище Financial Management System, то проект в хранилище Project Management System следует перепостроить, поскольку он использует API из хранилища Financial Management System. Ваш сервер CI должен обеспечить вам возможность построения зависимости. Это запуск построения одного проекта в результате инициации построения другого.

"Наш проект распределен географически, так как мы можем заниматься CI?"

Вы имеете группы разработки, функционирующие дистанционно, и испытываете затруднения во внедрении CI? Причиной может быть медленное сетевое подключение или высокая степень защиты интеллектуальной собственности. Большинство серверов CI позволяют использовать зависимости проекта. Предположим, что в Вирджинии разрабатывается проект программного продукта, алгоритмы и некоторые компоненты которого создает группа из Калифорнии. Для хранения специальных алгоритмов компания использует хранилище с контролем версий CVS в Вирджинии. Кроме того, в Калифорнии компания установила новое хранилище Subversion. Технический руководитель в Вирджинии настраивает сервер CI CruiseControl так, чтобы поддерживать оба проекта: один в Вирджинии, второй в Калифорнии. Прежде чем интеграционное построение будет успешно выполнено для группы в Калифорнии, сервер CI запускает его в Вирджинии. Но это будет работать, только если компоненты разделены корректно.

"Мои интеграционные построения занимают слишком много времени!"

См. раздел "Выполняйте быстрое построение" ранее в этой главе.

"У нас очень часты отказы при построении. Мы что-то делаем неправильно?"

Просто вы передаете в хранилище неработающий код! Возможно, он не компилируется, не проходит тестирование или инспекции, либо ваши скрипты базы данных содержат ошибки. Один из способов решения данной проблемы заключается в проведении на машинах разработки закрытых построений (см. главу 2, "Введение в непрерывную интеграцию"), в максимально возможной степени имитирующих среду интеграции, перед передачей изменений в хранилище с контролем версий. Это означает, что каждый разработчик помещает последние изменения в хранилище с контролем версий, добившись успешного прохождения на машине разработки всех тестов, инспекций, а также перепостроения базы данных с проверочными данными. Это также означает, что каждый разработчик должен иметь в среде собственное "пространство", в котором выполняются те же процессы, что и при интеграционном построении. Важнейший принцип, который следует уяснить: избежание больших построений предотвращает ошибки. Это означает, что передаче изменений в хранилище с контролем версий должен предшествовать процесс интеграции и проверки на машине разработчика. Рисунок 4.8 демонстрирует этапы выполнения закрытого построения перед передачей изменений в хранилище.


Рисунок 4.8. Выполнение закрытого построения уменьшает вероятность ошибки интеграционного построения

"Мы не можем предоставить отдельную машину для построения."

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

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

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

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

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

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

Хотя большинство систем управления построением может запускать его для нескольких линий разработки, "интеграционное построение CI" выполняется с основной линией.

Резюме

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

Таблица 4.3 подводит итог практик, описанных в данной главе.

Таблица 4.3

ПрактикаОписание
Автоматизируйте построенияПишите сценарии построения, которые отделены от IDE. Впоследствии они будут выполняться системой CI, чтобы программное обеспечение строилось при каждом изменении в хранилище
Выполняйте построение одной командойС учетом возможности загрузки некоторых инструментальных средств вы можете ввести одну команду и выполнить построение из вашего сценария, включая получение последнего кода и запуск всего построения
Отделяйте сценарии построения от вашего IDEВы должны уметь запускать автоматизированные построения без участия IDE
Централизуйте элементы программного обеспеченияДля уменьшения количества нарушенных зависимостей централизуйте все элементы программного обеспечения. Это снизит вероятность сбойных построений при перемещении на другую машину
Создайте строгую структуру каталогаСоздайте однозначную, логичную структуру каталога, которая облегчит построение программного обеспечения
Ранний сбой построенияЧем быстрее обратная связь, тем раньше устранение проблемы. Выполните операции построения в таком порядке, чтобы действия с наибольшей вероятностью неудачи выполнялись сначала
Осуществляйте построение для каждой средыПроводите на своей рабочей станции то же самое автоматизированное построение, что и на машине интеграционного построения, а при необходимости и для всех других сред
Используйте выделенную машину для интеграционного построенияИспользуйте для выполнения построения выделенную машину. Удостоверьтесь, что в области интеграции не остались прежние элементы
Используйте сервер CIВ дополнение или как альтернативу выполнению ручных интеграционных построений используйте сервер CI, такой как CruiseControl, для автоматического опроса хранилища с контролем версий на предмет изменений и запуска интеграционного построения на отдельной машине
Выполняйте интеграционное построение вручнуюЗапускайте последовательное интеграционное построение вручную, используя автоматизированное построение как способ уменьшения ошибок интеграционного построения. Некоторые применяют данный подход как альтернативу серверу CI
Выполняйте быстрое построениеПостарайтесь довести время интеграционного построения до десяти минут, увеличив ресурсы компьютера, ограничив медленные проверки, ограничив и пересмотрев инспекции и применив поэтапные построения
Поэтапное построениеПрименяйте облегченное "передающее" построение, осуществляющее компиляцию, юнит-тестирование и развертывание, сопровождаемое "последующим", исчерпывающим построением, которое включает проверки компонентов, системы и другие медленные проверки и инспекции

Вопросы

  • Автоматизировано ли ваше построение? Действительно ли вы способны запустить построение без IDE?
  • Сосредоточены ли все ваши элементы программного обеспечения в хранилище с контролем версий? Действительно ли вы способны полностью выполнить построение, получив все необходимые файлы из хранилища с контролем версий?
  • Уверены ли вы, что задачи построения с наибольшей вероятностью сбоя расположены в начале ваших сценариев построения, чтобы вы быстрее получили уведомление об отказе?
  • Имеете ли вы "кнопку <Integrate>" для запуска процессов построения программного обеспечения? Автоматизирована ли интеграция вашей базы данных? Проверка? Инспекция? Развертывание? Получаете ли и используете ли вы обратную связь этих процессов?
  • Происходит ли ваш процесс интеграционного построения на отдельной машине?
  • Какова продолжительность ваших интеграционных построений? Принимаете ли вы меры для его сокращения и улучшения обратной связи?
  • Используете ли вы сервер CI для интеграции программного обеспечения? Или ваш дисциплинированный коллектив осуществляет интеграционные построения вручную?
  • Как часто в вашем проекте выполняются интеграционные построения: еженедельно, ночью или ежечасно? Или непрерывно, при каждом изменении?