Михаил Михайлец, руководитель группы аналитиков направления облачных решений Лиги Цифровой Экономики, рассказал, как его команда попробовала быстро подготовить задачи по классическому ТЗ (ГОСТ 19) в Jira при работе с государственным заказчиком и что из этого получилось.

Пожалуй, большинству известно, что ГОСТ предусматривает водопадную модель разработки ПО. До недавнего времени она была зафиксирована в постановлении Правительства Российской Федерации от 6 июля 2015 г. № 676 «О требованиях к порядку создания, развития, ввода в эксплуатацию, эксплуатации и вывода из эксплуатации государственных информационных систем и дальнейшего хранения содержащейся в их базах данных информации».

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

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

  • В очередную часть частного технического задания выносятся сугубо функциональные требования к задачам приложения;

  • В процессе одного или нескольких спринтов документ согласовывается с заинтересованными лицами;

  • Далее следует классическая разработка и поведение приëмки;

  • После спринт считается закрытым.   

В память о нем остается стопка документов, которая будет удваиваться каждую итерацию.

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

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

Сами разработчики испытывают околорелигиозный ужас при вовлечении в проект по ГОСТу. Мол, мало того что скучно и грустно, так еще и овертаймы после очередного окончательного прояснения заказчиком некоторых изначально аморфных пунктов ТЗ.

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

Мы тоже пробовали организовать гибкую работу на проекте с государственным заказчиком, — и вот что из этого получилось.

Дано

  1. Техническое задание по ГОСТ 19.201-78 от государственного заказчика. Как водится, часть требований очень важные. Даже ОЧЕНЬ важные. Часть обещали прояснить потом. А часть в принципе не соответствовала базовым характеристикам однозначности, проверяемости, непротиворечивости и т. д.

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

  3. Пристальное внимание руководства к проекту.

  4. Отсутствие постоянных тестировщиков и фронтенд-разработчиков.

  5. Год на реализацию.

  6. Несгибаемый оптимизм.

Поехали

ТЗ представляло собой классический документ, который содержал требования в строгом соответствии с буквой стандарта.

Вот оно:

Ладно, это не совсем тот документ, о котором шла речь во вступлении, но структурно очень близкий
Ладно, это не совсем тот документ, о котором шла речь во вступлении, но структурно очень близкий

Определяем спектр работ

Первое, что требовалось сделать, — завести эпики в Jira и назначить ответственных. Поскольку, как видно из документа, далеко не все пункты требований нуждались в отдельном эпике в Jira, решено было вынести исключительно подпункты из раздела 4.1.

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

Как видно из примера выше, подсистемы при помощи стандартных средств форматирования MS Word указываются в заголовках третьего уровня (4.1.х), модули — в заголовках четвертого уровня (4.1.х.y), а сами требования — основным текстом. При корректном оформлении документа уровень того или иного абзаца текста можно посмотреть непосредственно в его свойствах:

В объектной модели VBA уровню абзаца соответствует атрибут OutlineLevel объекта Paragraph (абзац). Если представить упрощенную иерархию текстовых объектов в MS Word, то получится такая структура:

Каждый объект иерархии представляет собой массив элементов, к каждому из которых можно обратиться в VBA по индексу, начинающемуся с 1.

Соответственно, в нашем случае задача сводится к выборке всех абзацев основного текста с атрибутом OutlineLevel, равным OutlineLevel10, и соотнесения их с подсистемами и модулями упоминаемых в абзацах с OutlineLevel равным OutlineLevel3 и OutlineLevel4 соответственно. Эти абзацы основного будут импортированы в Jira как заголовки эпиков, а мнемоники подсистем и модулей будут размещены как теги. Чтобы не заниматься лишней чисткой исходного документа, оперировать будем объектом Selection, который представляет собой обычный выделенный мышкой фрагмент текста в документе. Быстро сделать это с разделом 4.1 можно из панели навигации:

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

Итого, чтобы вызвать из MS Word джина, который будет исполнять наши желания, нажимаем сочетание клавиш ALT + F8 и задаем название нашему макросу без пробелов. В тексте макроса создаем объект Paragraphs, которому присваиваем массив абзацев из документа, попавших под выделение мышкой, и тогда можно обращаться к каждому из них напрямую:

Dim reqs As Paragraphs
Set reqs = Selection.Paragraphs
For i = 1 To reqs.Count
... reqs(i).Range.Text

Муки выбора способа импорта

Чтобы выполнить импорт, можно воспользоваться API Jira напрямую, как описано здесь. Нам этот способ не подходит, поскольку у нас есть следующие нужды:

  1. Ручная правка файла импорта.

  2. Переиспользование получившегося списка задач.

  3. Упрощение отладки.

Посему решено было не мудрствовать лукаво, а переносить требования в новый документ Excel в нужной нам структуре. VBA позволяет писать сразу CSV-файлы, которые используются в импортере Jira, через метод CreateTextFile объекта Scripting.FileSystemObject, но работать с ними вне MS Excel тоже не очень удобно.

Пример работы с контентом xls-файла из VBA-скрипта, запущенного в MS Word:

' Создаём новый объект Excel с пустой рабочей книгой
Set xlApp = CreateObject("Excel.Application")
Set wbApp = xlApp.Workbooks.Add
' В ячейку А1 пишем текст
xlApp.Sheets("Лист1").Cells(1, 1).Value = "Какой-то текст для примера"
' Сохраняем файл
wbApp.SaveAs FileName:="C:\Documents\import.xls"
' Удаляем ненужные объекты
wbApp.Close False
Set wbApp = Nothing
xlApp.Quit
Set xlApp = Nothing

Собираем все вместе

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

Sub getRequirements()
'
' getRequirements Макрос для сбора требований в таблицу
'
' Определяем путь текущего документа MS Word с техническим заданием
Dim CurrentPath As String
    CurrentPath = Application.ActiveDocument.Path
' Получаем текущее выделение текста
Dim reqs As Paragraphs
    Set reqs = Selection.Paragraphs
' Проверяем, что документ существует в файловой системе и в нём что-то выделено    
    If (Len(CurrentPath) > 0) And (reqs.Count > 0) Then
' Создаём основу для будущего файла импорта
        Set xlApp = CreateObject("Excel.Application")
        Set wbApp = xlApp.Workbooks.Add
            xlApp.Sheets("Лист1").Cells(1, 1).Value = "Номер"
            xlApp.Sheets("Лист1").Cells(1, 2).Value = "Требование"
            xlApp.Sheets("Лист1").Cells(1, 3).Value = "Подсистема"
            xlApp.Sheets("Лист1").Cells(1, 4).Value = "Модуль"
' Модно инициируем счётчики и текстовые буферы. Запись i% = 1 равносильна Dim i As Integer i = 1. 
            i% = 1
            n% = 1
            CurrentSubsystem$ = "Подсистема не указана"
            CurrentModule$ = "Модуль не указан"
' Запуск перебора абзацев текста с анализом их уровня
                For i = 1 To reqs.Count
                    
                    Select Case reqs(i).OutlineLevel
                        Case Is = wdOutlineLevel3
                            CurrentSubsystem = reqs(i).Range.Text
                        Case Is = wdOutlineLevel4
                            CurrentModule = reqs(i).Range.Text
                        Case Else
                            n = n + 1
                            xlApp.Sheets("Лист1").Cells(n, 1).Value = CStr(n - 1)
                            xlApp.Sheets("Лист1").Cells(n, 2).Value = reqs(i).Range.Text
                            xlApp.Sheets("Лист1").Cells(n, 3).Value = CurrentSubsystem
                            xlApp.Sheets("Лист1").Cells(n, 4).Value = CurrentModule
                    End Select
                Next
' Сохранение файла и высвобождение памяти
        wbApp.SaveAs FileName:=CurrentPath + "\import.xls"

        wbApp.Close False
        Set wbApp = Nothing
        xlApp.Quit
        Set xlApp = Nothing
    End If

End Sub

 Результат выполнения:

Метод не без недостатков, поэтому подчеркну основные:

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

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

  3. Метод очень чувствителен к структуре документа. Если у вас в каком-то из подпунктов нет требований к модулям, а сразу есть к подсистеме, то нужно будет или удалить это вручную или усложнять логику скрипта.

  4. При работе с пунктами маркированных списков внутри текста документа MS Word каждый пункт размещается в отдельной строке xls-файла уже без маркера. В отрыве от вводного абзаца, скорее всего, не будет понятно, про что эпик. Поэтому требуется объединять текст ячеек таким образом, чтобы избежать использования новых строк, которые не без огрехов поддерживается импортом из CSV. Поскольку случаи использования списков достаточно индивидуальны, для каждого документа технического задания нужно придумывать свои подходы. Например, вручную свести все пункты каждого списка на листе Excel в одну ячейку и автозаменой убрать символы перевода строк. Символ перевода строки в ячейке кодируется комбинацией «010» на цифровой клавиатуре с зажатой левой клавишей ALT. Или придется вручную аккуратно перепечатать пункты с текстом вводного абзаца.

Продолжаем препарировать требования

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

Отличники аналитической подготовки могут прописать вводное описание к задачам, которое будет раскрывать смысл требований для проработки младшими коллегами (и помним про боль переводов строк в CSV).

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

Файл сохраняем в CSV. Я чаще всего использую в качестве разделителей точку с запятой, потому что экономлю энергию, но можно и экзотику, например «|». Главное, чего следует избегать, так это конфликтов между разделителями и знаками препинания по тексту.

Импорт

Препарированные и дополненные требования готовы к импорту, поэтому аутентифицируемся в любимой Jira и переходим в меню Задачи > Импортировать задачи из CSV (Issues > Import issues from CSV). Этот пункт будет доступен при наличии полномочий Create Issue в проекте и глобального полномочия Bulk Change в вашей Jira. Они раздаются администраторами. При отсутствии убедитесь, что с последними вы прежде не ругались, и обратитесь за помощью.

На первом шаге в поле Файл источник CSV (CSV source file) будет предложено указать CSV-файл импорта. Файл был подготовлен ранее на базе документа ТЗ с помощью простого VBA-скрипта.

На втором шаге нужно указать настройки импорта:

  • Импорт в проект (Import to project) — проект, где будут размещаться задачи. В выпадающем списке будут отображены все видимые вам проекты.

  • Кодировка файла (File encoding) — проверьте, что она указана корректно. Еще один довод в пользу использования Excel — то, что он чаще всего сохраняет в кодировке UTF-8, поэтому в этой части проблем быть не должно.

  • Разделитель (Delimiter) — собственно, вид разделителя. То самое место, где можно указать свой особенный соответствующий знак для файла импорта. Если у вас вдруг в качестве разделителя выступает табуляция, то укажите в поле значение «\t».

  • Формат даты (Date format) — актуален, если у вас в файле указаны даты. Я поменял формат по умолчанию в соответствии с тем, который был получен от менеджера проекта.

На третьем шаге требуется установить соответствие между полями файла импорта и атрибутами эпиков в Jira. У этого инструмента только одно явное правило — чтобы было заполнено поле Тема (Summary), в остальном ошибки проявятся только после импорта.

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

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

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

Не ленитесь проверять успешность импорта при помощи кнопки Проверить (Validate) в нижней части страницы. Как бы аккуратно ни работали со значениями, что-то с ошибкой да просочится в импорт.

Вот тот результат, к которому нужно стремиться:

Если ошибок нет или они несущественны, то приступайте к импорту, нажав на кнопку Начать импорт (Begin import).

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

Для перехода к созданным задачам кликните по ссылке Проверка созданных проблем (Check created issues).

Частая проблема — невозможность задать тип создаваемых задач в Jira (стандартное поле Issue Type). Это легко исправляется при помощи стандартной функции массового перемещения в Jira.

Вместо послесловия

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

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

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

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

P.S.

Существует поверье, что один раз один автор Habr не написал про ChatGPT, и теперь никто не знает, где он находится. Попробую уберечь себя от этой участи.

Вариант VBA-скрипта, предложенный сервисом, предлагает открывать документ из Excel и анализировать его, целиком исходя из названий стилей:

Sub ImportParagraphs()
    Dim objWord As Object
    Dim objDoc As Object
    Dim objRange As Object
    Dim objPara As Object
    Dim i As Long
    
    Set objWord = CreateObject("Word.Application")
    Set objDoc = objWord.Documents.Open("C:\Documents\example.docx") ' замените на путь к вашему документу Word
    Set objRange = objDoc.Range
    
    For Each objPara In objRange.Paragraphs
        i = i + 1
        If objPara.Style = "Heading 1" Then
            Range("A" & i).Value = objPara.Range.Text
        ElseIf objPara.Style = "Heading 2" Then
            Range("B" & i).Value = objPara.Range.Text
        ElseIf objPara.Style = "Heading 3" Then
            Range("C" & i).Value = objPara.Range.Text
        Else
            Range("A" & i).Value = objPara.Range.Text
        End If
    Next objPara
    
    objDoc.Close
    objWord.Quit
    Set objWord = Nothing
    Set objDoc = Nothing
    Set objRange = Nothing
    Set objPara = Nothing
    
End Sub

Комментарии (0)