Часть 3

ПРИНЦИПЫ ОРГАНИЗАЦИИ гибкой командной работы

Одна из целей этой книги — дать практические рекомендации по выбору эффективных методов и практик разработки программных продуктов. Прежде чем перейти к описанию организации командной работы по гибкой методологии, я расскажу:
  • о главных процессах разработки программных продуктов;
  • о теории процессного и проектного подходов, на которых базируются процессы разработки;
  • о моделях разработки, с помощью которых выполняются процессы;
  • о фреймворке гибкой методологии Scrum и основных заблуждениях, приводящих к неудачам при его внедрении;
  • о том, как осознанный Scrum помогает привести проект внедрения к успеху.
Далее я подробно расскажу о двух инструментах — «Нормативы» и «DevOps», гарантированно повышающих эффективность разработки в 12 раз.

ГЛАВНЫЕ ПРОЦЕССЫ РАЗРАБОТКИ ПРОГРАММНЫХ ПРОДУКТОВ

Главными процессами разработки программных продуктов являются те, при помощи которых создаются ценности для конечного заказчика, то есть посредством которых потребности конечных заказчиков или пользователей преобразуются в работающий программный продукт. Традиционно различают два главных процесса:
  • реализация требований;
  • исправление ошибок.
Процесс исправления ошибок часто называют процессом исправления несоответствий. Под несоответствием понимается несоответствие требованиям. Я рекомендую называть вещи своими именами. Ошибку называть ошибкой, а не использовать термин «несоответствие». Термин «несоответствие» часто используют для ухода от ответственности за ошибки в коде, ссылаясь на нечётко определенные или вновь добавленные требования.

Реализация требований

Требования (англ. requirements) — это исходные данные, на основании которых проектируются и создаются автоматизированные информационные системы (программные продукты). Понятие это довольно широкое, поэтому в книге под требованиями понимаются только те, что формулируют по отношению к продуктам заказчики. Различают функциональные и нефункциональные требования.
Функциональные требования регламентируют поведение системы и отвечают на вопрос: что должна делать система.
Нефункциональные требования, соответственно, регламентируют внутренние и внешние условия или атрибуты работы системы. Например, удобство, надёжность, производительность, определённый программно-аппаратный комплекс, где планируется эксплуатировать систему. Таким образом, нефункциональные требования, по сути своей, ограничивают применимость функциональных требований. Они влияют на выбор архитектурного решения при реализации функционального требования.
Требования выявляют, анализируя потребности заказчика. Одним из способов описания потребностей является вариант использования, или прецедент.
Вариант использования, или прецедент (англ. use cases) — это описание поведения системы, когда она взаимодействует с кем-то (или чем-то) из внешней среды. Он отражает функциональность системы с точки зрения получения значимого результата для пользователя.
Реализация требований — процесс создания автоматизированной информационной системы, удовлетворяющей требования. Под реализацией требований подразумевается процесс реализации функциональных требований к продукту. Реализация требований — одна из основных цепочек создания ценностей, когда требования материализуются в виде части программного продукта, поставляются и развёртываются на стендах заказчика.
Важно понимать: пока материализованное требование не установлено на стенде заказчика и не принято заказчиком — его не существует. Довольно часто разработчики считают, будто, выложив исходный код в соответствующий репозиторий, они уже реализовали требование. Это не так. Даже когда код прошёл все проверки и выпущен конфигурационный элемент, для заказчика требование ещё не реализовано. Так что важно разъяснять разработчикам, что их ответственность за реализацию требования заканчивается после приёмки заказчиком и установки программного продукта на рабочий стенд заказчика.
Почему это важно? Приведу бытовой пример. Я заказываю набор товаров в интернет-магазине. Там заказ сформировали, упаковали, передали в транспортную компанию. С точки зрения интернет-магазина, заказ выполнен, создан и выпущен (упакован) конфигурационный элемент (пакет). Но я — заказчик — его не получил. То есть мои требования по доставке не выполнены. И если пакет мне не доставят, я буду вынужден разбираться с транспортной компанией. Если же интернет-магазин считает заказ выполненным только после доставки пакета, с транспортниками разбирается он, принимая на себя ответственность за проблемы в этом звене цепочки. Следовательно, в первом случае я останусь недовольным заказчиком и, скорее всего, больше в этот интернет-магазин не обращусь, да ещё оставлю негативный отзыв, тем самым снизив базу потенциальных клиентов интернет-магазина. Во втором случае магазин, не теряя потенциальных покупателей, может проанализировать и устранить причины проблем с доставкой. То же самое и при разработке программных продуктов. Разработчик может считать требование реализованным, а заказчик будет недоволен, потому что не получил ожидаемого.
Таким образом, входом процесса реализации требований является потребность заказчика, выраженная в виде вариантов использования или входящего технического задания. На выходе — акт или другой подтверждающий документ о принятом в опытно-промышленную эксплуатацию программном продукте. Принятый в эксплуатацию продукт содержит множество реализованных требований. Между потребностями заказчика и реализованными в программном продукте требованиями должна быть проведена чёткая связь — трасса. Проходя через все этапы процесса реализации требования, мы не должны потерять само требование.
Трассируемостью, или прослеживаемостью требований, называют способность описывать и отслеживать жизненный цикл требования — как от происхождения, через разработку, до последующего развёртывания и использования, так и в течение периодов постоянных доработок на любом из этих этапов. Трассируемость — одна из важнейших характеристик требований, весьма сложная в реализации. С одной стороны, трассируемость требований — это понимание того, как требования высокого уровня декомпозируются в требования низкого уровня, а с другой стороны — это документирование отношений между многими видами артефактов разработки (требования, спецификации, проекты, тестовые сценарии, модели и разработанные компоненты).

РЕАЛИЗАЦИЯ ТРЕБОВАНИЙ

Сложность реализации трассировки требований заключается в том, что, во-первых, нужно отслеживать две пересекающиеся трассы. Одна, связанная с движением по этапам процесса реализации требований, фиксируется в информационных системах, поддерживающих процесс разработки. Вторая трасса проходит по артефактам процесса разработки. На определённых этапах процесса трассы могут разрываться. В таких случаях их восстанавливают через альтернативную трассу — то есть разрыв первой восстанавливается через вторую, и наоборот. Во-вторых, требование может быть многократно декомпозировано на более мелкие. Реализованным требование считается, когда реализованы и приняты заказчиком все его составные части. В примере с интернет-магазином мой заказ может быть разбит на части и доставлен несколькими пакетами в разное время. И будет считаться выполненным, когда все пакеты со всеми товарами будут доставлены. Однако ответственность за исполнение заказа и отслеживание доставки каждого пакета остаётся на интернет-магазине.
Основные этапы процесса реализации требований:
  • сбор и анализ требований;
  • декомпозиция и оценка требований;
  • разработка постановки;
  • кодирование;
  • тестирование требований;
  • выпуск;
  • развёртывание;
  • сдача заказчику;
  • запуск в опытно-промышленную эксплуатацию.
Важно понимать, что процесс реализации требований регламентирует ряд моментов:
  • что должно быть сделано на каждом шаге;
  • какая производственная роль отвечает за данный шаг;
  • какие артефакты должны быть созданы на каждом шаге.
Причём процесс не регламентирует, как это должно быть сделано и когда. На эти вопросы отвечает выбранная методология разработки в соответствии с этапами проекта. В книге даётся ответ на вопрос, как разрабатывать требования в рамках разработанной в компании «Диасофт» методологии осознанного Scrum. Этапы процесса реализации требований могут выполняться в рамках спринта в любом порядке, часто параллельно, например, «разработка постановки», «кодирование», «тестирование», «выпуск» и «развёртывание». Зато между этапами «регистрация» и «разработка постановки» может пройти и существенное время, например, несколько месяцев.
Подробнее остановлюсь на каждом этапе процесса реализации требований.

РЕАЛИЗАЦИЯ ТРЕБОВАНИЙ

Главными процессами разработки программных продуктов являются те, при помощи которых создаются ценности для конечного заказчика, то есть посредством которых потребности конечных заказчиков или пользователей преобразуются в работающий программный продукт.
СБОР И АНАЛИЗ ТРЕБОВАНИЙ
Сбор и анализ требований выполняют аналитики. И начинается всё с выявления потребностей заказчиков, то есть с поиска ответов на следующие вопросы:
  • Какие цели и задачи планирует решить заказчик?
  • Каким образом и в рамках каких бизнес-процессов он планирует использовать решение?
  • Какие роли будут использовать решение?
  • Какие есть ожидания от работы решения?
  • В какой IT-ландшафт должно быть встроено решение?
На основании полученных ответов аналитик формирует список изменяемых бизнес-процессов заказчика, вариантов использования, бизнес-требований и части нефункциональных требований.
Выявление требований отвечает на такие вопросы:
  • Что должно делать решение?
  • Какие данные решение должно получать из сторонних систем и передавать им?
Аналитик формирует спецификацию требований и матрицу их трассировки. Матрица трассировки требований устанавливает связь между функциональным требованием и бизнес-процессом заказчика, бизнес-требованиями, вариантами использования и сценариями тестирования, по которым заказчик будет его принимать. Качество и количество собранных требований напрямую зависит от квалификации аналитика и уровня зрелости заказчика.
Теперь несколько слов о «подводных камнях», ожидающих аналитика, и о том, как они влияют на количество и качество самих требований.

Cбор и анализ требований

  • Описываемые заказчиком бизнес-процессы по факту не разработаны, не утверждены и, следовательно, не внедрены. Заказчик сам не знает, чего хочет, и поэтому формулирует не потребности, а желания, что приводит к завышенным ожиданиям от решения и постоянным изменениям требований. После утверждения бизнес-процесса требования к решению могут существенно измениться.
  • Результатом внедрения решения может стать оптимизация существующего процесса, что может лишить исполнителей части их ответственности или вовсе ведёт к их увольнению. Это приводит к саботажу со стороны исполнителей, они могут выдвигать необоснованные требования, чтобы предотвратить внедрение решения.
  • Формальное отношение заказчика к согласованию требований. Заказчик, заинтересованное в результатах проекта лицо, согласовывает требования, только увязав их с исполнителями, участниками автоматизируемых бизнес-процессов. Это может привести к формулированию исполнителями не требуемого, а желаемого (зачастую завышенного) результата.
  • Требование может быть или излишне высокоуровневым, или излишне детализированным.
Мероприятия, снижающие риски сбора требований:
  • Требования нужно собирать, опираясь на готовое (или похожее на ожидаемое) решение. Сначала — продемонстрировать заказчику работу существующего решения и лишь затем фиксировать требования в виде изменений к нему.
  • Необходимо проверять требование на соответствие решаемым задачам, устанавливая прямую связь между требованием и потребностями. Для этого рекомендую использовать забавную конструкцию «чтобы что». Люди часто путают «зачем?» и «почему?». Чтобы такого не было, используйте волшебную фразу: «чтобы что?». В ней — более отчётливое целеполагание, и на неё нельзя ответить «потому что», как ни старайся. Например, «нужно выполнить эту задачу, чтобы что?». Каждый раз, когда в ответе вместо цели называют причину, не принимайте его, просите указать именно цель.
  • Если трудоёмкость реализации требования превышает 30 человеко-дней, его следует декомпозировать, чтобы каждое дочернее требование не превышало 30 человеко-дней.
Аналитик и тестировщик формируют программу и методику испытаний. Программа и методика испытаний (ПМИ) содержит данные (характеристики, параметры, свойства, показатели), подлежащие проверке при испытаниях решения, а также сценарии тестирования требований.
Аналитик регистрирует в системе автоматизации управления проектом (например, в Digital Q.Tasks или Atlassian Jira) все требования в виде задач специального типа — в разрезе создаваемых или дорабатываемых PBC. В каждом требовании описывает краткую концепцию, включающую всё, что нужно реализовать, и список типовых работ, например: пять микросервисов с типом «бизнес-объект», семь форм редактирования и прочее. Аналитик привязывает к задачам ссылку на спецификацию требований, матрицу трассировки требований и ПМИ. Аналитик совместно с архитектором продукта оценивает трудоёмкость реализации каждого требования и устанавливает её в задаче как плановую.
Требования выявляются в проекте на этапе обследования или в рамках заказа дополнительной доработки программного продукта — заказной доработки.
ДЕКОМПОЗИЦИЯ ТРЕБОВАНИЙ
Аналитик декомпозирует целое требование на самостоятельные функциональные части и регистрирует дочерние задачи на каждую часть. Количество необходимых уровней декомпозиции зависит от размера исходного требования и архитектуры решения. В нашем случае верхний размер требования ограничен трудоёмкостью реализации в тридцать человеко-дней. Декомпозицию рекомендуется делать в два шага. На первом — до уровня отдельно выпускаемых компонентов: PBC и микросервисов. На втором шаге нужно оценить трудоёмкость каждой составной части, и если она превышает десять человеко-дней, следует продолжить декомпозирование на самостоятельные функциональные части, пока трудоёмкость каждой не будет меньше десяти человеко-дней, а лучше — два-три человеко-дня. Такая задача гарантированно поместится в один спринт. Её не придётся дробить (например, сделать постановку в первом спринте, а всё остальное — во втором). Сумма трудоёмкости всех декомпозированных частей не должна превышать трудоёмкость исходного требования. Если же и она превышена, необходимо рассмотреть альтернативные архитектурные решения.
При декомпозировании требований следует избегать определённых ошибок.
  • Делить на технические части, вместо функциональных. Например, декомпозирование по виду работ:
  • создать постановку;
  • закодировать;
  • создать тестовые сценарии;
  • протестировать.
Это — пример неправильного декомпозирования, когда аналитики путают декомпозицию требования с декомпозицией задачи.
  • Оставлять задачи с трудоёмкостью свыше десяти человеко-дней. Практика показывает: если задача оценена в десять или более человеко-дней, аналитик и архитектор не знают, что будет сделано. Они не представляют себе полный набор работ. Как следствие, с высокой вероятностью реальная трудоёмкость окажется выше. А вот работу в два-три человеко-дня сотрудники способны оценивать реалистично.

Декомпозиция требований


РАЗРАБОТКА ПОСТАНОВКИ
Постановка (задачи) — имеется в виду описание задачи по определённым правилам, которое даёт исчерпывающее представление о её сущности, логике преобразования информации для получения результата. На основе постановки задачи программист должен представить логику её решения и рекомендовать стандартные программные средства, пригодные для её реализации.
Аналитик подробно описывает, что нужно реализовать согласно требованию. Оформляет и сохраняет документ с описанием в базе знаний (например, Atlassian Confluence), затем согласовывает детали постановки с программистом и тестировщиком. Так выглядит широко распространённый процесс разработки постановки. Мы в «Диасофт» разработали и внедрили другой, на наш взгляд, более эффективный процесс — без документа «постановка задачи».
Кому нужен документ «постановка задачи»? Странный вопрос. Очевидно, программисту — для разработки и кодирования логики задачи, тестировщику — для разработки тестовых сценариев проверки требования, техническому писателю — для актуализации пользовательской документации по требованию и, конечно, самому аналитику и его коллегам — для сохранения общей логики работы системы и ответов на вопросы пользователей о деталях реализации программного продукта.
Получается, документ нужен исключительно для внутреннего использования. Иными словами, его разработка противоречит сразу нескольким принципам построения эффективного производства. С точки зрения процессного подхода, документ «постановка задачи» как промежуточный результат цепочки создания ценности «реализация требований» не дотягивается до клиента. С точки зрения манифеста гибкой методологии, «работающий продукт должен быть важнее исчерпывающей документации». А в существующем процессе наоборот: сначала разрабатывается исчерпывающая документация, а затем работающий продукт.
В результате мы получаем две версии правды о программном продукте: одна в виде внутреннего документа, на который ориентируются сотрудники компании, и вторая в виде пользовательской документации, на которую ориентируются клиенты. Из-за расхождения этих двух версий появляются разногласия в понимании работы программного продукта клиентом и внутренними службами компании, что выливается в дополнительный поток обращений со стороны клиентов за консультациями и исправлением ошибок.

Разработка постановки

Можно ли отказаться от разработки документа «постановка задачи» без ущерба качеству процесса реализации требований? Как ни странно, вполне. Аналитик должен разработать единственный документ, который будет востребован и клиентом, и внутренними потребителями. Этот документ называется «описание для пользователя». То есть аналитик вместо описания для программиста делает описание для пользователя. Пользовательская документация формируется автоматически — простым включением в неё описания требований для пользователя.
Эффективный процесс разработки постановки выглядит следующим образом:
  • Аналитик совместно с программистом и тестировщиком согласовывают понимание того:
  • что нужно сделать — список типовых работ;
  • как и какими стандартными программными средствами реализовать решение;
  • какие части задачи проработать детальнее.
Этот шаг решает сразу несколько задач. Во-первых, аналитик будет описывать согласованное с программистом решение (вместо своего личного видения, которое может измениться в процессе согласования). Это уменьшает затраты на создание постановки, связанные с переписыванием. Во-вторых, программист и тестировщик вполне могут начать работу по задаче, не дожидаясь полного её описания. В-третьих, аналитик будет детально прорабатывать только те части задачи, которые требуются программисту или тестировщику.
  • Аналитик по заранее определённому шаблону разрабатывает документ «описание для пользователя» — в том виде, в каком его будет читать клиент, как будто решение уже разработано. В документе он описывает:
  • как с продуктом должен работать пользователь;
  • как пользователь может самостоятельно проверить правильность полученного результата и разобраться в причинах, если результат не соответствует ожиданию;
  • как методолог может подстроить продукт под требования бизнеса;
  • какие допуски может выдать администратор безопасности;
  • при помощи какого программного интерфейса разработчик сторонней организации или смежного продукта может реализовать взаимодействие с продуктом.
Изложенный процесс разработки постановки обладает выраженным положительным эффектом.
  • Вместо нескольких документов «постановка задачи», «описание изменений к продукту», «пользовательская документация», готовится один документ — «описание для пользователя».
  • Зная, что документ будет читать клиент, аналитик подходит к нему более ответственно, следовательно, он выходит с меньшим количеством орфографических, грамматических и стилистических ошибок. А это, кроме всего прочего, упрощает и понимание документа программистом и тестировщиком, уменьшая вероятность неправильного понимания реализуемого решения.
  • Когда аналитик фокусируется на том, как будет использовано разрабатываемое решение, а не только на том, что должно быть сделано, само решение получается более простым и удобным для пользователя.
  • Поскольку документ должен быть компактным и информативным, аналитик тратит на его создание меньше времени.
  • Существенно сокращается потребность в технических писателях.
КОДИРОВАНИЕ
Далее процесс развивается обычным образом. Программист кодирует решение в соответствии с принятыми в компании стандартами разработки программных продуктов и оформления кода, покрывает новый код unit-тестами, после чего выкладывает код и unit-тесты в распределённую систему управления версиями кода (СВК), например, в Git.
Наличие стандартов разработки программных продуктов и оформления кода позволяют повысить эффективность работы.
  • Существенно сократить объём постановочных документов, поскольку больше не нужно описывать в постановке то, что уже есть в стандарте.
  • Разные программные продукты, разработанные разными программистами для клиента, будут выглядеть одинаково и создавать впечатление целого.
  • Код, разработанный разными программистами в разное время, будет выглядеть единообразно, что существенно упрощает как его чтение, так и понимание всеми программистами. Даже свой код сложно читать и понимать, скажем, спустя год и более, если не придерживаться стандарта оформления кода и его комментирования.
  • Исключение грубых ошибок путём исключения из практики потенциально опасных конструкций.
  • Разрабатывать программу гораздо проще, если общие вопросы построения программных продуктов оговорены изначально.
  • Адаптация новых программистов происходит существенно проще и быстрее.
  • Воспитывает культуру разработки.
Стандарты, необходимые для разработки программных продуктов, известны, но есть смысл посвятить им несколько специальных абзацев.
  • Архитектурные принципы представляют собой фундаментальные «аксиомы» (другими словами, правила), которые используются в качестве «отправных точек» для принятия архитектурных решений.
  • Стандарт технологического стека — принятый набор технологий для создания продукта. Он включает выбранные языки программирования (например, Java, TypeScript), основные используемые фреймворки (к примеру, Angular, ReactJS, Spring), используемые СУБД (например, PostgreSQL), среды управления контейнерами и т. д. Инструменты и технологии, избранные как часть основного стека, сильно влияют на способность компании быстро разрабатывать и развёртывать код и легко масштабироваться. Крайне важно выбрать технологический стек с технологиями, имеющими большое сообщество с открытым исходным кодом, чтобы ваша команда программистов могла опираться на знания и опыт других.
  • Архитектурный шаблон продукта — референсная реализация продукта, полностью соответствующая принятым в компании стандартам. Стандарты могут содержать очень много правил, которые сложно удержать в голове, поэтому наличие продукта, который можно взять за основу (или просто посмотреть, как реализовано) существенно упрощает их понимание. Кроме того, архитектурный шаблон позволяет проверить и убедиться, что стандарты обладают полнотой и на их основе возможно создать продукт необходимого качества.
  • Стандарт пользовательского интерфейса — список утверждённых графических элементов пользовательского интерфейса, используемых при проектировании, руководство по стилю и визуальному отображению, шаблоны типовых диалоговых форм, руководство по способу взаимодействия пользователя с интерфейсом и рассказывающее о том, какие шаги ему нужно предпринять, чтобы что-то сделать.
  • Стандарт программного интерфейса (API) — набор определений, правил, протоколов и инструментов для разработки и управления контрактами, которые предоставляет программный продукт.
  • Стандарт оформления кода — набор правил и соглашений, используемых при написании исходного кода на выбранном языке программирования. Стандарт должен основываться на принятых для этого языка конвенциях по оформлению кода. Программист, имеющий опыт разработки, например, на языке Java, должен легко адаптироваться к принятым в компании правилам.
  • Стандарт именования методов и переменных (нейминг) — правила именования классов, методов и переменных. Как правило, является частью стандартов по API и оформлению кода. Обращаю внимание: он должен быть обязательно, и следование ему критически влияет на качество разрабатываемого программного кода, его простоту и понятность. Чем проще и яснее код, тем легче его прочитать и понять, и тем меньше ошибок в нём будет. Снова повторю — код должен читаться, как книга. Названия классов, методов и переменных должны быть осмысленными, то есть отражать их конкретную суть и назначение.

Кодирование

Чтобы стандарты стали действительно рабочим инструментом, а не вывеской, принципиально важно сделать их компактными, визуальными и структурированными. Программисты не станут читать многостраничные документы, а если и прочтут, то всё не смогут запомнить. Лучше один раз увидеть, чем читать многостраничные документы.
Программист должен ежедневно или чаще выкладывать работоспособный код в систему управления версиями кода. То, зачем ежедневно сохранять результаты своего труда, обычно вопросов не вызывает. Хотя бы для того, чтобы предотвратить потери от сбоя оборудования или, что происходит чаще, от непредвиденного невыхода программиста на работу, например, по болезни. А вот с определением того, какой код считать работоспособным, как правило, возникают трудности, поскольку единого понимания здесь нет.
Работоспособный код удовлетворяет определённым критериям.
  • Не приводит к ошибкам сборки программного продукта.
  • Не провоцирует ошибки выполнения unit-тестов продукта.
  • Не ведёт к ошибкам выполнения функциональных, нагрузочных и других автоматических тестов, включённых в конвейер непрерывного тестирования.
  • Не приводит к обнаружению критических и блокирующих ошибок средствами статического контроля качества кода.
  • Основная или задуманная логика по требованию работает.
  • Основные бизнес-процессы продукта работают, даже если не покрыты автоматическими тестами.
  • Не приводит к серьёзной деградации производительности.
  • Не приводит к неконтролируемому росту объёмов базы данных.
  • Не приводит к ошибкам автоматического развёртывания программного продукта.

BPMN 2.0 – МЕТАМОДЕЛЬ И НОТАЦИЯ БИЗНЕС-ПРОЦЕССОВ

Чтобы стандарты стали действительно рабочим инструментом, а не вывеской, принципиально важно сделать их компактными, визуальными и структурированными. Программисты не станут читать многостраничные документы, а если и прочтут, то всё не смогут запомнить.
Как программисту кодировать, чтобы код отвечал указанным выше критериям, подробно изложено в главе о культуре разработки.
При написании unit-тестов для каждой функции или метода программист использует специальный фреймворк, зависящий от языка программирования (к примеру, для Java используется Junit).
Одним из способов проверки качества unit-теста, как и качества кода, является рецензирование кода. Программист выкладывает код в свою персональную ветку в системе управления версиями кода. По завершении реализации задачи отдаёт свой код и unit-тесты на него на рецензию другим программистам.
ТЕСТИРОВАНИЕ ТРЕБОВАНИЙ
Тестирование программных продуктов — проверка соответствия между реальным и ожидаемым поведением программы, проводится на конечном наборе тестов.
Тестировщик описывает тестовые сценарии и выкладывает их в систему управления тестовыми сценариями, например, Squash TM. На их основе он записывает функциональные тесты для тестирования графического интерфейса пользователя (англ. graphical user interface, GUI) при помощи Selenium, программного интерфейса (англ. application program interface, API) при помощи Postman, выкладывает код автоматических тестов в систему управления версиями кода Git.
Тестовый сценарий, или тест-кейс — это чёткое описание действий, которые необходимо выполнить, для того чтобы проверить работу программы (поля для ввода, кнопки и т. д.). Это описание содержит:
  • предусловия — действия, которые надо выполнить до начала проверки;
  • шаги — действия, которые надо выполнить для проверки;
  • ожидаемый результат — описание того, что должно произойти после выполнения действий для проверки.
Функциональное тестирование является одним из ключевых видов тестирования, его задача — установить соответствие разработанного программного продукта исходным функциональным требованиям. Различают следующие виды функционального тестирования:
  • Тестирование программного интерфейса (англ. API testing) — функциональная проверка соответствия программного интерфейса заявленному контракту.
  • Тестирование пользовательского интерфейса (англ. GUI Testing) — функциональная проверка соответствия требованиям через пользовательский интерфейс.
  • Тестирование безопасности (англ. Security and Access Control Testing) — стратегия тестирования, используемая для проверки безопасности системы и для анализа рисков, связанных с обеспечением целостного подхода к защите приложения от атак хакеров, вирусов, несанкционированного доступа к конфиденциальным данным и функциям.
  • Тестирование взаимодействия (англ. Interoperability Testing) — проверяет способность приложения взаимодействовать с одним и более компонентами или системами.

Тестирование требований

Тестирование выполняется тестировщиком как вручную, так и автоматизированно, путём записи программы (автотеста), которая проверяет правильность другой программы.
Главный плюс автоматических тестов заключается в том, что они выполняются намного быстрее ручного тестирования и их можно запускать после каждого изменения в коде. Когда время выполнения тестов становится слишком длительным, следует разделить запуск тестов на несколько потоков. Это позволит существенно сократить срок их выполнения. Для двух потоков — в два раза, для трёх — в три и т. д.
Однако при написании автоматических тестов легко упустить из виду оценку их экономической целесообразности, а между тем их разработка значительно дороже ручного тестирования. Прежде чем её широко использовать, следует разумно оценить трудоёмкость реализации автотеста по отношению к ручному прохождению теста, поскольку она может быть выше в двадцать-двадцать пять раз. То есть для автоматизации теста, который тестировщик вручную проходит за час, может потребоваться до двадцати пяти часов. Очевидно, что автотестирование имеет экономический смысл, только если мы планируем запускать тест более двадцати пяти раз. Поэтому сначала нужно привести технологию написания автотеста к приемлемой стоимости. Приемлемой можно считать такую технологию записи автотестов, когда её трудоемкость превышает ручной вариант не более чем в шесть-восемь раз. В компании «Диасофт» применяется технология записи автотестов, которая превышает ручное прохождение теста в три-четыре раза. Таким образом, уже при пяти запусках теста будет выгодно иметь автотест.
Дополнительный плюс автоматического тестирования связан со строгим следованием рекомендации — запускать тесты на выделенном для этого стенде. При ручном варианте у тестировщика возникает соблазн проверить функционал на стенде разработки, это часто приводит к тому, что функционал не будет должным образом работать у клиента. Выделенный стенд тестирования позволяет избежать следующих ошибок.
  • Программист не выложил часть изменений кода.
  • Программист выложил новый код относительно проверенного на стенде разработки.
  • Программист не выложил изменения дистрибутивных данных.
  • На стенде разработки заведены новые или изменены настройки, которые надо повторить для работы функционала на целевом стенде.
Один из главных показателей качества тестирования — степень покрытия кода программного продукта функциональными тестами. Чем больше степень покрытия, тем лучше. Какую степень покрытия кода тестами можно считать достаточной? Непростой вопрос.
Большое количество тестов бывает как благом, так и злом. Сработавший тест может показать и наличие ошибки в коде, и наличие её в тесте. Чем больше тестов, тем выше вероятность ложных срабатываний. Поэтому тесты нужно писать так, чтобы возможно было легко и просто локализовать место в коде, приводящее к срабатыванию теста. Самыми простыми с точки зрения локализации места в коде являются unit-тесты. К тому же их относительно просто разработать. Именно поэтому их стоит писать как можно больше. Следующими по объёму идут тесты контрактов программного интерфейса. Главное их достоинство — устойчивость к изменениям и малая вероятность ложных срабатываний. Наименее устойчивы к изменениям тесты графического интерфейса пользователя. Поэтому их рекомендуется разрабатывать после стабилизации пользовательского интерфейса продукта.

Виды тестирования

Важно понимать, что даже стопроцентное покрытие кода тестами не гарантирует отсутствия ошибок в коде. Поведение программы сильно зависит от контекста, в рамках которого она работает. Но вносить изменения в код будет намного безопаснее. Изменения кода, приводящие к изменению поведения программы, мгновенно выявляются тестами.
При тестировании требований следует придерживаться рекомендаций, повышающих эффективность процесса.
  • Начинать создание тестов нужно с наиболее важного функционала. Важным считается функционал, который будет использоваться часто и/или наибольшим количеством пользователей.
  • Трудоёмкость написания автотеста не должна превышать ручной вариант более чем в шесть раз.
  • Тесты должны быть по возможности самодостаточными, не зависеть от настроек стенда и результата исполнения предыдущих тестов. На практике соблюдение этой рекомендации может привести к необоснованному росту сложности и трудоёмкости написания теста. Наличие стенда с эталонными настройками существенно снижает стоимость написания тестов. Допустимо даже создавать цепочку, когда каждый следующий тест базируется на результатах предыдущего, если это упрощает их написание. Например, при тестировании шагов бизнес-процесса можно создать такую цепочку тестов:
  • «открыть вклад»;
  • «внести сумму во вклад»;
  • «причислить проценты ко вкладу»;
  • «вывести остаток со вклада»;
  • «закрыть вклад».
Создание приведённой цепочки тестов обойдётся значительно дешевле реализации набора независимых тестов. Важно, чтобы время исполнения цепочки не превышало десяти минут. Поэтому быстро приводить стенд в исходное состояние важнее, нежели делать тесты полностью самодостаточными.
  • Технология записи и исполнения автоматических тестов должна обеспечивать запись различных метрик (например, время выполнения теста и его частей). Наличие метрик позволяет, кроме проверки функциональных требований, проверять и нефункциональные. Если время выполнения теста увеличилось более чем на 10% от среднего срока прохождения, это сигнал о деградации производительности.
  • Тестовые данные должны соответствовать функционалу как по бизнесу, так и по формату. Экранные формы с введёнными в них тестовыми данными должны быть максимально похожими на реальные и не содержать ошибок. В результате будет не стыдно:
  • использовать скриншоты экранных форм с тестовыми данными для документации;
  • приглашать на демонстрацию внешних заказчиков;
  • использовать тестовый стенд для проведения демонстраций потенциальным клиентам.
Если специально не уделять этому вопросу внимания, то тестовые данные будут представлять собой либо случайный набор символов, например, «йцукен», либо пример излишне креативного и неуместного мышления.
  • Тестовые сценарии должны базироваться на сценариях программной методики испытаний.
ВЫПУСК
Выпуск — утверждённая в компании процедура проверки программного продукта, пройдя которую, продукт получает доступ на установку в промышленной среде. Современные тренды в разработке программных продуктов предполагают полностью автоматизированную процедуру выпуска после каждого изменения кода продукта. Зрелость процессов разработки определяется количеством успешных выпусков продуктов в день. Чем чаще выпуск, тем быстрее обнаруживается и исправляется ошибка и тем меньше вероятность, что она станет источником новых, наведённых ошибок. Поэтому чем чаще выпуск, тем меньше ошибок в программном продукте.
Обычно компании по разработке программных продуктов делают один выпуск в полгода или в год. Лидеры делают по несколько тысяч выпусков в день. Например, Google — 5500, а Amazon — невероятные 23 000. В компании «Диасофт» — 2500 успешных выпусков релизов программных продуктов в день.
В рамках процедуры выпуска исходный код программного продукта преобразуется в исполняемый код и упаковывается в конфигурационный элемент поставки — релиз программного продукта, например, в архив с docker-образом. Собранный и прошедший процедуру выпуска конфигурационный элемент размещается в специальном репозитории выпущенных программных продуктов, к примеру, Docker Registry.
РАЗВЁРТЫВАНИЕ
Развёртывание — это действия, которые делают программный продукт доступным для использования. Оно представляет собой сложнейший и критически важный процесс доставки, установки и обеспечения доступа к продукту пользователей или программных систем. От того, насколько автоматизирован процесс развёртывания, зависит срок, за который изменение станет доступно пользователю. Чем выше автоматизация и чем меньше ручных действий, тем меньше времени требуется. Чтобы достигнуть времени в несколько минут от размещения изменения в кодовой базе до доставки его до рабочей среды, требуется стопроцентная автоматизация процесса. По-крупному, процесс развёртывания состоит из двух этапов.
  1. Доставка нового релиза программного продукта, содержащего изменение, до рабочего стенда организации. В процессе доставки новый релиз последовательно (а в каких-то случаях параллельно) устанавливается на тестовые, предрабочий и рабочий стенды. В крупных организациях бывает несколько рабочих и, соответственно, предрабочих стендов. Предрабочий стенд представляет собой полную копию рабочего: по составу оборудования, установленного системного и прикладного программного обеспечения, включая копии всех интегрируемых систем, а также полную копию данных в этих системах. Стенд используется для проведения финальных проверок продукта в условиях, максимально приближенных к рабочим.
  2. Обеспечение доступа групп пользователей и программных систем к установленному новому релизу. Современные контейнеризированные программные решения допускают параллельную работу нескольких релизов одного продукта. Установленный на рабочий стенд новый релиз не сразу доступен для использования. Пользователи продолжают работать с предыдущим релизом. Сначала включают доступ к новому релизу ограниченной группе пользователей. Если в течение определённого срока нет нареканий на работу функционала, к нему подключаются следующие группы, пока все пользователи не будут переключены на новый релиз. Только после этого предыдущий релиз отключают и удаляют со стенда. Приведённый способ развёртывания снижает вероятность массовой остановки рабочих процессов организации — за счёт предварительной локализации действия изменений на специальной группе пользователей.

Развёртывание

Как видно из описания, в процессе развёртывания новый релиз программного продукта устанавливается на множество стендов. Для понимания сложности процесса приведу пример развёртывания приложения, построенного в микросервисной архитектуре.
Что же представляет собой релиз программного продукта? Типовое приложение включает графический интерфейс пользователя, бизнес-логику и базу данных. Для простоты примера предположим, что приложение (PBC) поставляется в виде трёх docker-образов. В одном размещаются html-страницы диалоговых форм графического интерфейса пользователя, во втором — исполняемый код, реализующий бизнес-логику приложения, в третьем — код создания структуры базы данных и первичного наполнения справочными данными. В реальной жизни число поставляемых docker-образов в рамках одного приложения много больше трёх. И само по себе приложение работать не будет. Для работы ему нужно специфическое программно-аппаратное окружение — стенд.
Современное программно-аппаратное окружение представляет собой своего рода слоёный пирог, который включает следующее.
  • Аппаратное обеспечение:
  • отдельные физические серверы или ЦОД (центр обработки данных);
  • системы хранения данных (СХД);
  • различное сетевое оборудование.
  • Средства виртуализации. Виртуализация серверов — процесс, когда с помощью ПО множество физических серверов разделяется на уникальные и логически изолированные виртуальные серверы.
  • Инфраструктурное программное обеспечение:
  • операционные системы (ОС), например, CoreOS, операционная система с открытым исходным кодом на базе ядра Linux;
  • системы управления базами данных, например, PostgreSQL;
  • платформы управления контейнерами, например, Kubernetes;
  • брокеры сообщений, например, Apache Kafka;
  • платформы управления сетью микросервисов, например, Istio;
  • HTTP-сервер, например NGINX.
  • Прикладные программные продукты. Приложения (PBC), необходимые для корректной работы развёртываемого решения. В зависимости от сложности его архитектуры число наименований прикладных продуктов может варьироваться от единиц до сотен и даже тысяч штук.
Каждый элемент «пирога» имеет семейство возможных вариантов реализации. Вместо PostgreSQL могут использоваться другие промышленные СУБД, например, MS SQL Server. Важно знать не только имя продукта, но и его версию. Полная спецификация установленных на стенд программных продуктов с указанием их версий называется конфигурацией стенда. В зависимости от назначения стенда конфигурации различаются. Например, для модульного тестирования используется одна конфигурация, для нагрузочного тестирования другая. О конфигурациях нужно не только знать, ими необходимо управлять. Для этого предназначены специальные базы данных управления конфигурацией (англ. configuration management database, CMDB), например, Digital Q. CMDB от компании «Диасофт».
Постоянно иметь в развёрнутом, готовом к использованию состоянии всё множество стендов — не слишком разумное использование серверной инфраструктуры. Большинство стендов создаётся по мере необходимости — на основании информации о конфигурации из CMDB — и удаляется после использования. Чтобы упростить создание стендов, используется подход «платформа как сервис» (англ. Platform as a Service, PaaS). Например, платформа управления контейнерами Kubernetes позволяет быстро создавать изолированное окружение для запуска приложений. Это существенно упрощает управление стендами.
Организуя процесс развёртывания программного продукта, важно учитывать одно из основных требований — обеспечение непрерывности бизнеса. Во-первых, любой сбой в процессе развёртывания приложения или в нём самом не должен приводить к остановке бизнес-процессов организации. Во-вторых, штатная замена версии приложения не должна приостанавливать какой-либо процесс в организации. Замена должна производиться незаметно для пользователей — без технологических окон и без ручных административных действий. Используется подход «инфраструктура как код» (англ. Infrastructure as code, IaС). Точно так же необходимые для работы приложения настройки должны устанавливаться автоматически с помощью подхода «настройка как код». Если на любом из этапов происходит сбой, все стенды, которые были изменены, приводятся в исходное состояние.

СХЕМА РАЗВЁРТЫВАНИЯ:

СДАЧА ЗАКАЗЧИКУ
DevOps-инженер устанавливает на тестовый стенд заказчика набор сервисов с реализованным требованием. Если предполагается многократная установка сервисов на один тестовый стенд, рекомендую включить этот шаг в CI/CD-конвейер для автоматической установки или обновления сервисов. Как правило, чтобы решение можно было использовать, его нужно предварительно настроить, например, внести справочные данные или настроить определённые шаги бизнес-процесса. Настройки можно сделать вручную, но в зрелых организациях настройки упаковываются в пакеты, проходят стандартную процедуру выпуска, как программные продукты, и устанавливаются на целевой стенд автоматически.

Сдача заказчику

Это гарантирует идентичность настроек на тестовом стенде и на промышленном, что позволяет избежать многих ошибок, связанных с ручным вводом, например:
  • часть настроек сотрудник забыл ввести;
  • часть настроек выполнена способом, отличным от того, что использовался при сдаче заказчику; разница в способе настройки затруднит локализацию ошибок и может стать источником наведённых ошибок, связанных с последующими изменениями настроек;
  • сотрудник может случайно или даже намеренно испортить настройки другого работающего решения, что приведёт к существенным финансовым или репутационным потерям.
Чтобы исключить подобные ошибки (особенно опасность неправомерных изменений на рабочем стенде), в зрелых организациях запрещён доступ к любым ручным административным действиям. Доступ запрещён не только к рабочему, но и к любому другому стенду, содержащему реальные персональные данные клиентов.
Ответственный за внедрение сотрудник вручную или с помощью автоматических тестов проверяет работоспособность решения согласно утвержденным ПМИ, а затем сдаёт его заказчику. Чтобы минимизировать вероятность появления дополнительных требований в процессе сдачи, разумнее проводить предварительные демонстрации решения заказчику, когда требование ещё находится в разработке и стоимость внесения изменений невысока. После принятия заказчиком положительного решения DevOps-инженер размещает контейнер с сервисом и пакет настроек в репозитории продуктов, готовых к установке на рабочий стенд. В зрелых организациях контейнеры с сервисом и пакеты настроек автоматически устанавливаются на рабочий стенд сразу после успешной процедуры выпуска. Однако доступ к функциональности предоставляется ограниченной группе лиц. Сдача-приёмка решения заказчику осуществляется на рабочем стенде.
Обнаруженные в процессе сдачи функционала ошибки инженер по внедрению регистрирует в специальной системе отслеживания ошибок (англ. bug tracking), например, Digital Q. Tasks или Atlassian Jira. Для управления динамикой исправления ошибок используется график сходимости дефектов.
График сходимости дефектов/ошибок позволяет оценить динамику заведения новых ошибок, исправления ошибок и визуализировать общее число зарегистрированных и исправленных ошибок.
ЗАПУСК В ОПЫТНО-ПРОМЫШЛЕННУЮ ЭКСПЛУАТАЦИЮ
После того, как принято решение о запуске в опытно-промышленную эксплуатацию, продукт устанавливается на предрабочий стенд (PREPROD). Как уже сказано, это — полная копия рабочего стенда по всем параметрам. На нём выполняется набор автоматических тестов, цель которых — гарантировать, что устанавливаемые продукты не приведут к остановке бизнес-процессов компании или к существенной деградации производительности системы. После успешного прохождения тестов продукт устанавливается на рабочий стенд (PROD). В ходе опытной эксплуатации доступ к функциональности имеет ограниченная группа лиц, а затем, при положительной динамике, их число постепенно увеличивается. Такой способ называется «канареечным» развёртыванием.

ЗАПУСК В ОПЫТНО-ПРОМЫШЛЕННУЮ ЭКСПЛУАТАЦИЮ

Исправление ошибок

Исправление ошибок — второй по значимости после реализации требований процесс. От того, как он организован, зависит, насколько быстро и незаметно для пользователя будет реализовано и установлено исправление. В современной разработке программных продуктов бытует мнение, будто бы неважно, сколько сделано ошибок при реализации требований, главное — их быстро выявлять и устранять. Как уже и говорил, я в корне не согласен с таким подходом. Нужно делать и то, и другое. Если не задумываться о снижении числа ошибок при реализации требований, их поток захлестнёт разработчиков, и они станут всё своё время тратить на исправление очередных ошибок, а не на реализацию новых требований. Причём исправление ошибок будет порождать очередную их порцию. Поэтому принципиально важно организовать процессы так, чтобы, во-первых, минимизировать число ошибок при реализации требований, а во-вторых, быстро выявлять и устранять обнаруженные ошибки.
В первую очередь процесс исправления ошибок должен обеспечить выполнение SLA. Для этого все зарегистрированные ошибки классифицируются по важности. Важность определяется степенью влияния ошибки на бизнес-процессы. Чем большее количество бизнес-процессов затрагивается и чем они более значимые (следовательно, чем чаще процессы используются), тем выше деструктивное влияние ошибки и её критичность. Регламент исправления должен соответствовать критичности ошибки. Чем критичнее ошибка, тем быстрее надо её устранить. Тем не менее, нужно ставить цель исправлять все ошибки, любой важности, в течение одного рабочего дня с момента первого обращения пользователя. Важно понимать, что пользователь и разработчик по-разному определяют фактические сроки исправления ошибки, что часто приводит к конфликту между ними. Разработчик, как правило, считает время исправления ошибки с момента её регистрации, а пользователь — со своего первого обращения в службу поддержки, а зачастую — с момента проявления ошибки в программном продукте. Если ошибка для пользователя не очень критична, он может некоторое время потерпеть. Поэтому не стоит особо удивляться, когда разгневанный пользователь предъявляет претензии, что ошибка не исправляется уже, например, больше двух месяцев. Ситуация усугубляется, если от момента первого обращения пользователя до регистрации ошибки прошло несколько дней, в течение которых служба поддержки классифицировала обращение, как ошибочные действия пользователя или как ошибку функционирования программного продукта.
И только если обращение классифицировано, как ошибка функционирования программного продукта, сотрудник службы поддержки регистрирует ошибку в системе отслеживания ошибок. Именно поэтому важно мотивировать сотрудников исправлять ошибки день в день от момента первого обращения пользователя, а не от момента регистрации ошибки. Высший пилотаж — когда ошибки выявляют и исправляют без прямого обращения пользователя, только на основании данных телеметрии.
Кроме того, важно отделять ошибки поведения программы, воспринимаемые клиентом, от ошибок в коде. В коде может быть множество ошибок, приводящих к одной и той же ошибке поведения. Программист, внося исправления в разные места кода, справедливо сочтёт, будто исправил много ошибок, а пользователь при этом будет считать, что ошибка либо не исправляется, либо постоянно возвращается (регресс). Ситуация существенно осложняется, если ошибки кода присутствуют в зоне ответственности разных разработчиков, команд и даже в разных программных продуктах, при этом все они приводят к одной, с точки зрения пользователя, ошибке поведения. Поэтому процесс исправления ошибок должен ориентироваться на исправление ошибки поведения программы, в рамках которой вполне можно исправить несколько ошибок в коде разных программных продуктов.
Приведу пример, основанный на реальных событиях. Допустим, у пользователя не работает отчёт, который собирает данные из двух разных продуктов. При этом кодом самого отчёта владеет один разработчик, кодом первого продукта — другой, а кодом второго — третий. Служба поддержки регистрирует ошибку на первого разработчика и прикладывает материалы для повторения условий возникновения ошибки. Разработчик, отвечающий за отчёт, воспроизводит условия ошибки и понимает, что первый продукт возвращает неверные данные. Стандартной — и абсолютно неправильной — практикой при обработке подобного рода ошибок остаётся перевод ошибки на разработчика, ответственного за первый продукт. Он исправляет ошибку в своём продукте и выпускает пакет исправлений. Служба поддержки устанавливает пакет и закрывает ошибку. Пользователь запускает отчёт — и снова получает ошибку. Отчёт не работает. Пользователь фиксирует повторное обращение, а служба поддержки, само собой, регистрирует новую ошибку на разработчика отчёта. Разработчик повторно воспроизводит условия ошибки, понимает, что теперь второй продукт возвращает неверные данные, и переводит её на разработчика второго продукта. Разработчик второго продукта исправляет ошибку и выпускает пакет исправлений. После установки очередного пакета обновлений пользователь запускает отчёт, и он опять не работает. Весьма расстроенный пользователь в третий раз обращается в службу поддержки, которая регистрирует третью ошибку на разработчика отчёта. В этот раз разработчик отчёта находит ошибку в самом отчёте, исправляет её, выпуская пакет исправлений. В итоге, с точки зрения пользователя, единственную ошибку исправили с третьего раза, нарушив, разумеется, все регламенты. С точки зрения разработчиков — исправили несколько разных ошибок и все в строгом соответствии с регламентом.

Исправление ошибок

Как же должен выглядеть правильный процесс? При выявлении ошибки в первом продукте разработчик отчёта регистрирует новую ошибку на него, связанную с текущей ошибкой на отчёт, которая имеет более короткий регламент на исправление. Получив исправление, он должен убедиться, что отчёт работает корректно. И если опять не работает, надо зарегистрировать новую ошибку на второй продукт. После чего исправить отчёт и передать в службу поддержки полный пакет исправлений, состоящий из трёх пакетов.
Чтобы все эти действия были оперативными и эффективными, разработчики должны быть обучены быстрому выявлению и локализации ошибок. И, реализуя требования, встраивать средства журналирования и мониторинга бизнес-процессов пользователя. Нужна хорошая мотивация на быстрое исправление ошибок и соответствующий инструментарий, позволяющий оперативно справляться с их потоком.
Важный момент: определить, в каком виде и каким образом исправления будут доставляться до стенда пользователя. Исправление может быть выпущено в таких вариантах:
  • хотфикс (англ. hotfix, горячее исправление), или заплатка; имеется в виду небольшое локальное изменение, содержащее единственное исправление;
  • накопительный пакет исправлений (накопительный хотфикс), содержащий все исправления с момента выпуска релиза продукта;
  • новый релиз продукта.
Со своей стороны, крайне не рекомендую выпускать исправления в виде отдельных хотфиксов. Этот способ ведёт к конфигурационным ошибкам, то есть к ошибкам, связанным с неправильной конфигурацией стенда. При этом в коде программного продукта ошибок нет. Типовой пример: выпуская третий хотфикс, вы не знаете, в каком окружении он будет установлен. Между тем, возможны четыре варианта:
  • ни один из первых двух хотфиксов не установлен;
  • установлен только первый хотфикс;
  • установлен только второй хотфикс;
  • установлены оба хотфикса.
И чем больше хотфиксов вы выпускаете, тем больше вариантов конфигурации стенда. Протестировать все возможные конфигурации практически невозможно, так что нельзя предугадать, заработает ли хотфикс. Он может даже не установиться, если, например, базируется на ранее выпущенной «заплатке», которую клиент по каким-то соображениям не установил.
Поэтому выпускать исправления нужно либо в виде накопительного пакета исправлений, либо в новом релизе программного продукта. В этих случаях конфигурация клиента определена, значит, её легко повторить и протестировать установку исправления.
Основные этапы процесса исправления ошибок:
  • регистрация обращения;
  • регистрация ошибки;
  • повторение ошибки;
  • локализация ошибки;
  • регистрация ошибок на смежные продукты;
  • исправление ошибки;
  • тестирование исправления ошибки;
  • выпуск пакета исправлений;
  • развёртывание пакета исправлений;
  • закрытие ошибки.
Срок исправления ошибки, с точки зрения пользователя, исчисляется временем от регистрации обращения до закрытия ошибки. Полагаю, читателю пригодятся рекомендации по сокращению этого срока.
  • Минимизируйте число уточнений условий, при которых возникла ошибка. Данных при её регистрации должно быть достаточно для её повторения.
  • Обучите программистов быстро локализовывать ошибки.
  • Для критических и блокирующих ошибок лично подтверждайте передачу обработки ошибки на следующий шаг процесса смежнику — к нему надо обратиться лично, не надеясь на информационную систему. Смежник может просто не сразу обратить внимание на ошибку.

Конфигурационное управление

Тема конфигурационного управления — краеугольный камень процесса разработки программных продуктов. Об этот камень могут разбиться все попытки наладить эффективный и качественный их выпуск.
Разработка крупной программной системы включает в себя создание тысяч микросервисов. Для разработки каждого из них требуется создать множество артефактов: технические задания, файлы исходного кода для разных языков программирования, тестовые сценарии, файлы исходного кода автоматических тестов, пользовательскую документацию и многое другое. В рамках жизненного цикла программной системы большинство артефактов многократно меняется, и нужно чётко понимать, какие изменения актуальны, а какие нет. Зачастую в одни и те же артефакты изменения вносят одновременно несколько сотрудников. Вносить их нужно корректно и не терять. На помощь в этом хаосе приходит как раз конфигурационное управление. Основная его задача — навести порядок в работе с сотнями тысяч, миллионами и даже сотнями миллионов различных артефактов.
Для начала приведу основные определения.
Конфигурационный элемент (англ. configuration item) — атомарный объект, которым нужно управлять. К конфигурационным элементам относятся:
  • техническое задание;
  • файлы исходного кода;
  • тестовые сценарии;
  • исходный код автотестов;
  • API (программный интерфейс);
  • макеты диалоговых форм;
  • схемы бизнес-процессов;
  • скрипты сборки и развёртывания;
  • файлы, созданные в результате сборки;
  • Docker-образы;
  • шаблоны отчётов;
  • Packaged Business Capabilities (PBC);
  • микросервисы;
  • инсталляционные пакеты программных продуктов.
О том, что такое PBC, подробно будет рассказано в четвёртой части книги.
Версия — состояние конфигурационного элемента, которое может быть восстановлено в любой момент времени, независимо от истории его изменения.
Конфигурация — совокупность версий конфигурационных элементов.
Управление конфигурацией (англ. software configuration management, SCM) — управление набором конфигурационных элементов и их версиями.

Управление конфигурацией

Элементы конфигурации складываются вместе согласно архитектуре программной системы. Компоненты системы, например, PBC и микросервисы, сами, являясь, по сути, конфигурационными элементами, становятся центрами, вокруг которых объединяются другие конфигурационные элементы. Применяется «принцип матрёшки», когда одни элементы состоят из других. Во главе угла — единица поставки, то есть экземпляр программы или компонент, имеющий бизнес-ценность для заказчика. В микросервисной архитектуре такими компонентами являются PBC и микросервис.
Выделяют стандартные виды конфигураций.
  • Релиз программного компонента — состав единицы поставки, полный набор исполняемых файлов, скриптов развёртывания, шаблонов отчётов и прочих элементов, необходимых для развёртывания и функционирования компонента. Например, релиз микросервиса содержит:
  • Docker-образ микросервиса, включающий:
  • исполняемый код микросервиса;
  • исполняемый код фреймворка spring boot;
  • http-сервер, например, Feign;
  • операционную систему.
  • Docker-образ метаданных микросервиса, в который входят:
  • скрипты по созданию и обновлению структуры БД;
  • описание структуры БД;
  • скрипты по конвертации данных;
  • скрипты по загрузке стандартных справочников;
  • данные стандартных справочников для загрузки.
  • Паспорт микросервиса — описание состава, программных интерфейсов (API), зависимостей от других микросервисов и т. д.
  • Сопроводительная документация.
  • Пакет исправлений к релизу программного компонента — дельта между полным составом программного компонента с учётом всех исправлений и релизом программного компонента.
  • Конфигурация стенда — набор релизов и пакетов исправлений программных компонентов или PBC, установленных на стенд. Для централизованного ведения реестра стендов и их конфигураций используются специальные базы данных управления конфигурацией (CMDB).
  • Ветка разработки программного компонента — линия разработки кода, хранит историю изменений всех файлов исходного кода, входящих в состав программного компонента или являющихся источником для его сборки. В зависимости от назначения ветки разделяют на следующие Разновидности:
  • Личная ветка программиста (англ. private branch) — для временного хранения изменений программистом, до выкладки в ветку для нового функционала.
  • Ветка для нового функционала (англ. feature branch) — позволяет избежать негативного влияния незавершённой разработки на другие её линии. После успешной реализации функционала ветка интегрируется с основной.
  • Основная ветка (англ. master branch) — центральное место интеграции линий разработки. На её основе производится выпуск релиза программного компонента.
  • Релизная ветка (англ. release branch) — служит для внесения изменений при исправлении ошибок в релизе программного компонента. Именно на её основе производится выпуск пакета исправлений релиза программного компонента.
Для работы с историей изменения файлов исходного кода, создания новых версий и управления ветками разработки используются системы контроля версий (англ. version control system, VCS): платформа Digital Q.VCS или распределённая система управления версиями Git, программный продукт с открытым исходным кодом. Для хранения файлов исходного кода и истории их изменения создаётся репозиторий исходного кода.
В компании должны быть утверждены: порядок работы с системой контроля версий и репозиториями исходного кода, формат именования веток и правила работы с ними.
  • Базовая конфигурация (англ. baseline) — конфигурация, выбранная и закреплённая на любом этапе жизненного цикла разработки в качестве основы для дальнейшей работы. Так, базовая конфигурация упрощает выпуск программного продукта. При тестировании достаточно проверить работоспособность в условиях, когда версии смежных компонентов соответствуют базовой конфигурации. Нет необходимости поддерживать совместимость с более старыми, чем в базовой конфигурации, версиями программных продуктов.
Для идентификации конкретной конфигурации среди множества других используется номер. Например, номер релиза, который присваивается в соответствии с принятыми и утверждёнными правилами нумерации релизов программных продуктов. Понимание логики нумерации позволяет и всем участникам процесса разработки, и заказчикам уже на основании номера судить о характере изменений, их сути и составе конфигурации. Например, номер пакета исправлений должен однозначно идентифицировать номер релиза и системное наименование программного продукта, к которому он выпущен. Все элементы конфигурации однозначно соотносятся друг с другом, и это соотношение должно быть чётко идентифицировано. Каждый элемент конфигурации хранит в себе номер релиза или пакета исправлений и наименование программного продукта, в рамках которого выпущен. Рекомендую также включать номер релиза или пакета исправлений в наименование элементов конфигурации, к каким они относятся. Наблюдая, например, через консоль Kubernetes за работающими контейнерами, вы будете точно знать, к каким PBC и релизам PBC они относятся.

Выпуск конфигурационного элемента

Выпуск — это утверждённая в компании процедура проверки конфигурационного элемента, после которой компонент получает «добро» на установку в рабочей среде. Выпуск конфигурационного элемента, говоря современным языком, включает практики непрерывной интеграции (англ. Continuous Integration, CI) и непрерывной доставки (англ. Continuous Delivery, CD).
Непрерывная интеграция заключается в том, что все изменения, вносимые в код, объединяются в центральном репозитории исходного кода. Такая операция называется «слиянием». Слияние происходит по нескольку раз в день, и после каждого из них срабатывает автоматическая сборка и тестирование. В процессе сборки исходный код компилируется в исполняемый, а затем его упаковывают в Docker-образ. Потом система статического анализа кода (например, SonarQube) проверяет качество кода, автоматические Unit-тесты — работу модулей кода, GUI-тесты — работу пользовательского интерфейса, а API-тесты — качество и надёжность программного интерфейса.
Непрерывная доставка — практика автоматизации всего процесса выпуска программного обеспечения. Включает в себя непрерывную интеграцию плюс автоматическое развёртывание релиза конфигурационного элемента на различных тестовых стендах — для проведения интеграционных, нагрузочных и деградационных испытаний.
Если все этапы проверки пройдены успешно, релизу конфигурационного элемента присваивается номер — в соответствии с правилами нумерации. Он прописывается во все конфигурационные элементы, входящие в релиз. Файл, содержащий такой релиз, заверяется утверждённым в компании алгоритмом подписи. Подпись нужна, чтобы можно было удостовериться: после выпуска в файл с релизом не вносились изменения. Затем выпущенный релиз конфигурационного элемента размещают в реестре выпущенных компонентов, и он становится доступным для установки в рабочей среде.

ТЕОРИЯ ПРОЕКТНОГО И ПРОЦЕССНОГО ПОДХОДОВ

Проектные практики разработки программных продуктов

Проект (по PMBOK) — временное предприятие, предназначенное для создания уникальных продуктов, услуг или результатов. Временный характер проекта означает, что у любого проекта есть определённое начало и завершение — оно наступает, когда достигнута цель проекта. Большинство проектов реализуется для достижения устойчивого, длительного результата, а их успешным завершением становится создание уникального продукта или услуги. Поскольку жизненный цикл программного продукта, как правило, превышает сроки проекта по его разработке, его развитие реализуется через серию проектов.
У современных программных продуктов модульная структура. Модуль является обособленной, имеющей самостоятельную ценность единицей. Размеры модулей могут серьёзно различаться и зависят от принятой в компании методологии проектирования и выбранного технологического стека. Например, модули, создаваемые в рамках SOA (service oriented architecture — архитектура, ориентированная на сервисы), значительно крупнее, нежели в рамках микросервисной архитектуры. В одном проекте обычно создают и развивают одновременно несколько модулей. Вполне жизненной можно считать ситуацию, когда одни и те же модули развиваются в нескольких параллельно идущих проектах.
Разберём, как гибкие методологии разработки могут использоваться в проектах. По-крупному, в проекте разработки программных продуктов присутствуют три стадии: обследование, реализация и внедрение. Каждая из них разбивается на целый ряд этапов в соответствии с целями и задачами проекта. Бывают и дополнительные, не связанные непосредственно с разработкой, этапы. Я заведомо упрощаю структуру проекта, чтобы донести идеи построения эффективного производства программных продуктов. Хотя описание каждой стадии и будет сделано независимо, это не значит, будто стадии должны выполняться строго друг за другом, как это происходит в водопадной модели.
Основная задача любого проекта — достижение его целей согласно утверждённым срокам и бюджету. Как же удержать проект в этих рамках, если гибкие методы предполагают работу короткими, например, двухнедельными спринтами, и задачи каждого спринта могут меняться? Ответ достаточно простой. Определяем трудоёмкость разработки модулей и сдачи функционала заказчику. Зная производительность каждой выделенной на проект команды в течение спринта, получим необходимое число спринтов и, следовательно, сроки выполнения проекта. Управляя выделенной на проект производительностью команд и количеством спринтов, можно подстроить план-график под необходимые сроки. О том, как оценивать задачи и определять производительность и мощность команды, речь пойдёт в следующих главах.
ОБСЛЕДОВАНИЕ
Основные задачи обследования:
  • определение границ проекта;
  • разработка и согласование архитектуры решения;
  • описание требований к программному решению;
  • оценка бюджета и сроков проекта.
Довольно часто бюджет проекта определён ещё до того, как проведено обследование. В этом случае задачей обследования является определение такого способа реализации требований, чтобы бюджет не превысил утверждённой суммы. Радикально настроенные адепты гибких методологий считают, будто обследование вообще лишняя трата времени, и лучше сразу начинать разработку. Заказчик сам может не знать всех требований и будет уточнять их от спринта к спринту. Для небольших проектов или тех, что реализуются по схеме повременной оплаты, подобный подход применим. Однако для крупных проектов с фиксированной стоимостью стадия обследования обязательна. На самом деле, его результаты больше нужны исполнителю проекта, чем заказчику, и поэтому встаёт вопрос эффективного, наименее затратного проведения обследования. По опыту, на обследование нужно потратить не более 10% бюджета проекта (здесь и далее под этим термином понимается бюджет, позволяющий исполнителю получить запланированную прибыль). То есть эффективным считается обследование, когда на его проведение потрачено менее 10% фактического бюджета проекта, при этом исполнитель получит запланированную прибыль.

СИСТЕМНАЯ СХЕМА ПРОЕКТА

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

Матрица трассировки требований

РЕАЛИЗАЦИЯ
Основная задача этапа — реализация требований в согласованные сроки и в рамках утверждённого бюджета. Прежде чем давать рекомендации по организации эффективной работы на этом этапе, рассмотрим, как сейчас обстоят дела с управлением проектами по разработке программных продуктов. Идея, утверждающая, будто проект наиболее эффективно разрабатывать выделенной на 100% и, следовательно, сфокусированной командой, не оправдалась. Я не рассматриваю небольшие продукты, созданные по принципу «внедрили и забыли». Я говорю о крупных программных продуктах класса ERP (англ. Enterprise Resource Planning, планирование ресурсов предприятия) или АБС (автоматизированная банковская система), жизненный цикл которых значительно превышает не только сроки проекта, но и жизненный цикл программно-аппаратных архитектур, на которых они изначально построены. Практика современной разработки программных продуктов показала, что команды, сформированные вокруг продукта, более эффективны, нежели те, что формируются вокруг проекта. И этому есть свои объяснения.
  • Вновь сформированная команда тратит время на то, чтобы её члены научились эффективно взаимодействовать друг с другом и выйти на плато продуктивности.
  • Если в каждом проекте продукт разрабатывает новая команда, то код такого продукта скоро начинает походить на лоскутное одеяло. В нём с каждым разом всё сложнее разобраться, и всё выше вероятность внести регресс (перестаёт работать ранее работавший функционал).
  • Мотивация делать качественный продукт у проектной команды слабее. Для неё главное — вовремя сдать функционал, а исправляет и дорабатывает пусть кто-то другой.
Поэтому команда управления проектом, как правило, не может рассчитывать на то, что команды разработки продуктов будут полностью выделены на проект.
Вторым серьезным фактором, влияющим на организацию в проекте этапа разработки, является невозможность построить план-график в привычном понимании. Команды разработки продуктов, работающие спринтами, сами решают, в каком порядке и кем будут реализованы такие задачи, как аналитика, кодирование, тестирование, документирование и прочие. С другой стороны, менеджер проекта свободен от микроменеджмента в части планирования и отслеживания отдельных задач проекта.
Приведу проверенные практикой идеи организации эффективной работы на этапе разработки.
  • Необходимо разделить разработку на продуктовые потоки. Когда речь идёт о больших продуктах, разработку которых ведут несколько команд, необходимо выделить потоки для каждой команды.
  • Требования из матрицы трассировки группируются в сценарии сдачи требований заказчику.
  • Обратным планированием определяется максимальное число спринтов, в течение которых должно быть реализовано требование, чтобы сдать заказчику весь сценарий в срок.
  • Вместо плана-графика реализации требований составляется план-график спринтов каждой команды, фиксирующий набор требований, реализуемых в каждом спринте. Загрузку спринта требованиями нужно планировать с учётом выделенной на проект мощности команды.
  • Спринты разработки требований и сдачи сценариев планируются «встык», реализованное требование нужно сдавать заказчику, пока оно «горячее». То есть команда разработки ещё помнит требование и сможет быстро и с меньшими трудозатратами внести необходимые изменения в продукт или проконсультировать команду внедрения.
  • Как следствие, крайне неэффективно планировать реализацию требований задолго до сдачи заказчику, поскольку к моменту сдачи команда «забудет» о требовании, и ей понадобится больше времени, чтобы вспомнить все моменты реализации.
  • Команда управления проектом должна стать активным заказчиком для команд разработки продуктов. Менеджер проекта участвует в планировании и обзоре результатов спринтов, где планируется реализация требований проекта, и даёт команде объективную обратную связь.
  • Прогресс по проекту нужно отслеживать по уменьшению плановых трудозатрат, оставшихся после реализации требований из матрицы трассировки. Фактические трудозатраты нужно использовать для управления бюджетом проекта.
  • Нужно вовлекать команды в управление бюджетом проекта. Одной из целей спринта должна стать цель по попаданию фактических трудозатрат на реализацию требования в запланированные в матрице трассировки. Уже на старте спринта команда должна понимать, как она реализует требования и сможет ли уложиться в запланированную трудоёмкость.
Продуктовый поток, или бэклог продукта — упорядоченный набор всех задач, которые задумано получить от продукта. Этот список содержит краткие описания всех желаемых возможностей продукта. Если в проекте планируется создавать или развивать пять продуктов пятью командами, необходимо сформировать пять продуктовых наборов задач. Для этого требования из матрицы трассировки нужно предварительно декомпозировать до уровня продуктов.
Обратное планирование — определение последовательных шагов от цели к текущему состоянию. В обыденной жизни такое планирование удобно использовать для определения времени выхода из дома, чтобы успеть на самолёт. Допустим, вылет запланирован в 14:00. Для прохождения всех процедур на зарубежных рейсах рекомендуется прибыть за три часа до вылета. Получаем, что в аэропорту мы должны быть в 11:00. Если поездка до него занимает час, выходить из дома надо не позднее 10:00.
КОНФИГУРАЦИОННОЕ УПРАВЛЕНИЕ В ПРОЕКТЕ
В ходе реализации проекта создаётся и изменяется множество артефактов.
Артефакт — это любой искусственно созданный элемент программной системы:
  • проектная документация;
  • техническое задание;
  • исходные тексты программных продуктов;
  • пакеты тестов;
  • пользовательская документация;
  • инсталляционные пакеты программных продуктов и т. д.
Таких артефактов за время жизни проекта создаётся сотни и тысячи. И у каждого есть версия — некоторое состояние в определённый момент. Поскольку состояние артефактов постоянно меняется в проекте, появляются новые версии артефактов. Одной из причин большого количества ошибок в ПО является несогласованность версий различных артефактов.
Поясню на примере. Допустим, у нас есть два модуля. Один из них предоставляет программный интерфейс, другой — его использует. Такие модули называются связанными, поскольку работа одного модуля зависит от работоспособности другого. Если модуль, предоставляющий интерфейс, работает некорректно, то и вызывающий его модуль тоже будет работать некорректно.
Рассмотрим более сложный случай несогласованности версий модулей, взяв за основу предыдущий пример. Изначально у модулей была первая версия, и предоставляемый интерфейс также имел первую версию. Предположим, мы расширили интерфейс модуля, предоставляющего интерфейс, до второй версии, например, добавили новый метод. Второй модуль во второй своей версии начал использовать вторую версию интерфейса, то есть начал вызывать новый метод. Теперь, если мы установим на стенд первые версии обоих модулей, всё будет работать корректно. Если обновим второй модуль до второй версии, возникнет ошибка, поскольку будет попытка вызова метода из первого модуля, реализации которого на стенде нет. Чтобы устранить ошибку, достаточно обновить первый модуль до второй версии. Получается, при несогласованности версий будут проявляться ошибки, даже при условии, что реальных ошибок в коде нет. И чтобы не запутаться во всём многообразии артефактов и их версий, этим нужно управлять.
Управление выпускаемыми релизами ПО в проекте должно базироваться на принятых в компании процессе выпуска конфигурационного элемента и релизной политике. Поскольку они влекут за собой существенные ограничения, в незрелых организациях часто пренебрегают этими правилами или осознанно их нарушают, искренне считая, будто приносят проекту благо.
Приведу пример «типового» нарушения. В крупном проекте разработка программного продукта может идти несколько месяцев, допустим, шесть. Возникает соблазн провести работы по выпуску продуктов в конце разработки всех требований, а, допустим, не ежемесячно, как диктует принятая релизная политика, то есть один раз вместо шести. Вроде бы всё резонно: зачем тратить ресурсы на выполнение процесса выпуска шесть раз, когда можно ограничиться одним? Экономия бюджета очевидна! На самом деле, всё не так. Опыт показывает, что стоимость выпуска один раз за полгода гораздо выше, нежели шесть раз каждый месяц. Более того, современные практики разработки программных продуктов требуют ежедневного автоматического проведения процедуры выпуска. Тогда, как ни странно, трудоёмкость выпуска релиза будет равна нулю. И появится возможность обновлять промышленный стенд после каждого внесения изменения в код разработчиком.
Сразу оговорюсь: ежедневный выпуск — не средство получения качественного продукта, а способ удержания его на определенном уровне качества. Нужный уровень достигается за счёт комплексного внедрения практик и развития культурного уровня команд разработки. Переход к ежедневному выпуску — процесс постепенный. Как невозможно сходу пробежать марафон за два часа, так нельзя за один шаг перейти от выпуска раз в полгода к ежедневному. Сначала нужно выйти на выпуск качественного продукта раз в месяц, затем каждый спринт и только после этого переходить к ежедневным выпускам.
Почему трудоёмкость шести выпусков раз в месяц меньше одного раза за шесть месяцев? Чем дольше продолжается разработка, тем выше трудоёмкость переработки (rework) на выявление и исправление ошибок функционала на единицу времени разработки. Здесь особенно важны несколько факторов.
  • Кумулятивный эффект от вносимых в код ошибок, включая архитектурные ошибки.
  • Реализация функционала, основанного на ошибочном поведении смежных модулей. Например, один модуль «заложился» на ошибочное поведение метода другого модуля. После исправления работы метода наш модуль также перестанет работать корректно. Возникнет незапланированная переработка. Чем дольше мы не выявляем ошибочное поведение, тем больше кода может быть основано на этой ошибке. В какой-то момент проще признать ошибку особенностью поведения и не исправлять её, чтобы снизить объём переработки после исправления.
  • Чем дольше не исправляются ошибки, тем больше их накапливается. Практика показывает, что при исправлении трёх ошибок обязательно вносится, как минимум, одна новая. Чем больше накопилось ошибок, тем выше вероятность внесения новых ошибок при исправлении старых. Ошибки начинают расти, как снежный ком.
В итоге, после шести месяцев разработки без промежуточных выпусков может понадобиться ещё месяца три (а то и больше) активной работы всех команд по стабилизации решения. Если же выпускать ежедневно, трудозатраты на стабилизацию за весь период будут близки к нулю.
Для снижения числа ошибок, связанных с несогласованностью версий, вплоть до полного их исключения, необходимо управлять выпускаемыми релизами и управлять развёртыванием ПО на выделенной проекту IT-инфраструктуре.
Все реализуемые в продукте требования нужно разложить по версиям, в которых они будут доступны. Число версий зависит от принятой релизной политики. Если продукт разрабатывается двухнедельными спринтами в течение двух месяцев, то при ежемесячном выпуске будет две версии, а при выпуске в спринте — четыре. Такой подход существенно упростит управление конфигурациями в проекте. Вместо того, чтобы следить за датой выпуска каждого требования (а их может быть тысячи и десятки тысяч), проще следить за версиями продуктов, коих будут единицы или десятки. Каждая версия чётко определяет список реализованных требований. Поэтому при передаче очередной версии продукта заказчику известен полный набор включённых в неё требований.
Для запуска продукта в промышленную эксплуатацию нужно учитывать, что в процессе приёмки функционала обязательно будут выявлены ошибки, поэтому запускается не чистый релиз, а пакет исправлений релиза. В плане выпуска релизов надо обязательно предусмотреть выпуск пакета исправлений на последний релиз. Именно на нём и будет произведён запуск. Для критически важных продуктов рекомендую планировать выпуск двух пакетов исправлений.
Для управления развёртыванием программного обеспечения необходимо определить, сколько стендов и на каких этапах проекта нужны и какие версии ПО на них должны быть развёрнуты. В проектах, включающих стадию разработки программных продуктов, выделяют несколько этапов проверки:
  • проверка функционала разработчиком;
  • функциональное тестирование командой разработки;
  • выпуск релизов продуктов;
  • приёмка разработанного функционала командой внедрения;
  • сдача сценариев тестирования заказчику;
  • нагрузочное тестирование;
  • интеграционное тестирование в условиях, приближенных к промышленным;
  • запуск в промышленную эксплуатацию.
Здесь приведены основные или обязательные этапы проверки программного продукта на соответствие требованиям. В реальном проекте их может быть больше. Для каждого этапа проверки необходимо создать отдельный стенд. Для этапов проверки разработчиком, функционального тестирования и выпуска, как правило, создают отдельный стенд на каждый разрабатываемый модуль. Каждый стенд имеет уникальную конфигурацию установленных релизов модулей и поэтому свой план обновления. Ошибки в обновлении стендов могут иметь серьёзные последствия для проекта: смещение сроков выполнения работ, простой команд, ожидающих восстановления стенда, негативная реакция заказчика. Конечно, масштаб последствий зависит от этапа проверки и числа сотрудников проекта и заказчика, использующих стенд. Этапы приведены в порядке увеличения степени наносимого вреда проекту и заказчику в случае «падения» стенда. Например, «падение» стенда разработчика, кроме него самого, никто и не заметит. Но если «упал» стенд для приёмо-сдаточных испытаний, остановится работа нескольких команд внедрения, и неминуемо недовольство заказчика, поскольку ему передают «сырой» продукт. Заказчик может быть настолько разочарован в качестве передаваемого продукта, что потребует проведения дополнительных циклов тестирования без участия представителей заказчика, а это — лишние средства из бюджета. «Падение» же промышленного стенда ведёт к прямым экономическим потерям заказчика.
Под «падением стенда» понимается приведение его в такое состояние, когда использование его для проведения работ теряет всякий смысл. Например, стенд может быть физически недоступен для подключения, либо не работает базовая функциональность, что ведёт к многочисленным ошибкам и невозможности выполнять многие тестовые сценарии.
Цель такого количества этапов проверки в том, чтобы на каждом следующем этапе число ошибок снижалось, а в промышленной эксплуатации их не было бы совсем. Ошибки в обновлении стендов могут перечеркнуть усилия команд и отбросить проект на более ранние этапы проверки, что негативно скажется на сроках выполнения работ и на бюджете.
Как снизить или полностью исключить вероятность «падения» стенда после обновления? Простого ответа нет. Это целый комплекс мероприятий, которые нужно выполнять. Прежде чем обратиться к ним, рассмотрим типовые причины, приводящие к «падению» стенда:
  • аппаратный сбой может быть вызван как дефектом оборудования, так и неправильной настройкой его системного ПО;
  • ошибки и неправильная настройка системного ПО, в том числе операционных систем, серверов приложений, серверов баз данных и т. д.;
  • ошибки в коде и настройке разрабатываемого ПО;
  • несогласованность версий модулей;
  • сбой при обновлении одного из модулей;
  • ошибка в настройке пользовательских прав и привилегий;
  • ошибки в действиях администраторов системного программного обеспечения, а также настройщиков и пользователей прикладного ПО. Например, удаление критически важной информации.
Для каждой причины существует свой список упреждающих и восстановительных мероприятий. Упреждающие мероприятия призваны минимизировать вероятность сбоя, а восстановительные должны максимально быстро привести стенд в рабочее состояние. Близкой аналогией можно считать противопожарные мероприятия, где основной комплекс направлен на предотвращение возгорания. Но если оно всё-таки произошло, его надо быстро потушить. Например, упреждающее действие для установки несогласованных по версиям модулей — жёсткий контроль совместимости программных интерфейсов, устанавливаемых и уже установленных на стенде модулей. Это как нестандартная вилка: если она к розетке не подходит, то не нужно такой прибор подключать к сети. То же самое и с модулями. Современные архитектуры, использующие контейнерные технологии, позволяют на одном стенде запускать одновременно несколько версий одного модуля. Это позволяет проверить его новую версию на фокус-группе, причём основная группа пользователей продолжает работать на старой версии. После приёмки новой версии модуля фокус-группой запускают процедуру постепенного переключения пользователей на новую версию. Если на любом шаге процесса обнаружится критический сбой, пользователей плавно переключат на старую версию модуля. Старая версия отключается после переключения всех пользователей на новую. Для решений, не имеющих архитектурной возможности одновременного исполнения нескольких версий одного модуля, надо позаботиться о процедуре восстановления. Например, создать перед обновлением копию базы данных и всех заменяемых исполняемых файлов. При необходимости, восстановить базу данных и исполняемые файлы из созданной копии.
При внимательном анализе причин сбоев не стоит упускать и человеческий фактор. Человек — самый ненадёжный «элемент» системы, следовательно, для упреждения большинства сбоев надо исключить его участие из процессов обновления. Для этого в проекте нужно внедрять практики DevOps и CI/CD-конвейер. Об этом речь пойдёт немного позже.
ВНЕДРЕНИЕ
Стадия внедрения включает приёмо-сдаточные испытания и запуск в опытно-промышленную эксплуатацию.
Приёмо-сдаточные испытания — проверка разработанного программного продукта методом имитации процессов, в рамках которых предполагается его использовать.
Потребности заказчика выявляют на этапе обследования, в рамках которого создают сценарии использования программного продукта в автоматизируемых процессах.
Сценарий использования — это описание поведения системы при взаимодействии с кем-то (или чем-то) из внешней среды. В самом простом случае в сценарии описан набор действий некоторой роли и ожидаемый результат.
Во время приёмо-сдаточных испытаний заказчики или их представители с помощью ручного и автоматизированного выполнения сценариев использования проверяют:
  • способность каждой роли выполнить обязанности в рамках автоматизируемых бизнес-процессов;
  • нагрузочную способность программного продукта, то есть его способность обслужить всех пользователей системы без нарушения работоспособности и выполнить необходимые операции в регламентное время;
  • способность программного продукта к взаимодействию с внешними системами в рамках автоматизируемых процессов.
В проектах с высокими требованиями к работе под нагрузкой дополнительно проверяют способность программного продукта работать в течение нескольких часов при максимальной нагрузке без нарушения работоспособности и деградации производительности.
На основании успешного прохождения приёмо-сдаточных испытаний принимается решение о передаче программного обеспечения в опытно-промышленную эксплуатацию. Активную роль в этих испытаниях играет заказчик, поэтому очень важно минимизировать число повторений при сдаче сценариев. Идеально сдать их все с первого раза. Для этого нужно ещё на этапе разработки создать на основании сценариев использования сценарии тестирования для проверки продукта в спринтах по ходу реализации требований. И активно привлекать заказчиков к демонстрации сценариев использования по мере их готовности. С одной стороны, демонстрация продукта на ранних стадиях положительно влияет на отношение заказчика к продукту и проекту, с другой — позволяет выявлять и уточнять детали требований и вносить небольшие изменения в продукт задолго до его сдачи. Это обеспечивает высокую готовность программного продукта к прохождению приёмо-сдаточных испытаний.
В процессе прохождения приёмо-сдаточных испытаний команды разработки должны быть сфокусированы на исправлении ошибок. В первую очередь нужно исправлять ошибки, блокирующие прохождение испытаний.
Хорошая практика — построение ежедневного отчёта об обороте ошибок проекта. На каждую дату испытаний считают:
  • количество новых ошибок;
  • количество исправленных ошибок;
  • количество исправленных ошибок, принятых заказчиком;
  • общее количество ошибок на стороне исполнителя;
  • общее количество ошибок на стороне заказчика;
  • скорость прироста ошибок — среднее количество вновь регистрируемых ошибок в день;
  • скорость исправления ошибок — среднее количество исправленных ошибок в день;
  • прогнозную дату исправления всех ошибок с учётом прироста и скорости исправления.
Прогнозная дата исправления ошибок позволяет определить, достаточна ли текущая скорость исправления ошибок, чтобы уложиться в сроки сдачи функционала. Если она ниже или равна скорости прироста, то тренда на уменьшение общего количества ошибок нет, следовательно, спрогнозировать сроки сдачи невозможно. Ситуация становится неуправляемой. Как повысить скорость и эффективность исправления ошибок, описано в следующем разделе.
Made on
Tilda