Please use this identifier to cite or link to this item:
https://er.chdtu.edu.ua/handle/ChSTU/9680| Title: | Хмарний застосунок для оцінки ефективності та прогнозування навантаження Agile-команд з використанням метрик якості |
| Authors: | Оксамитна, Любов Павлівна Антоневич, Артем Вячеславович |
| Keywords: | AGILE;JIRA;ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ;HEALTH SCORE;МЕТОД МОНТЕ-КАРЛО;ХМАРНИЙ ЗАСТОСУНОК;МЕТРИКИ ЯКОСТІ;CYCLE TIME. |
| Issue Date: | 10-Jun-2026 |
| Abstract: | У сучасній розробці програмного забезпечення за методологією Agile критично важливим є точне прогнозування термінів виходу продукту (Time-to-Market). Проте, більшість існуючих аналітичних інструментів для Jira фокусуються виключно на швидкості (Velocity), ігноруючи якісні показники та технічний борг. Створення хмарного застосунку, що інтегрує метрики якості в модель прогнозування, дозволить підвищити точність планування та оптимізувати навантаження команд. Основні положення і результати кваліфікаційної роботи бакалавра доповідалися і були обговорені на конференції «День студентської науки кафедри КНСА», 22 квітня 2026 року, Черкаси, Україна. Практичне значення отриманих результатів полягає у тому, що розроблений застосунок надає Scrum Master’ам та Project Manager’ам інструмент планування, що враховує реальний стан процесної дисципліни команди. Прогноз завершення беклогу, побудований на основі Health Score та методу Монте-Карло, дає реалістичнішу картину порівняно зі стандартною екстраполяцією Velocity. Застосунок підключається до Jira через OAuth 2.0 без встановлення плагіна і доступний будь-якій команді з браузера. Hexagonal архітектура забезпечує незалежність бізнес-логіки від інфраструктури, що спрощує подальшу підтримку та розширення системи. |
| URI: | https://er.chdtu.edu.ua/handle/ChSTU/9680 |
| Appears in Collections: | 122 Комп’ютерні науки (Комп’ютерні науки та прикладне програмування) |
Files in This Item:
| File | Description | Size | Format | |
|---|---|---|---|---|
| Пояснювальна записка_Антоневич Артем_КН-2201_2025-2026.pdf Restricted Access | 1.74 MB | Adobe PDF | View/Open Request a copy |
Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.
Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
Факультет інформаційних технологій і систем
Кафедра комп’ютерних наук та системного аналізу
Пояснювальна записка
до кваліфікаційної роботи
бакалавра
(освітньо-кваліфікаційний рівень)
на тему: «Хмарний застосунок для оцінки ефективності та прогнозування
навантаження Agile-команд»
Виконав: студент 4 курсу, групи КН-2201
Спеціальності 122 «Комп’ютерні науки»
(шифр і назва спеціальності)
Освітня програма «Комп’ютерні науки та
(назва освітньої програми)
прикладне програмування»
Артем АНТОНЕВИЧ
Керівник Любов ОКСАМИТНА
(ім’я та прізвище)
Рецензент Сергій СТАСЬ
(ім’я та прізвище)
Черкаси 2026 року
2
Бланк завдання на кваліфікаційну роботу бакалавра студенту
Черкаський державний технологічний університет
Факультет Інформаційних технологій і систем
Кафедра Комп’ютерних наук та системного аналізу
Освітньо-кваліфікаційний рівень Бакалавр
Спеціальність 122 – Комп’ютерні науки
Освітня програма Комп’ютерні науки та прикладне програмування
ЗАТВЕРДЖУЮ
Завідувач кафедри КНСА
_______________ Юрій ТРИУС
«____» _____________ 2026 р.
ЗАВДАННЯ
на кваліфікаційну роботу бакалавра студенту
Антоневичу Артему Вячеславовичу
(прізвище, ім‘я, по батькові)
1. Тема роботи Хмарний застосунок для оцінки ефективності та прогнозування навантаження
Agile-команд
Керівник роботи Оксамитна Л.П., к.т.н., доцент
(прізвище, ім’я, по батькові, науковий ступінь, вчене звання)
затверджені наказом університету від «12» березня 2026 р. № 56/03-03.
2. Строк подання студентом роботи «08» червня 2026 року
3. Вихідні дані до роботи:
Практичні навички роботи з інформаційними ресурсами. Робота з базами даних.
Звіт з виробничої практики. Звіт з переддипломної практики.
4. Зміст пояснювальної записки (перелік питань, що їх належить розробити):
Вступ
4.1. Дослідження предметної області.
4.2. Проєктування хмарного застосунку для оцінки ефективності та прогнозування
навантаження Agile-команд.
4.3. Програмна реалізація.
Висновки.
5. Перелік додатків (з точним зазначенням назв додатків):
5.1. Додаток А. Специфікація 482. ЧДТУ. 62292-01.
5.2. Додаток Б. Текст програми.
5.3. Додаток В. Інструкція користувача.
5.4. Додаток Г. Структура бази даних
5.5. Додаток Д. Апробація кваліфікаційної роботи бакалавра
5.4 Презентація у вигляді 23 слайдів.
3
6. Консультанти розділів роботи
Прізвище, ініціали та Підпис, дата
Розділ посада
консультанта завдання видав завдання прийняв
7. Дата видачі завдання 12.01.2026 р.
КАЛЕНДАРНИЙ ПЛАН
№ з/п Назва етапів кваліфікаційної роботи бакалавра Строк виконання
етапів роботи Примітка
1 Видача завдання на кваліфікаційну роботу
бакалавра. 12.01.2026 Виконано
2 Аналіз літературних джерел, об’єкту та предмету
дослідження. до 08.02.2026 Виконано
3 Написання теоретичного розділу кваліфікаційної
роботи бакалавра. до 23.03.2026 Виконано
4 Написання аналітичного розділу (аналіз об’єкту
й предмету дослідження). до 06.04.2026 Виконано
5 Написання практичних розділів й висновків по
роботі. до 15.05.2026 Виконано
6 Передзахист кваліфікаційної роботи бакалавра
на засіданні кафедри КНСА. 20.05.2026 Виконано
7 Подання роботи завідувачу кафедри КНСА. до 10.06.2026 Виконано
8 Захист кваліфікаційної роботи бакалавра. 10.06.2026 Виконано
Студент Артем АНТОНЕВИЧ
(підпис)
Керівник роботи Любов ОКСАМИТНА
(підпис)
4
РЕФЕРАТ
Кваліфікаційна робота бакалавра містить: 92 с., 3 рис., 2 таблиці, 22
використаних джерел, 5 додатків.
Актуальність теми. У сучасній розробці програмного забезпечення за
методологією Agile критично важливим є точне прогнозування термінів виходу
продукту (Time-to-Market). Проте, більшість існуючих аналітичних інструментів для
Jira фокусуються виключно на швидкості (Velocity), ігноруючи якісні показники та
технічний борг. Створення хмарного застосунку, що інтегрує метрики якості в модель
прогнозування, дозволить підвищити точність планування та оптимізувати
навантаження команд.
Мета роботи і задачі дослідження. Метою кваліфікаційної роботи бакалавра є
розробка хмарного застосунку для автоматизованої оцінки ефективності Agile-
команд та стохастичного прогнозування навантаження на основі штрафної моделі
Health Score й методу Монте-Карло.
Завдання кваліфікаційної роботи бакалавра:
− проаналізувати методологію Agile та провести порівняльний аналіз існуючих
інструментів аналітики для Jira: ActionableAgile, EazyBI, Screenful;
− розробити штрафну модель Health Score, що враховує шість компонентів
процесної дисципліни: estimation coverage, capacity mismatch, completion rate, split-
задачі, reopen count та execution rate;
− реалізувати алгоритм стохастичного прогнозування методом Монте-Карло
(5 000 симуляцій, результати P10/P50/P90 та probability);
− спроєктувати архітектуру застосунку за принципом Hexagonal (Ports &
Adapters) на стеку Next.js 16 + Supabase + Vercel з інтеграцією Jira REST API v3 через
OAuth 2.0;
− реалізувати capacity planning з урахуванням productivity_coefficient кожного
члена команди та їхніх відпусток;
− провести тестування алгоритмів за допомогою Vitest (43 тест-кейси).
5
Об’єкт дослідження: процеси оцінки ефективності та прогнозування
навантаження Agile-команд в ІТ-проєктах.
Предмет дослідження: методи оцінки процесної дисципліни команди та
стохастичного прогнозування термінів завершення беклогу на основі метрик якості.
Методи дослідження. Порівняльний аналіз для оцінки існуючих
інструментів; штрафна модель оцінки для обчислення Health Score; метод Монте-
Карло для стохастичного прогнозування; гексагональна архітектура (Ports &
Adapters) для проєктування застосунку; OAuth 2.0 для безпечної інтеграції з Jira.
Апробація результатів роботи. Основні положення і результати
кваліфікаційної роботи бакалавра доповідалися і були обговорені на конференції
«День студентської науки кафедри КНСА», 22 квітня 2026 року, Черкаси, Україна.
Публікації. Антоневич А. В., Оксамитна Л. П. Автоматизація процесу Capacity
Planning в Agile-командах за допомогою хмарних рішень : Збірник тез доповідей
студентської науково-практичної конференції ЧДТУ : 22-23 квітня 2026 / [упорядник
Мельник І.В.]; Міністерство освіти і науки України, Черкаський державний
технологічний ун-т. Черкаси : ЧДТУ, 2026. С. 9.
Практичне значення отриманих результатів полягає у тому, що розроблений
застосунок надає Scrum Master’ам та Project Manager’ам інструмент планування, що
враховує реальний стан процесної дисципліни команди. Прогноз завершення беклогу,
побудований на основі Health Score та методу Монте-Карло, дає реалістичнішу
картину порівняно зі стандартною екстраполяцією Velocity. Застосунок
підключається до Jira через OAuth 2.0 без встановлення плагіна і доступний будь-якій
команді з браузера. Hexagonal архітектура забезпечує незалежність бізнес-логіки від
інфраструктури, що спрощує подальшу підтримку та розширення системи.
Ключові слова: AGILE, JIRA, ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ,
HEALTH SCORE, МЕТОД МОНТЕ-КАРЛО, ХМАРНИЙ ЗАСТОСУНОК, CYCLE
TIME, ВЕЛОСІТІ, МЕТРИКИ ЯКОСТІ.
ABSTRACT
The bachelor’s qualification thesis consists of: 92 pages, 3 illustrations, 2 tables,
22 references, and 5 attachments.
6
Relevance of the topic. In modern software development using the Agile
methodology, accurate Time-to-Market forecasting is critical. However, most existing
analytical tools for Jira focus exclusively on velocity, ignoring quality indicators and
technical debt. Creating a cloud application that integrates quality metrics into the
forecasting model will increase planning accuracy and optimize team workload.
Purpose and objectives of the research. The purpose of the bachelor's qualification
work is to develop a cloud application for automated evaluation of Agile teams' efficiency
and stochastic workload forecasting based on the Health Score penalty model and the Monte
Carlo method.
The tasks of the bachelor's qualification work:
− analyze Agile methodology and conduct a comparative analysis of existing
analytics tools for Jira: ActionableAgile, EazyBI, Screenful;
− develop a Health Score penalty model incorporating six process discipline
components: estimation coverage, capacity mismatch, completion rate, split tasks, reopen
count, and execution rate;
− implement a stochastic forecasting algorithm using the Monte Carlo method (5,000
simulations, P10/P50/P90 and probability results);
− design the application architecture following the Hexagonal (Ports & Adapters)
principle on the Next.js 16 + Supabase + Vercel stack with Jira REST API v3 integration
via OAuth 2.0;
− implement capacity planning considering the productivity coefficient of each team
member and their vacation periods;
− conduct algorithm testing using Vitest (43 test cases).
Object of study: processes of efficiency evaluation and workload forecasting of
Agile teams in IT projects.
Subject of the study: methods for evaluating team process discipline and stochastic
forecasting of backlog completion timelines based on quality metrics.
Research Methods. Comparative analysis for evaluation of existing tools; penalty-
based assessment model for computing the Health Score; Monte Carlo method for stochastic
7
forecasting; Hexagonal architecture (Ports & Adapters) for application design; OAuth 2.0
for secure integration with Jira.
Approbation of the results of the work. The main provisions and results of the
bachelor's qualification work were reported and discussed at the conference "Day of Student
Science of the Department of KNSA", April 22, 2026, Cherkasy, Ukraine.
Publications. Antonevych A. V., Oksamytna L. P. Automation of the Capacity
Planning Process in Agile Teams Using Cloud Solutions : Proceedings of the Student
Scientific and Practical Conference of ChSTU : April 22–23, 2026 / [compiled by Melnyk
I. V.]; Ministry of Education and Science of Ukraine, Cherkasy State Technological
University. Cherkasy : ChSTU, 2026. P. 9.
The practical significance of the results obtained lies in providing Scrum Masters
and Project Masters with a planning tool that accounts for the real state of the team's process
discipline. A backlog completion forecast built on the Health Score and the Monte Carlo
method delivers a more realistic picture compared to standard Velocity extrapolation. The
application connects to Jira via OAuth 2.0 without plugin installation and is accessible to
any team from a browser. The Hexagonal architecture ensures independence of business
logic from infrastructure, simplifying further maintenance and extension of the system.
Keywords: AGILE, JIRA, WORKLOAD FORECASTING, HEALTH SCORE,
MONTE CARLO METHOD, CLOUD APPLICATION, CYCLE TIME, VELOCITY,
QUALITY METRICS.
8
ЗМІСТ
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ 10
ВСТУП………………………………………………………………………………. 13
1 ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ…………………………………... 16
1.1 Призначення та область застосування методології Agile………………... 16
1.2 Аналіз теми дослідження ………………………………………………….. 17
1.3 Аналіз ринку та порівняння існуючих рішень для оцінки ефективності
та прогнозування в екосистемі Jira………………………………………... 18
1.3.1 ActionableAgile Analytics: еталон аналізу потоку………………...... 18
1.3.2 EazyBI Reports and Charts for Jira: потужність OLAP-аналітики…... 19
1.3.3 Screenful: візуалізація та простота…………………………………... 19
1.4 Порівняльний аналіз існуючих рішень та постановка завдання………… 20
Висновки до розділу 1…………………………………………………………... 23
2 ПРОЄКТУВАННЯ ХМАРНОГО ЗАСТОСУНКУ ДЛЯ ОЦІНКИ
ЕФЕКТИВНОСТІ ТА ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-
КОМАНД…………………………………………………………………………. 24
2.1 Теоретичні основи методу прогнозування, скоригованого на метрики
якості Quality-Weighted Forecasting………………………………………... 24
2.2 Опис методів для оцінки ефективності навантаження Agile-команд…… 25
2.3 Опис методів для прогнозування навантаження Agile-команд………….. 27
2.4 Обґрунтування вибору сучасних технологій для створення хмарного
застосунку для оцінки ефективності та прогнозування навантаження
Agile-команд………………………………………………………………… 28
2.5 Проєктування структури бази даних……………………………………….. 32
2.6 Опис процесу проєктування хмарного застосунку для оцінки
ефективності та прогнозування навантаження Agile-команд ……………. 34
2.6.1 Опис предметної області…………………………………………….. 34
2.6.2 Формування вимог до хмарного застосунку……………………….. 35
2.6.3 Проєктування інтерфейсу користувача…………………………….. 36
9
2.6.4 Проєктування та розробка хмарного застосунку для оцінки
ефективності та прогнозування навантаження Agile-команд……... 38
Висновки до розділу 2…………………………………………………………..... 41
3 ПРОГРАМНА РЕАЛІЗАЦІЯ ……………………………………………………. 43
3.1 Вибір та обґрунтування методів вирішення проблеми……………………. 43
3.2 Інтерфейс хмарного застосунку для оцінки ефективності та
прогнозування навантаження Agile-команд……………………………….. 44
3.3 Програмна реалізація хмарного застосунку для оцінки ефективності та
прогнозування навантаження Agile-команд……………………………….. 46
3.4 Вибір та обгрунтування методів тестування хмарного застосунку………. 49
3.5 Тестування хмарного застосунку для оцінки ефективності та
прогнозування навантаження Agile-команд……………………………….. 49
Висновки до розділу 3…………………………………………………………... 52
ВИСНОВКИ………...……………………………………………...………………... 54
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ………………...………………................. 57
ДОДАТОК А. Специфікація 482. ЧДТУ. 62292-01……………….......................... 60
ДОДАТОК Б. Текст програми………………...……………….................................. 62
ДОДАТОК В. Інструкція користувача………………...……………….................... 76
ДОДАТОК Г. Структура бази даних………………...………………...................... 83
ДОДАТОК Д. Апробація кваліфікаційної рооботи бакалавра ………………….... 89
10
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ
БД – база даних;
ПЗ – програмне забезпечення;
СУБД – система керування базами даних;
AES-256-GCM – Advanced Encryption Standard з 256-бітним ключем у ре±жимі
Galois/Counter Mode (стандарт симетричного шифрування з автентифікацією);
API – Application Programming Interface (інтерфейс програмування застосунків);
CSS – Cascading Style Sheets (каскадні таблиці стилів);
DD – Defect Density (щільність дефектів – відношення кількості Bug-задач до
виконаного обсягу роботи);
HMAC – Hash-based Message Authentication Code (код автентифікації повідомлень на
основі хешу);
HTTP – HyperText Transfer Protocol (протокол передачі гіпертексту);
HTTPS – HyperText Transfer Protocol Secure (захищений протокол передачі
гіпертексту);
ID – Identifier (ідентифікатор);
IT – Information Technology (інформаційні технології);
IV – Initialization Vector (ініціалізаційний вектор у алгоритмах шифрування);
JSON – JavaScript Object Notation (текстовий формат обміну даними);
JSONB – JSON Binary (двійковий формат зберігання JSON у PostgreSQL);
KPI – Key Performance Indicator (ключовий показник ефективності);
OAuth 2.0 – Open Authorization 2.0 (відкритий стандарт авторизації);
OAuth 2.0 (3LO) – трьохстороннє OAuth-делегування для авторизації зовнішніх
застосунків до Jira Cloud;
OLAP – Online Analytical Processing (аналітична обробка даних в режимі реального
часу);
P10 – 10-й перцентиль розподілу результатів Монте-Карло симуляції (оптимістична
оцінка);
P50 – 50-й перцентиль (медіана) розподілу результатів симуляції;
11
P90 – 90-й перцентиль розподілу результатів симуляції (консервативна оцінка);
QWF – Quality-Weighted Forecasting (прогнозування навантаження, скориговане на
метрики якості процесу);
REST – Representational State Transfer (архітектурний стиль побудови веб-сервісів);
RLS – Row Level Security (безпека на рівні рядків у PostgreSQL);
RR – Reopen Rate (частота перевідкриттів задач після їх закриття);
SHA-256 – Secure Hash Algorithm 256-bit (алгоритм захищеного хешування);
SQL – Structured Query Language (мова структурованих запитів до реляційних БД);
SSR – Server-Side Rendering (серверний рендеринг сторінок);
UI – User Interface (інтерфейс користувача);
UML – Unified Modeling Language (уніфікована мова моделювання);
URL – Uniform Resource Locator (уніфікований локатор ресурсів);
UUID – Universally Unique Identifier (універсальний унікальний ідентифікатор);
WIP – Work In Progress (задача у стані виконання; також WIP-Limit – обмеження
незавершеної роботи в Kanban).
Терміни:
Agile – сімейство методологій гнучкого управління IT-проєктами, засноване на Agile
Manifesto (2001);
Backlog – впорядкований перелік задач, що очікують виконання у Jira-проєкті;
Capacity Planning – планування доступної ємності команди з урахуванням відпусток
та коефіцієнта продуктивності кожного учасника;
Changelog – журнал переходів задачі між статусами в Jira, що використовується для
розрахунку Cycle Time та Reopen Rate;
Cron – планувальник задач, що запускає функції за часовим розкладом (формат cron-
виразу);
Cycle Time – час від початку активної роботи над задачею до її завершення;
Health Score – інтегральний показник процесної дисципліни команди у спринті за
шкалою 0–100, що обчислюється штрафною моделлю з шести компонентів;
Jira – система управління проєктами та відстеження задач від компанії Atlassian;
12
Kanban – Agile-метод безперервного управління потоком задач без фіксованих
ітерацій;
Monte Carlo – метод стохастичного моделювання для прогнозування термінів
завершення беклогу шляхом статистичної вибірки з исторії Velocity;
PostgreSQL – об'єктно-реляційна СУБД з відкритим кодом з підтримкою типу JSONB;
Scrum – ітеративний Agile-фреймворк з фіксованими спринтами та визначеними
ролями;
Serverless – модель хмарних обчислень, де виконання коду забезпечується
провайдером без управління серверами з боку розробника;
Sprint – фіксований часовий проміжок ітерації в Scrum (1–4 тижні), протягом якого
команда виконує заплановані задачі;
Story Points – відносна одиниця вимірювання складності задачі, що використовується
для розрахунку Velocity;
Supabase – хмарна платформа на базі PostgreSQL зі вбудованим REST API та пакетом
@supabase/ssr для Next.js;
Throughput – кількість задач, завершених командою за одиницю часу (спринт або
тиждень);
Token – цифровий маркер авторизації OAuth 2.0, що надає право доступу до ресурсів
Jira API;
Velocity – кількість story points, завершених командою за один спринт;
Vercel – платформа для serverless-деплою Next.js застосунків з вбудованою
підтримкою cron jobs;
Webhook – HTTP-запит, що автоматично надсилається зовнішньою системою (Jira)
при настанні певної події.
13
ВСТУП
Команди, що розробляють програмне забезпечення за Agile-методологіями,
стикаються з парадоксом планування: методологія побудована на прозорості та
передбачуваності, проте більшість інструментів прогнозування навантаження дають
систематично оптимістичні оцінки. Причина таких результатів аналізів полягає в
структурному обмеженні наявних рішень: вони спираються виключно на кількісні
метрики (Velocity, Throughput) і не враховують якісний стан команди. Forsgren,
Humble та Kim у праці «Accelerate» (2018) встановили, що команди з накопиченим
технічним боргом витрачають до 38% робочого часу на незаплановану роботу, що
безпосередньо занижує реальну пропускну здатність у наступних спринтах [6]. Цю
похибку жоден із поширених аналітичних інструментів для Jira не компенсує.
Актуальність теми. Актуальність кваліфікаційної роботи бакалавра «Хмарний
застосунок для оцінки ефективності та прогнозування навантаження Agile-команд»
визначається двома взаємопов'язаними проблемами. Перша – методологічна:
відсутність інструменту, який оцінює процесну дисципліну команди через єдиний
інтегральний показник і враховує цю оцінку при побудові прогнозу завершення
беклогу. Існуючі рішення – ActionableAgile, EazyBI, Screenful – або застосовують
стохастичне прогнозування без урахування якості, або надають метрики якості
ізольовано від прогнозної моделі. Друга – технологічна: наявні аналітичні рішення
реалізовані як marketplace-плагіни для Jira, що потребують адміністративних прав для
встановлення. Самостійний веб-застосунок, що підключається до Jira через
стандартний OAuth 2.0 без втручання в конфігурацію Jira-інстансу, відсутній як клас
рішень у цьому сегменті.
Зв'язок роботи з науковими програмами, планами і темами кафедри.
Роботу виконано на кафедрі комп'ютерних наук та системного аналізу Черкаського
державного технологічного університету відповідно до плану підготовки бакалаврів
зі спеціальності 122 «Комп'ютерні науки». Тематика роботи відповідає науковому
напряму кафедри в галузі розробки прикладних інформаційних систем та
автоматизації процесів управління.
14
Мета і завдання дослідження. Метою роботи є розробка хмарного застосунку
для автоматизованої оцінки ефективності Agile-команд та стохастичного
прогнозування навантаження на основі штрафної моделі Health Score й методу Монте-
Карло.
Для досягнення мети вирішено такі завдання:
− проаналізувати методологію Agile та провести порівняльний аналіз існуючих
інструментів аналітики для Jira: ActionableAgile, EazyBI, Screenful;
− розробити штрафну модель Health Score, що враховує шість компонентів
процесної дисципліни: estimation coverage, capacity mismatch, completion rate, split-
задачі, reopen count та execution rate;
− реалізувати алгоритм стохастичного прогнозування методом Монте-Карло
(5 000 симуляцій, результати P10/P50/P90 та probability);
− спроєктувати архітектуру застосунку за принципом Hexagonal (Ports &
Adapters) на стеку Next.js 16 + Supabase + Vercel з інтеграцією Jira REST API v3 через
OAuth 2.0;
− реалізувати capacity planning з урахуванням productivity_coefficient кожного
члена команди та їхніх відпусток;
− провести тестування алгоритмів за допомогою Vitest (43 тест-кейси).
Об'єкт дослідження: процеси оцінки ефективності та прогнозування
навантаження Agile-команд в ІТ-проєктах.
Предмет дослідження: методи оцінки процесної дисципліни команди та
стохастичного прогнозування термінів завершення беклогу на основі метрик якості.
Методи дослідження. У роботі використано: порівняльний аналіз для оцінки
існуючих інструментів; штрафну модель оцінки для обчислення Health Score; метод
Монте-Карло для стохастичного прогнозування; гексагональну архітектуру (Ports &
Adapters) для проєктування застосунку; OAuth 2.0 (3LO) для безпечної інтеграції з
Jira; AES-256-GCM для шифрування облікових даних; Vitest для unit-тестування
доменної логіки.
15
Апробація результатів роботи. Основні положення та результати
кваліфікаційної роботи доповідалися й обговорювалися на конференції «День
студентської науки кафедри КНСА» у квітні 2026 року.
Публікації. Антоневич А. В., Оксамитна Л. П. Автоматизація процесу Capacity
Planning в Agile-командах за допомогою хмарних рішень : Збірник тез доповідей
студентської науково-практичної конференції ЧДТУ : 22-23 квітня 2026 / [упорядник
Мельник І.В.]; Міністерство освіти і науки України, Черкаський державний
технологічний ун-т. Черкаси : ЧДТУ, 2026. С. 9.
Практичне значення отриманих результатів. Розроблений застосунок надає
Scrum Master'ам та Project Manager'ам інструмент планування, що враховує реальний
стан процесної дисципліни команди. Прогноз завершення беклогу, побудований на
основі Health Score та методу Монте-Карло, дає реалістичнішу картину порівняно зі
стандартною екстраполяцією Velocity. Застосунок підключається до Jira через OAuth
2.0 без встановлення плагіна і доступний будь-якій команді з браузера. Hexagonal
архітектура забезпечує незалежність бізнес-логіки від інфраструктури [13].
16
1 ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ
1.1 Призначення та область застосування методології Agile
Agile – це набір принципів гнучкого управління проєктами, що з'явився у 2001
році разом з «Маніфестом гнучкої розробки програмного забезпечення» (Agile
Manifesto). Маніфест містить чотири цінності: людей і взаємодію вище процесів й
інструментів, працюючий продукт вище вичерпної документації, співпрацю з
замовником вище жорстких контрактів, реакцію на зміни вище слідування плану [2].
Під цими принципами існує кілька конкретних фреймворків: Scrum, Kanban,
Extreme Programming (XP), Lean. Кожен реалізує їх по-своєму, проте всі будуються
на ітеративно-інкрементальній розробці.
Scrum – найпоширеніший з них. Робота ділиться на спринти тривалістю від
одного до чотирьох тижнів. Product Backlog містить усі вимоги до продукту; Sprint
Backlog – те, що команда взяла в поточний спринт. Спринт починається з
планування (Sprint Planning), щодня проводиться коротка нарада (Daily Scrum), а
наприкінці – перегляд результату (Sprint Review) та ретроспектива (Sprint
Retrospective).
Kanban не має фіксованих ітерацій. Натомість він акцентує на безперервному
потоці завдань і двох метриках: Cycle Time (час від початку роботи над задачею до
її завершення) та Throughput (кількість задач, виконаних за одиницю часу).
Обмеження незавершеної роботи (WIP-Limit) не дозволяє команді
перевантажуватися і тримає потік передбачуваним.
Сьогодні Agile використовується далеко за межами IT – у маркетингу,
фінансах, освіті. Але саме в розробці програмного забезпечення він набув
найбільшої зрілості. За даними State of Agile Report (2023), понад 70% IT-організацій
застосовують Agile-практики в тій чи іншій формі [4].
Jira Software від Atlassian – одна з найпоширеніших систем, де ці практики
реалізуються на рівні інструменту. Вона дозволяє вести беклог, планувати спринти
та відстежувати задачі [5]. Jira Cloud надає REST API v3 [8], через який зовнішні
застосунки можуть отримувати дані про спринти, задачі та їхню историю переходів
17
між статусами. Саме ця можливість лежить в основі застосунку, що розробляється:
збір аналітичних даних через авторизований API-доступ без встановлення плагіна
безпосередньо у Jira.
1.2 Актуальність теми дослідження
Agile вирішив проблему негнучкого планування, але породив нову. Команди,
що працюють під тиском дедлайнів, намагаються підвищити Velocity – кількість
Story Points або годин роботи за спринт. Оскільки Velocity вимірює обсяг, а не якість,
виникає спокуса закривати задачі швидше, скорочуючи час на тестування та
відкладаючи рефакторинг. У підсумку, Velocity залишається стабільною або навіть
зростає, а реальна продуктивність команди знижується.
Це явище Martin Fowler описував як «технічний борг» ще в 1992 році: кожен
свідомо залишений недолік коду в майбутньому коштуватиме більше, ніж коштував
би зараз [7]. Forsgren, Humble та Kim у книзі «Accelerate» (2018) підтвердили це
статистично: команди з великим технічним боргом витрачають до 38% робочого
часу на «виплату відсотків» – виправлення помилок замість нових функцій.
Проблема в тому, що більшість інструментів прогнозування цього не
враховують. Якщо команда виконувала 30 Story Points у п'яти останніх спринтах,
інструмент прогнозує ті ж 30 і надалі. Але якщо в цей час накопичувався технічний
борг або зростав відсоток перевідкритих задач – реальна продуктивність почне
падати, а прогноз цього не покаже. Зростання Reopen Rate (частки задач, повернених
у роботу після завершення) означає, що команда фактично виконує більше роботи,
ніж показує Velocity.
Для коригування прогнозів необхідні показники дисципліни процесу. Defect
Density – кількість задач типу Bug відносно обсягу виконаної роботи; її зростання є
раннім сигналом проблем з якістю. Reopen Rate – частка задач, повернених у роботу
після закриття; висока частота свідчить про неякісне тестування або нечіткі критерії
завершення. Estimation discipline – чи команда оцінює задачі перед початком
спринту; відсутність оцінок ускладнює capacity planning. Capacity mismatch – розрив
між тим, скільки команда реально може і скільки вона взяла в спринт.
18
Другий аспект актуальності теми дослідження – відсутність доступного
standalone-рішення для комплексного аналізу Agile-команд. Більшість аналітичних
інструментів для Jira – це marketplace-плагіни, що потребують встановлення в Jira-
інстанс, мають обмежений доступ для зовнішніх команд або організацій з
жорсткими вимогами до плагінів. Альтернативою є самостійний веб-застосунок, що
підключається до Jira через стандартний OAuth 2.0 (3LO), зберігає дані в PostgreSQL
та розгортається на serverless-платформі [12]. Такий підхід не потребує
адміністративних прав у Jira для встановлення плагіна і доступний з будь-якого
браузера.
Таким чином, тема кваліфікаційної роботи бакалавра стоїть на перетині двох
проблем: відсутності інструменту, що оцінює процесну дисципліну команди через
штрафну модель Health Score та інтегрує цю оцінку в стохастичне прогнозування
навантаження; відсутності доступного рішення в форматі standalone-застосунку з
відкритою OAuth 2.0 інтеграцією з Jira.
1.3 Аналіз ринку та порівняння існуючих рішень для оцінки ефективності
та прогнозування в екосистемі Jira
На Atlassian Marketplace є сотні інструментів звітності для Jira. Для порівняння
обрано трьох найбільших гравців, що представляють різні підходи: ActionableAgile
Analytics (метрики потоку і Монте-Карло), EazyBI (OLAP-аналітика загального
призначення) та Screenful (готові дашборди без налаштування). Жоден із них не
поєднує оцінку процесної дисципліни зі стохастичним прогнозуванням у форматі
самостійного веб-застосунку з OAuth 2.0 інтеграцією.
1.3.1 ActionableAgile Analytics: еталон аналізу потоку. ActionableAgile від
55 Degrees AB розроблявся саме для Kanban-команд і є де-факто стандартом у
застосуванні методу Монте-Карло до Jira [9]. Інструмент відповідає на запитання
«коли буде готово?» із зазначенням ймовірності: наприклад, «85% ймовірність
завершення 10 задач за 14 днів». Це суттєво корисніше, ніж звичайна дата без
розуміння ризиків.
Крім Монте-Карло, ActionableAgile надає аналіз Cycle Time, Throughput та
19
Work Item Age. Остання метрика показує, скільки часу задача вже перебуває в
незавершеному стані, – це дозволяє виявляти «застряглі» задачі до того, як вони
стануть кризою. Можна динамічно включати або виключати окремі статуси з
розрахунків і фільтрувати за типом задач.
Принципова проблема – інструмент прогнозує на основі виключно кількісної
історії. Якщо команда мала високий Throughput, тому що скорочувала тестування, –
ActionableAgile цього не побачить. Defect Density у модель не входить. Ціна для
великих команд становить близько $1.4–$1.5 за користувача на місяць, і враховуючи
ліцензійну модель Atlassian (оплата за всіх користувачів інстансу), сума швидко
зростає. Інтерфейс орієнтований на фахівців з Kanban, що ускладнює adoption у
звичайних Scrum-командах.
1.3.2 EazyBI Reports and Charts for Jira: потужність OLAP-аналітики.
EazyBI – інструмент бізнес-аналітики загального призначення [10]. Він імпортує
дані Jira у власну базу, будує OLAP-куби та дозволяє аналізувати їх у будь-яких
розрізах: за часом, проєктом, автором, статусом, кастомними полями .
Головна сила EazyBI – мова MDX (MultiDimensional eXpressions), через яку
можна реалізувати практично будь-який алгоритм. Наприклад, порахувати середній
час перебування в конкретному статусі тільки для задач, що були перевідкриті. Є
інтеграція з Bitbucket та Bamboo, що дозволяє будувати звіти по метриках DORA.
Проте, MDX – це повноцінна мова запитів для кубів даних. Для Scrum-майстра
чи Product Owner'а без технічного бекграунду порогом входу буде не налаштування,
а навчання. AI-асистент для формул з'явився нещодавно, але складні прогнозні
моделі залишаються роботою для аналітика даних. Ще одне обмеження: EazyBI
імпортує дані за розкладом, зазвичай раз на годину або добу, тому звіти не є
реалтаймовими. З грудня 2025 року ціна для малих тірів зросла приблизно на 20% і
становить від $2.50/користувача (малі команди) до $0.20/користувача (великі).
1.3.3 Screenful: візуалізація та простота. Screenful вирішує задачу,
протилежну EazyBI: мінімум налаштувань, максимум готових звітів. Після
підключення до дошки Jira одразу доступні Velocity, Lead Time та Cycle Time – без
жодних налаштувань [11].
20
Серед конкурентів Screenful вирізняється наявністю віджета «Escaped
Defects», що показує баги, знайдені після релізу. Є підтримка TV mode –
відображення дашборду на офісному екрані – можливість вбудовування в
корпоративні портали.
Глибина прогнозування обмежена: Screenful використовує лінійну
екстраполяцію в Release Burndown. У стабільних умовах це прийнятно, але при
нестабільному потоці лінійний прогноз помиляється значно частіше, ніж Монте-
Карло. Метрики якості та швидкості існують окремо: «Escaped Defects» не впливає
на розрахунок прогнозу. Ціна фіксована – від $79 до $399 на місяць залежно від
плану.
1.4 Порівняльний аналіз існуючих рішень та постановка завдання
Проаналізовані інструменти закривають різні задачі, але жоден не вирішує ту,
що стоїть перед цією роботою: оцінка процесної дисципліни команди через штрафну
модель Health Score та її інтеграція у стохастичне прогнозування навантаження у
форматі самостійного веб-застосунку. Таблиця 1.1 зводить ключові характеристики
для наочного порівняння.
Таблиця 1.1 – Порівняльний аналіз систем аналітики та прогнозування для Jira
Критерій Actionable EazyBI Reports Agile Forecaster
Agile Analytics & Charts Screenful (розроблюваний)
Метод Монте-Карло Будь-який Лінійна Монте-Карло: 5 000
прогнозу- на основі алгоритм через екстраполяція симуляцій,
вання Throughput; MDX-запити (Release P10/P50/P90,
Process (реалізація Burndown) probability
Behaviour вручну)
Charts
Результат Ймовірнісний Будь-який Точкова P10 / P50 / P90 +
прогнозу діапазон формат через оцінка probability для
(n задач за k MDX (лінійна) заданого targetSprints
тижнів з X%)
Метрики Відсутні (лише Гнучко через Escaped 6 компонентів Health
якості кількісні: MDX, готових Defects Score: estimation,
Cycle Time, шаблонів (ізольовано capacity mismatch,
немає від прогнозу)
21
Критерій Actionable EazyBI Reports Screenful Agile Forecaster
Agile Analytics & Charts (розроблюваний)
Throughput, completion, split,
WIA) reopens, execution
Health Відсутній Відсутній Відсутній Є: штрафна модель
Score / (потребує Health Score →
інтеграція розробки враховується у QWF
якості MDX) (заплановано)
у прогноз
Capacity Відсутній Відсутній Відсутній Є:
Planning productivity_coefficient
на рівні × (workingDays −
учасника daysOff) × 8 год
Формат Jira Jira SaaS Standalone веб-
розгор- Marketplace Marketplace (окремий застосунок
тання plugin plugin (Cloud / сервіс) (Vercel Serverless)
Server)
Спосіб Marketplace Marketplace OAuth 2.0 OAuth 2.0 (3LO) – без
інтеграції plugin – plugin – (SaaS) встановлення плагіна,
з Jira потребує прав потребує прав без прав адміна
адміністратора адміністратора
Зберіган- В Власна база Власна база PostgreSQL (Supabase)
ня інфраструктурі EazyBI Screenful – під контролем
даних Atlassian (поза Atlassian) розробника
Шифру- Не документо- Не документо- Не AES-256-GCM на рівні
вання вано вано документо- застосунку перед
облікових вано збереженням у БД
даних
Склад- Низька Висока Дуже низька Низька
ність (вивчення (zero-config) (OAuth flow через
початко- MDX) Settings сторінку)
вого
налашту-
вання
Орієнто- ~$1.4–1.5 / user Від $500 / Від $49 / Безкоштовно
вна / місяць місяць місяць (open-source, self-
вартість hosted на Vercel Free)
Підтримка Переважно Обидва (через Обидва Обидва
Kanban та Kanban MDX) (Scrum – sprints;
Scrum Kanban – time
windows)
22
З таблиці 1.1 видно три незакриті проблеми.
По-перше, жоден з інструментів не вимірює процесну дисципліну як єдину
метрику. ActionableAgile не враховує estimation coverage та capacity mismatch;
EazyBI дозволяє реалізувати будь-яку метрику, але потребує MDX-розробки;
Screenful відображає Escaped Defects, але ізольовано від прогнозу. Жоден не
обчислює інтегрального Health Score із розбивкою на штрафні компоненти.
По-друге, жоден з інструментів не надає per-member capacity planning з
урахуванням productivity_coefficient та відпусток у вигляді структурованих даних у
власній базі. ActionableAgile та Screenful орієнтовані на командний рівень, а не на
окремих виконавців з різною доступністю.
По-третє, всі три рішення є marketplace-плагінами Jira або SaaS-продуктами,
що потребують встановлення в Jira-інстанс або окремої підписки. Самостійний веб-
застосунок, що підключається через стандартний OAuth 2.0 (3LO) і не потребує прав
адміністратора Jira для інсталяції – відсутній як клас рішень у цьому сегменті.
Висновки до розділу 1
У розділі розглянуто предметну область і сформовано підгрунтя для розробки
хмарного застосунку для автоматизованої оцінки ефективності Agile-команд та
стохастичного прогнозування навантаження на основі штрафної моделі Health Score
й методу Монте-Карло. Визначено ключову прогалину на ринку: жоден із
розглянутих інструментів – ActionableAgile, EazyBI, Screenful не поєднує оцінку
процесної дисципліни команди зі стохастичним прогнозуванням навантаження у
форматі самостійного веб-застосунку.
Обгрунтовано актуальність теми та сформульовано завдання кваліфікаційної
роботи бакалавра. Визначено дві взаємопов'язані незалежні проблеми:
методологічну та технологічну.
Проведено порівняльний аналіз систем аналітики та прогнозування для Jira:
ActionableAgile, EazyBI та Screenful, який показав: ActionableAgile застосовує метод
Монте-Карло, але не враховує процесну дисципліну; EazyBI гнучкий, але потребує
MDX-розробки і не має готових шаблонів для Health Score; Screenful простий, але
23
прогнозування в ньому лінійне і метрики якості на нього не впливають. Жоден не
надає per-member capacity planning з productivity_coefficient та day-offs.
Методологія Agile та її реалізації через Scrum і Kanban описані в обсязі,
достатньому для розуміння контексту задач, що вирішує розроблюваний застосунок.
Показано роль Jira, як основного інструменту реалізації Agile-практик, та Jira REST
API v3, як технологічної бази для збору аналітичних даних.
24
2 ПРОЄКТУВАННЯ ХМАРНОГО ЗАСТОСУНКУ ДЛЯ ОЦІНКИ
ЕФЕКТИВНОСТІ ТА ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-
КОМАНД
У цьому розділі обґрунтовано теоретичні основи методів оцінки та
прогнозування, обраних для реалізації застосунку та описано проєктні рішення, що
забезпечують їх практичне втілення. Зокрема, обґрунтовано вибір штрафної моделі
Health Score з шістьма компонентами оцінки та методу Монте-Карло з 5 000
симуляцій для формування довірчих інтервалів прогнозу. Описано вибір
технологічного стеку (Next.js 16, Supabase, Vercel, AES-256-GCM), спроєктовано
схему бази даних із семи таблиць, визначено функціональні та нефункціональні
вимоги, а також описано архітектуру застосунку за принципом Hexagonal (Ports &
Adapters) та структуру інтерфейсу користувача.
2.1 Теоретичні основи методу прогнозування, скоригованого на метрики
якості Quality-Weighted Forecasting
Основна проблема класичних прогнозних підходів для Agile-команд полягає в
тому, що вони враховують лише кількісні дані – скільки задач або story points команда
завершила за минулі спринти – і не беруть до уваги якість цієї роботи. Команда може
показувати стабільну Velocity, накопичуючи водночас технічний борг,
перевідкривати задачі та систематично не завершувати заплановане. Такий
прихований стан не відображається у числі story points, але безпосередньо впливає на
реальну пропускну здатність у майбутніх спринтах.
Метод Quality-Weighted Forecasting (QWF), покладений в основу архітектурних
рішень застосунку, поєднує два компоненти: кількісний вимір (Velocity, Throughput)
та якісний (Health Score). Ідея полягає в тому, що прогноз навантаження повинен
враховувати не лише середню швидкість команди, а й поточний стан дисципліни
роботи: чи команда оцінює задачі перед спринтом, чи вкладається в заплановану
ємність, чи завершує взяті зобовязання без перенесень і повторних відкриттів.
25
2.2 Опис методів для оцінки ефективності навантаження Agile-команд
Velocity – кількість завершених story points за один спринт. Середнє значення
обчислюється за формулою:
1
= � � × �(i = 1. . n) , (2.1)
де – Velocity i-го спринту;
n – розмір вибірки (зазвичай 5–6 останніх спринтів).
Velocity зручна як вхідні дані для прогнозування, але не враховує якість
виконаної роботи: команда може збільшити Velocity, скорочуючи тестування або
переносячи незавершені задачі у наступний спринт.
Throughput – кількість завершених задач за одиницю часу (спринт або тиждень)
незалежно від їх розміру. Не потребує оцінки у story points, що робить метрику
придатною для Kanban-команд та для побудови статистичних розподілів у методі
Монте-Карло.
Defect Density (DD) – концентрація дефектів відносно виконаного обсягу
роботи:
кількість задач
= _ . (2.2)
Обчислюється в use-case AggregateSprintMetrics: з усіх завершених задач
спринту виокремлюються задачі типу Bug та підраховується їх кількість відносно
сумарних story points. Зростання DD є раннім сигналом погіршення якості кодової
бази.
Health Score – інтегральний показник дисципліни роботи команди у спринті за
шкалою від 0 до 100. Модель базується на принципі штрафів: стартова оцінка 100
зменшується за кожне порушення процесу. Формула реалізована у чистій функції
computeHealthScoreMath (src/domain/services/health-score.service.ts).
1. Штраф за відсутність оцінки: −2% за кожну задачу, що не мала оцінки на
момент старту спринту (has_estimate_at_start = false):
26
= ∗ 2. (2.3)
2. Штраф за розрив capacity–committed (до −20%): якщо фактично взяте
навантаження суттєво перевищує або не довикористовує заплановану ємність
команди, фіксується штраф:
− |
ℎ = min�20, � (2.4)
∗ 100 ��.
3. Штраф за незавершені задачі: −3% за кожну задачу, яку не перевели до
статусу Done до кінця спринту (is_completed = false):
= ∗ 3. (2.5)
4. Штраф за розбиті задачі: −5% за кожну задачу, що була розбита на частини в
ході спринту (is_split = true):
= ∗ 5. (2.6)
5. Штраф за перевідкриття: −3% за кожну задачу, що перевідкривалась хоча б
один раз (reopen_count > 0):
= ∗ 3. (2.7)
6. Виконавчий штраф: −10%, якщо команда завершила менше половини взятих
задач (completionRate < 0.5):
= 10,якщо (−). (2.8)
<0.5
Підсумковий Health Score обчислюється як:
= max( 0, min (100, (100 −�). (2.9)
Інтерпретація значень: 85–100 – команда у нормальному стані (зелена зона); 70–
84 – є сигнали для уваги (жовта зона); 0–69 – критичний стан (червона зона). Ці
порогові значення використовуються в компоненті HealthScoreDashboard для вибору
27
кольору індикатора. Health Score зберігається у таблиці sprints у полі health_score (int)
разом із деталізацією breakdown у полі health_score_breakdown (JSONB).
2.3 Опис методів для прогнозування навантаження Agile-команд
Проста екстраполяція Velocity – лінійний прогноз:
= ,
(2.10)
де RW (Remaining Work) – обсяг залишеної роботи у story points;
V_avg – середня Velocity за останні n спринтів.
Метод легко зрозумілий стейкхолдерам, але дає точкову оцінку без меж
невизначеності та не враховує варіабельність між спринтами.
Метод Монте-Карло – стохастичний прогноз шляхом симуляції завершення
залишкового беклогу. Реалізований у чистій функції runMonteCarlo
(src/domain/services/forecast.service.ts). Алгоритм:
− визначається ліміт симуляцій: cap = min(simulations, 5 000). Обмеження в 5
000 ітерацій обрано з вимоги виконання на Vercel Serverless Functions за час менше 50
мс;
− у кожній ітерації: поки remaining > 0 і кількість спринтів < 50, випадково
обирається значення Velocity з масиву velocities (вибірка з поверненням); якщо обране
значення ≤ 0, підставляється 1 для захисту від нескінченного циклу;
− кількість спринтів, необхідних для завершення беклогу, записується в масив
sprintCounts;
− після cap ітерацій масив сортується, витягуються процентилі: medianSprints
(P50), p10Sprints (P10), p90Sprints (P90);
− підраховується probability – частка симуляцій, що завершились не пізніше
targetSprints.
Функція повертає об'єкт { probability, medianSprints, p10Sprints, p90Sprints,
simulations }. P90 є консервативним орієнтиром для комунікації зі стейкхолдерами: у
90% змодельованих сценаріїв команда вкладається в цей термін. P10 відповідає
28
оптимістичному сценарію.
Quality-Weighted Forecasting (QWF) – теоретична розширена модель, що
покладена в основу аналітичного підходу застосунку. Суть чого: замість
використання сирої Velocity у Монте-Карло симуляції враховується якісний стан
команди. Команда з низьким Health Score систематично не завершує заплановане,
перевідкриває задачі та накопичує неявні витрати часу – тобто, її ефективна
пропускна здатність у наступних спринтах буде нижчою за номінальну Velocity.
Коефіцієнт Health Score може бути використаний для коригування вхідної вибірки
Velocity перед симуляцією, що дасть реалістичніший прогноз. Практична реалізація
QWF у вигляді окремого endpoint заплановано як наступний крок розробки.
2.4 Обґрунтування вибору сучасних технологій для створення хмарного
застосунку для оцінки ефективності та прогнозування навантаження Agile-
команд
Next.js 16.2.6 обрано як основний веб-фреймворк з трьох причин [17]. По-
перше, App Router архітектура надає Server Components і Server Actions, що дозволяє
виконувати запити до Supabase безпосередньо на сервері – без зайвого round-trip через
клієнт і без витоку service role ключів у браузер. По-друге, вбудовані API Routes
забезпечують реалізацію всіх серверних endpoint у тому ж репозиторії (OAuth
callback, webhook receiver, internal cron). По-третє, Next.js оптимізовано для деплою
на Vercel Serverless, що надає автоматичне масштабування без конфігурації серверів.
Supabase [16] обрано як базу даних з двох причин. Перша – управляємий
PostgreSQL з підтримкою складних типів: JSONB для зберігання
health_score_breakdown, UUID для primary keys, повноцінні foreign keys між
таблицями. Це набагато гнучкіше від key-value сховищ. Друга – пакет @supabase/ssr
надає клієнт, адаптований для Next.js App Router: окремі режими для Server
Components (service role, обходить RLS) та Client Components (anon key, через RLS).
Singleton getSupabaseAdmin() централізує підключення і уникає множинних зєднань
між serverless invocations.
Jira REST API v3 + OAuth 2.0 (3LO) – єдиний офіційний механізм доступу до
29
даних Jira Cloud для зовнішніх застосунків без Atlassian Forge. Використовуються
scopes: read:jira-work, read:jira-user, read:board-scope:jira-software, read:sprint:jira-
software, read:issue:jira-software, read:project:jira-software та offline_access для
отримання refresh token. Усі OAuth токени шифруються на рівні застосунку
алгоритмом AES-256-GCM перед збереженням у Supabase: навіть при компрометації
бази даних токени залишаються нечитабельними без ключа
JIRA_ENCRYPTION_KEY.
AES-256-GCM (Advanced Encryption Standard, режим Galois/Counter Mode)
реалізовано в TokenEncryption.ts (src/infrastructure/crypto/). Алгоритм: 96-бітний IV
генерується випадково для кожного шифрування (randomBytes(12)), формат
збереження – base64(IV || authTag || ciphertext), де authTag (128 біт) забезпечує
автентифікацію зашифрованих даних. GCM обрано замість CBC через вбудовану
перевірку цілісності (authenticated encryption): будь-яке несанкціоноване змінення
зашифрованих даних призводить до помилки декрипції.
Vercel обрано як платформу деплою через нативну інтеграцію з Next.js,
автоматичне масштабування serverless functions та підтримку cron jobs для планового
запуску /api/internal/aggregate. Застосунок задеплоєно за адресою: https://agile-load-
forecaster.vercel.app.
TypeScript 5 використовується у всіх шарах застосунку. Типи interface Issue,
Sprint, TeamMember, HealthIssueData, CapacityMetrics описують доменні обєкти; типи
портів (ISprintRepository, IJiraClient тощо) гарантують відповідність між
залежностями; TypeScript у функціях computeHealthScoreMath та runMonteCarlo
унеможливлює передачу некоректних аргументів.
Vitest обрано для unit-тестування замість Jest з двох причин: швидший cold start
завдяки ESM-нативному підходу (не потребує transpiling через Babel) та повна
сумісність з TypeScript без додаткових плагінів. Vitest використовує ідентичний API
(describe, it, expect), що полегшує міграцію наявних тестів.
Tailwind CSS 4 + Lucide React забезпечують стильовий шар інтерфейсу. Власна
дизайн-система визначена через CSS custom properties (--bg-base, --bg-elev, --bg-subtle,
--hairline, --ink, --ink-muted, --mint-deep, --sand-deep, --coral-deep) в globals.css; Lucide
30
надає уніфіковані SVG-іконки.
Таблиця 2.1 – Порівняльна характеристика обраних технологій за критеріями
застосовності до вимог проєкту
Технологія Призначення
в проєкті Ключові можливості Альтерна- Обґрунтування
тиви вибору
Next.js Веб- App Router з Remix, Нативна
16.2.6 фреймворк: Server/Client SvelteKit, інтеграція з
(React 19) маршрути- Components; SSR без Express + Vercel; Server
зація, Server окремого сервера; React SPA Actions усувають
Components, Server Actions для потребу в
Server прямих запитів до окремому API-
Actions, API Supabase шарі; найбільша
Routes екосистема серед
React-
фреймворків
Supabase Основна база Управляємий Firebase Реляційна схема з
(PostgreSQ даних: PostgreSQL з JSONB Firestore, JSONB необхідна
L 15) зберігання (health_score_breakdow PlanetScale, для
команд, n), UUID, foreign keys; Neon, health_score_brea
спринтів, @supabase/ssr для App MongoDB kdown;
задач, Router; service role + Atlas @supabase/ssr
day_offs, RLS офіційно
метрик підтримує Next.js
App Router;
безкоштовний
тариф для
розробки
Jira REST Джерело Endpoint Atlassian OAuth 2.0 (3LO)
API v3 даних: /rest/api/3/search?expan Forge не потребує
+ OAuth синхроніза- d=changelog для (вбудована встановлення
2.0 (3LO) ція спринтів, отримання історій auth), Jira плагіна в Jira-
задач та переходів; Connect інстанс;
changelog з offline_access scope для (server- стандартний
Jira Cloud refresh token; авто- based) протокол для
refresh за 60 с до standalone веб-
закінчення застосунків;
підтримує token
refresh без участі
користувача
AES-256- Шифрування 96-бітний IV AES-256- GCM забезпечує і
GCM OAuth- генерується для CBC, bcrypt конфіденцій-
(Node.js токенів перед кожного шифрування; (для ність, і цілісність
crypto) збереженням 128-бітний authTag паролів), в одному
у Supabase забезпечує libsodium проході;
автентифікацію вбудований у
(authenticated Node.js crypto без
encryption); формат: зовнішніх
31
Технологія Призначення Ключові можливості Альтерна- Обґрунтування
в проєкті тиви вибору
base64(IV||authTag||cip залежностей;
hertext) RFC 5116
рекомендує GCM
для токенів
Vercel Платформа Zero-config деплой для AWS Офіційна
(Serverless) деплою: Next.js; автоматичне Lambda + платформа для
serverless масштабування; CloudFront, Next.js від тієї ж
functions, Vercel Cron для Railway, команди; cron
cron jobs, /api/internal/aggregate; Render, jobs без
CDN Edge Network для Fly.io сторонніх
статичних ресурсів сервісів;
безкоштовний
тариф покриває
потреби проєкту
TypeScript Мова Строга типізація JavaScript Типи портів
5 розробки всіх інтерфейсів портів (без типів), гарантують
шарів: (ISprintRepository, Flow відповідність між
Domain, IJiraClient); типи шарами
Application, доменних об'єктів архітектури;
Infrastructure, (Sprint, Issue, TypeScript 5
UI HealthIssueData); підтримується
виявлення помилок на Next.js та Vitest
етапі компіляції без додаткової
конфігурації
Vitest 4 Фреймворк ESM-нативний підхід Jest, Mocha У 5–10 разів
unit- без Babel; cold start < + Chai швидший cold
тестування 200 мс; API сумісний з start порівняно з
доменних та Jest (describe/it/expect); Jest для
інфраструкту vi.fn() для мок-функцій TypeScript-
рних функцій у тестах retry проєктів; не
потребує
babel.config.js або
ts-jest; офіційно
рекомендований
для Vite/Next.js
проєктів
Tailwind Стильовий CSS custom properties styled- Tailwind не додає
CSS 4 шар UI: CSS для кольорової components JavaScript bundle;
+ Lucide custom системи (--mint-deep, -- , MUI, Ant CSS variables
React properties, sand-deep, --coral- Design, дозволяють
дизайн- deep); utility-first Chakra UI динамічно
система, класи; Lucide надає змінювати тему;
іконки уніфіковані SVG- Lucide – легка
іконки (Gauge, Split, (tree-shakeable)
RotateCcw) альтернатива
FontAwesome
32
2.5 Проєктування структури бази даних
База даних реалізована на PostgreSQL через Supabase. Схема складається з семи
таблиць, між якими встановлені foreign key залежності та представлена на рис. 2.1.
Рисунок 2.1 – Схема структури бази даних
Усі первинні ключі – UUID, генеруються на рівні PostgreSQL
(gen_random_uuid()). Таблиця teams є кореневою сутністю: всі інші таблиці повязані
з нею безпосередньо або через sprints.
Таблиця teams – зберігає зареєстровані команди та їхні Jira-підключення. Поле
jira_tokens_encrypted містить AES-256-GCM зашифрований JSON зі структурою
{access_token, refresh_token, expires_at, cloud_id, scope}. Поле jira_board_id –
ідентифікатор Jira-дошки для синхронізації спринтів. jira_cloud_id – унікальний
ідентифікатор Atlassian Cloud-інстансу.
team_members – члени команди з привязкою до Jira. Поле jira_account_id
використовується для зіставлення assignee задач з конкретним членом команди при
розрахунку capacity. Поле productivity_coefficient (float, за замовчуванням 1.0)
дозволяє враховувати часткову зайнятість: значення 0.8 означає, що член команди
33
доступний лише на 80% від стандартного робочого дня.
sprints – відображає спринти Jira з їхніми метаданими та результатами аналізу.
jira_sprint_id + team_id утворюють унікальне обмеження (onConflict при upsert). state
приймає значення future/active/closed. health_score (int) та health_score_breakdown
(JSONB) записуються use-case CalculateSprintHealth після агрегації метрик.
issues – задачі Jira, що синхронізуються через API та webhook. Поле
has_estimate_at_start (bool) фіксується у snapshot на початку спринту і залишається
незмінним протягом спринту – це дозволяє коректно обчислити estimationPenalty
навіть якщо оцінку додали пізніше. is_split (bool) виставляється у true, якщо задача
була перенесена частково або розбита в ході спринту. reopen_count (int)
інкрементується при кожному поверненні задачі у роботу після Done. deleted_at
реалізує мяке видалення: при отриманні jira:issue_deleted через webhook поле
виставляється у поточну дату замість виконання DELETE
day_offs – записи про відпустки та лікарняні членів команди. Кожен запис:
team_member_id, start_date, end_date. При розрахунку capacity виконується запит з
фільтрами lte(start_date, sprint.end_date) та gte(end_date, sprint.start_date) для
отримання тільки відпусток, що перетинаються зі спринтом.
sprint_metrics – агрегаційна таблиця для зберігання денних знімків метрик
активного спринту: capacity_sec, committed_at_start_sec, velocity_sp
(накопичувально), defect_density. Заповнюється use-case AggregateSprintMetrics через
/api/internal/aggregate. metric_date + sprint_id утворюють унікальний ключ.
sprint_snapshots – фіксує стан capacity та committed_at_start_sec у двох
критичних точках: type = start (перший робочий день спринту, година >= 06:00 UTC)
та type = end (останній робочий день, година >= 21:00 UTC). Знімки забезпечують
незмінність базових значень для розрахунку Health Score навіть якщо склад спринту
змінювався у процесі.
Зв’язки між таблицями: teams -> team_members (1:N), teams -> sprints (1:N),
sprints -> issues (1:N), team_members -> day_offs (1:N), sprints -> sprint_metrics (1:N),
sprints -> sprint_snapshots (1:2). Зовнішні ключі з ON DELETE CASCADE гарантують
каскадне видалення при видаленні команди або спринту.
34
2.6 Опис процесу проєктування хмарного застосунку для оцінки
ефективності та прогнозування навантаження Agile-команд
2.6.1 Опис предметної області. Застосунок «Agile Forecaster» призначений для
Agile-команд, що використовують Jira Software для управління задачами та
спринтами. Аналіз предметної області грунтується на трьох категоріях акторів.
Team Lead / Project Manager – основний користувач застосунку. Відповідає за
планування спринтів, контролює навантаження команди, приймає рішення про
розподіл роботи. Основні сценарії: перегляд Health Score поточного спринту та
порівняння з попередніми; аналіз capacity і виявлення перевантажених членів
команди; отримання Monte Carlo прогнозу для комунікації термінів зі
стейкхолдерами.
Scrum Master – відповідає за процес. Аналізує breakdown Health Score для
виявлення системних проблем: висока estimationPenalty вказує на слабку культуру
оцінювання, висока reopenPenalty – на проблеми з Definition of Done, висока
capacityMismatchPenalty – на надмірний або недостатній commitment при плануванні.
Система (фонові процеси) – виконує автоматичний збір та агрегацію даних без
взаємодії з користувачем: вебхуки від Jira оновлюють задачі у реальному часі,
плановий cron-запит щодня агрегує метрики активних спринтів.
Для застосунку визначено шість основних варіантів використання:
− налаштування команди. Team Lead створює команду, додає членів із
зазначенням productivity_coefficient, привязує Jira account IDs;
− підключення до Jira – Team Lead ініціює OAuth 2.0 авторизацію (redirect до
Atlassian), після успішного callback токени шифруються та зберігаються у Supabase;
− перегляд Health Score – актор відкриває Overview або Health page, обирає
спринт, отримує числову оцінку та розбивку breakdown за штрафами;
− аналіз capacity – актор відкриває Capacity page, обирає спринт, бачить
планову ємність і committed навантаження по кожному члену команди у годинах;
− прогнозування беклогу – актор переходить на Forecast page, вводить
залишковий обсяг роботи, отримує P10/P50/P90 та probability estimate;
35
− автоматична агрегація метрик – cron-запит до /api/internal/aggregate ініціює
AggregateSprintMetrics для всіх активних спринтів; webhook від Jira оновлює таблицю
issues у реальному часі.
2.6.2 Формування вимог до хмарного застосунку. На основі аналізу
предметної області та сценаріїв використання сформульовано функціональні та
нефункціональні вимоги.
Функціональні вимоги:
− ФВ-01. Застосунок збирає дані спринтів та задач з Jira REST API v3 через
OAuth 2.0 (3LO), зберігає їх у таблицях sprints та issues в Supabase;
− ФВ-02. Застосунок обчислює Health Score за штрафною моделлю, що
включає estimationPenalty, capacityMismatchPenalty, completionPenalty, splitPenalty,
reopenPenalty, executionPenalty, та зберігає результат у полях health_score та
health_score_breakdown таблиці sprint;
− ФВ-03. Застосунок надає стохастичний прогноз методом Монте-Карло (5 000
симуляцій) з результатами P10, P50, P90 та probability для заданого targetSprints;
− ФВ-04. Застосунок розраховує ємність команди (capacity) з урахуванням
productivity_coefficient кожного члена та його day_offs у рамках спринту; формула:
(workingDays − daysOff) × coefficient × 8 год × 3600 с;
− ФВ-05. Застосунок приймає webhook-події від Jira (jira:issue_created,
jira:issue_updated, jira:issue_deleted) з верифікацією HMAC-SHA256 підпису в
заголовку x-hub-signature;
− ФВ-06. Застосунок виконує планову агрегацію метрик через захищений
endpoint /api/internal/aggregate (Bearer CRON_SECRET): для кожного активного
спринту фіксуються snapshot-и на початку та кінці спринту, обчислюється Health
Score та capacity.
Нефункціональні вимоги:
− НФВ-01. Час відповіді при завантаженні Overview Dashboard – не більше 2
секунд для типового спринту з 50 задачами;
36
− НФВ-02. Час виконання Монте-Карло симуляції (5 000 ітерацій) – не більше
50 мс в середовищі Vercel Serverless. Обмеження cap = 5 000 гарантує дотримання цієї
вимоги;
− НФВ-03. OAuth 2.0 токени зберігаються виключно у зашифрованому вигляді
(AES-256-GCM, 96-бітний IV) у полі jira_tokens_encrypted таблиці teams.
Автоматичне поновлення токена виконується за 60 с до закінчення терміну дії;
− НФВ-04. Усі запити до Jira REST API виконуються через withRetry з
exponential backoff (800 мс -> 1 600 мс -> 3 200 мс, максимум 3 повтори) при transient-
помилках (HTTP 429, 502, 503, 504, мережеві таймаути);
− НФВ-05. Застосунок підтримує мяке видалення задач (soft delete): при
отриманні jira:issue_deleted проставляється deleted_at без фізичного видалення рядка,
що зберігає аналітичні дані та дозволяє їх відновлення.
2.6.3 Проєктування інтерфейсу користувача. Інтерфейс застосунку
побудований на Next.js App Router та складається з пяти основних сторінок,
доступних через бічну навігаційну панель (sidebar). Дизайн-система базується на CSS
custom properties та темній кольоровій схемі.
Overview (головна сторінка, /). Завантажує дані через React Server Actions з
Promise.all для паралельного запиту Health, тренду та summary. Складається з трьох
компонентів:
− sprintSummaryBlocks – верхній рядок зведених карток спринту (кількість
задач, завершені, incomplete, split);
− summaryCards – KPI-карти: Health Score із дельтою до попереднього спринту,
Velocity, кількість reopens;
− healthScoreDashboard – кругова шкала (SVG ring, 240×240 px) з числом Health
Score у центрі; колір ring змінюється залежно від зони (mint >= 85, sand 70–84, coral <
70); під ring – список breakdown-штрафів з іконками Lucide (Gauge, Ruler,
CheckCircle2, Split, RotateCcw, AlertCircle).
Health (/health). Клієнтський компонент, що завантажує SprintHealth через GET
/api/health?sprintId={id}. Відображає той самий HealthScoreDashboard. Призначена
для детального перегляду метрик якості конкретного спринту з можливістю
37
порівняння тренду.
Forecast (/forecast). Наразі містить заглушку. Планується: форма введення
Remaining Story Points, кнопка Calculate, картки з результатами P10/P50/P90 та
probability. Логіка прогнозування (runMonteCarlo) вже реалізована у domain layer.
Capacity (/capacity). Server Component, що безпосередньо звертається до
Supabase. SprintSelector дозволяє обрати спринт через URL-параметр ?sprintId.
Відображає: traffic-light індикатор (зелений/жовтий/червоний кружок з SVG-glow
ефектом) із підсумком committed/capacity у годинах; решітку карток per-member
breakdown із progress bar та відсотком завантаження; попередження (warnings) при
відсутніх датах спринту або порожньому складі команди.
Team + Settings (/team, /settings). TeamManagementClient дозволяє керувати
складом команди (додавати/редагувати членів, встановлювати
productivity_coefficient). Settings реалізує OAuth-flow: кнопка «Підключити Jira»
ініціює redirect на /api/auth/jira/login, після успішного callback відображає статус через
URL-параметри (jira_connected=1 або jira_error={code}). Також доступна кнопка
ручної синхронізації даних з Jira.
Дизайн-система. Кольорова палітра: --bg-base (темний фон), --bg-elev
(підвищений елемент), --bg-subtle (тонкий фон), --hairline (межа), --ink / --ink-muted /
--ink-faint (текст за ступенями видимості). Семантичні кольори: --mint-deep (зелена
зона Health Score), --sand-deep (жовта зона), --coral-deep (червона зона). Sidebar
включає секцію «Quality Insights» з автоматично згенерованими insights-
повідомленнями.
2.6.4 Проєктування та розробка хмарного застосунку для оцінки
ефективності та прогнозування навантаження Agile-команд. Застосунок
побудований за принципом гексагональної архітектури (Hexagonal Architecture, Ports
& Adapters), що забезпечує незалежність бізнес-логіки від деталей інфраструктури та
UI. Архітектура складається з чотирьох концентричних шарів і візуалізована на
рисунку 2.2.
38
Рисунок 2.2 – Архітектура додатку
Domain Layer (src/domain/) – ядро бізнес-логіки без будь-яких зовнішніх
залежностей. Містить:
− entities/ – типи Sprint, Issue, TeamMember, DayOff описують базові доменні
обєкти.
− value-objects/ – SprintHealth, CapacityMetrics, HealthScoreBreakdown –
незмінні обєкти, що несуть результати обчислень;
− services/ – чисті функції computeHealthScoreMath, runMonteCarlo,
computeCapacityMath, countOverlapWorkingDays. Не виконують I/O і не мають
зовнішніх залежностей – їх можна викликати у тестах без жодних моків.
Application Layer (src/application/) – оркестрація доменних сервісів через use-
cases та визначення портів. Ports – TypeScript-інтерфейси (ISprintRepository,
IIssueRepository, ITeamRepository, IMetricsRepository, IJiraClient), що описують
абстрактний контракт для зовнішніх залежностей. Use-cases:
39
− aggregateSprintMetrics – щоденно агрегує метрики всіх активних спринтів:
sprint snapshot -> capacity -> health -> upsert sprint_metrics;
− calculateSprintHealth – завантажує задачі та метрики спринту, викликає
computeHealthScoreMath, зберігає health_score у БД;
− сalculateTeamCapacity – завантажує членів команди, day_offs, задачі спринту,
викликає computeCapacityMath;
− processWebhookEvent – верифікація webhook, upsert або soft-delete задачі у
IssueRepository;
− syncJiraData – синхронізація спринтів та задач з Jira API через IJiraClient
порт.
Infrastructure Layer (src/infrastructure/) – конкретні реалізації портів:
− db/ – репозиторії SprintRepository, IssueRepository, TeamRepository,
MetricsRepository реалізують відповідні інтерфейси через Supabase Admin Client.
Функції toSprint(), toIssue(), toMember() виконують mapping між рядками БД та
доменними обєктами;
− jira/ – JiraApiClient реалізує IJiraClient, обгортає функції з lib/jira/api.ts у
withRetry(); JiraNormalizer – чиста функція normalizeJiraIssue() перетворює сирий
JiraIssue на Partial<Issue>;
− crypto/ – TokenEncryption.ts: encryptTokens() та decryptTokens() з AES-256-
GCM;
− resilience/ – withRetry<T>() з exponential backoff (800 мс -> 1 600 мс -> 3 200
мс), ізолює транзієнтні помилки (429, 502–504, мережеві).
UI Layer (src/app/, src/components/) – Next.js App Router pages та React 19
компоненти. Server Components мають прямий доступ до Supabase через service role.
Client Components (позначені 'use client') отримують дані через React Server Actions
(src/app/actions/) або fetch до API routes. AppContext (src/components/AppContext.tsx)
зберігає глобальний стан: teamId, sprintId, список sprints, teams – передається через
React Context усім client компонентам.
Структура проєкту відображена на рисунку 2.3.
40
Рисунок 2.3 – Структура проєкту
Ключові потоки даних.
OAuth 2.0 потік.
1) Користувач натискає «Підключити Jira» – відбувається redirect на
/api/auth/jira/login, де формується URL Atlassian auth з state=teamId для CSRF-захисту.
2) Після авторизації Atlassian робить redirect на /api/auth/jira/callback з code та
state.
3) exchangeCodeForTokens() обмінює code на tokens, запитує accessible-resources
для cloud_id, шифрує токени AES-256-GCM та зберігає в таблиці teams.
4) При кожному запиті до Jira API getValidToken() перевіряє expires_at: якщо до
закінчення < 60 с – автоматично виконує token refresh.
Webhook потік.
1) Jira надсилає POST /api/webhook/jira при створенні, оновленні або видаленні
задачі.
2) verifySignature() перевіряє HMAC-SHA256 підпис через timingSafeEqual для
захисту від timing-атак.
3) ProcessWebhookEvent визначає тип події та викликає IssueRepository.upsert()
або .softDelete().
4) При jira:issue_updated оновлюється reopen_count: якщо подія означає
41
повернення задачі у роботу після Done – лічильник збільшується.
Cron-агрегація.
1) Зовнішній планувальник (Vercel Cron або GitHub Actions) щодня надсилає
POST /api/internal/aggregate з заголовком Authorization: Bearer {CRON_SECRET}.
2) AggregateSprintMetrics.execute() отримує всі active спринти.
3) Для кожного спринту: checkAndCaptureSnapshot() фіксує start-snapshot
(перший день, год >= 06:00 UTC) або end-snapshot (останній день, год >= 21:00 UTC)
– кожен записується лише одноразово.
4) Обчислюється Health Score, capacity та defect_density, результати upsert-
уються у sprint_metrics.
Висновки до розділу 2
У розділі сформовано теоретичну та проєктну основу хмарного застосунку
«Agile Forecaster».
Описано чотири метрики оцінки ефективності: Velocity, Throughput, Defect
Density та Health Score. Health Score реалізовано як штрафну модель, що починається
від 100 та зменшується за шістьма типами порушень процесу: відсутність оцінок (-2%
за задачу), розрив capacity–committed (до -20%), незавершені задачі (-3% за задачу),
split задачі (-5% за задачу), перевідкриті задачі (-3% за задачу), низький completion
rate (додатково -10%).
Розглянуто три підходи до прогнозування: проста екстраполяція Velocity,
Монте-Карло (5 000 симуляцій, P10/P50/P90, час виконання < 50 мс) та концептуальна
модель Quality-Weighted Forecasting як наступного кроку розвитку.
Обґрунтовано вибір технологічного стеку: Next.js 16.2.6 (App Router, Server
Actions, API Routes), Supabase (PostgreSQL з JSONB для health_score_breakdown), Jira
OAuth 2.0 (3LO) з AES-256-GCM шифруванням токенів (96-бітний IV, 128-бітний
authTag), Vercel для serverless деплою, TypeScript 5 та Vitest для тестування.
Спроєктовано схему бази даних із семи таблиць: teams, team_members, sprints,
issues, day_offs, sprint_metrics, sprint_snapshots. Ключові особливості: м’яке видалення
задач (deleted_at), has_estimate_at_start та is_split як незмінні прапорці для коректного
42
обчислення Health Score, sprint snapshots для фіксації стану на початку та кінці
спринту, productivity_coefficient для реалістичного capacity planning.
Архітектура застосунку побудована за принципом Hexagonal (Ports & Adapters)
із чотирьох шарів: Domain (чисті функції без I/O), Application (use-cases та порти-
інтерфейси), Infrastructure (Supabase репозиторії, JiraApiClient з retry,
TokenEncryption), UI (Next.js App Router + React 19 + AppContext). Описано три
основні потоки даних: OAuth 2.0 авторизація з автоматичним refresh, Jira webhook із
HMAC-SHA256 верифікацією та щоденна cron-агрегація метрик зі sprint snapshots.
43
3 ПРОГРАМНА РЕАЛІЗАЦІЯ
Розділ описує практичну реалізацію застосунку «Agile Forecaster»: від
конкретних задач програмування до результатів тестування. Кожен модуль
розроблено з дотриманням принципів гексагональної архітектури, де бізнес-логіка
повністю відокремлена від інфраструктурних деталей, а алгоритми оцінки та
прогнозування реалізовані у вигляді чистих функцій без зовнішніх залежностей.
3.1 Вибір та обґрунтування методів вирішення проблеми
Програмна реалізація хмарного застосунку охоплює такі конкретні задачі:
1. Реалізувати OAuth 2.0 (3LO) авторизацію з Jira: маршрут /api/auth/jira/login
для побудови authorization URL, маршрут /api/auth/jira/callback для обміну code на
tokens та збереження зашифрованих токенів у Supabase.
2. Реалізувати шифрування OAuth-токенів алгоритмом AES-256-GCM з 96-
бітним IV та 128-бітним authentication tag перед записом у поле jira_tokens_encrypted
таблиці teams.
3. Реалізувати синхронізацію спринтів та задач з Jira REST API v3 через use-case
SyncJiraData: отримання спринтів по boardId, нормалізація задач через JiraNormalizer,
upsert у Supabase.
4. Реалізувати алгоритм computeHealthScoreMath з шістьма типами штрафів: за
відсутність оцінки (-2% за задачу), розрив capacity-committed (до -20%), незавершені
задачі (-3%), split-задачі (-5%), перевідкриття (-3%), низький completion rate (-10%).
5. Реалізувати алгоритм runMonteCarlo (5 000 симуляцій, P10/P50/P90,
probability) для стохастичного прогнозування завершення беклогу.
6. Реалізувати алгоритм computeCapacityMath з підтримкою
productivity_coefficient на члена команди та day_offs з автоматичним виключенням
вихідних днів.
7. Реалізувати прийом Jira webhook (POST /api/webhook/jira) з HMAC-SHA256
верифікацією підпису та обробкою подій created/updated/deleted.
8. Реалізувати планову агрегацію метрик через /api/internal/aggregate: sprint
44
snapshots на початку та кінці спринту, щоденне оновлення Health Score, capacity та
defect_density.
9. Реалізувати механізм withRetry з exponential backoff (800 мс -> 1 600 мс -> 3
200 мс) для захисту від транзієнтних помилок Jira API.
10. Розробити UI з п'ятьма сторінками (Overview, Health, Forecast, Capacity,
Team/Settings) та провести тестування у Vitest (unit-тести доменних функцій) і ручне
тестування на реальному Jira-проєкті.
3.2 Інтерфейс хмарного застосунку для оцінки ефективності та
прогнозування навантаження Agile-команд
Хмарний застосунок «Agile Forecaster» доступний в браузері за адресою:
https://agile-load-forecaster.vercel.app. Після входу відображається shell-компонент
(src/components/shell.tsx) із бічною навігаційною панеллю (Sidebar) та основним
контентом. Sidebar містить п'ять пунктів: Overview, Capacity, Forecast, Team, Settings,
а також секцію «Quality Insights» з автоматично згенерованими аналітичними
повідомленнями.
Сторінка Overview (/). Головна сторінка відображає стан поточного спринту у
трьох блоках. Компонент SprintSummaryBlocks – горизонтальний рядок зведених
карток у верхній частині сторінки, який показує: загальну кількість задач спринту,
кількість завершених, кількість незавершених (incomplete) та кількість split-задач.
Компонент SummaryCards відображає KPI у вигляді карток: Health Score з дельтою
відносно попереднього спринту (наприклад, «+5» зеленим або «-3» червоним),
Velocity у story points, кількість reopens. Компонент HealthScoreDashboard займає
нижню частину сторінки і містить SVG-кільце (240×240 px) з числовим значенням
Health Score у центрі та список breakdown-штрафів із іконками: Gauge (capacity
mismatch), Ruler (estimation), CheckCircle2 (completion), Split (split), RotateCcw
(reopens), AlertCircle (execution). Кольорова схема кільця: mint (HS >= 85), sand (70-
84), coral (< 70).
Сторінка Health (/health). Клієнтський компонент, що завантажує SprintHealth
через GET /api/health?sprintId={id} при зміні обраного спринту. Він відодображає той
45
самий компонент HealthScoreDashboard. Сторінка Health призначена для ізольованого
перегляду метрик якості конкретного спринту з можливістю ручного вибору будь-
якого завершеного спринту зі списку. При завантаженні відображається заглушка
«Loading health score...», при помилці – текст помилки червоним кольором.
Сторінка Forecast (/forecast). Наразі містить заглушку з повідомленням «This
page will contain the Monte Carlo Forecast components». Функція прогнозування
runMonteCarlo вже реалізована в domain layer і повністю протестована; візуальний
компонент запланований як наступний крок розробки. Очікуваний вміст: форма
введення Remaining Story Points, кнопка Calculate, три картки з результатами
P10/P50/P90, probability-bar та порівняння з простою екстраполяцією.
Сторінка Capacity (/capacity). Server Component виконує запити до Supabase
безпосередньо на сервері. Компонент SprintSelector дозволяє обирати спринт через
URL-параметр ?sprintId; вибір синхронізується через shallow routing без
перезавантаження сторінки. Відображає traffic-light індикатор у вигляді кольорового
кружка (зелений – committed <= capacity, жовтий – незначне перевищення, червоний
– перевищення > 5%) з SVG-glow ефектом і текстом committed/capacity у годинах.
Нижче – решітка карток per-member breakdown: ім'я члена команди, навантаження у
форматі «Xh / Yh», коефіцієнт productivity_coefficient, progress bar із кольоровим
заповненням відповідно до рівня завантаження, відсоткова цифра у правому куті
картки. Попередження (warnings) відображаються жовтим блоком при відсутніх датах
спринту або порожньому складі команди.
Сторінка Team (/team). Клієнтський компонент TeamManagementClient
дозволяє керувати складом команди: додавати нових членів із зазначенням
jira_account_id та productivity_coefficient, редагувати наявні записи, переглядати
підключений статус Jira. Форма валідує productivity_coefficient у діапазоні 0.1–1.0.
Сторінка Settings (/settings). Реалізує OAuth 2.0 авторизацію. Кнопка
«Підключити Jira» ініціює redirect на /api/auth/jira/login. Після успішного OAuth
callback сторінка перезавантажується з URL-параметром jira_connected=1, і
компонент відображає повідомлення «Jira підключено успішно!». При помилці
авторизації параметр jira_error містить код: missing_params, token_exchange_failed або
46
access_denied з відповідним повідомленням українською мовою. Кнопка
«Синхронізувати дані» запускає Server Action syncSprintDataAction для ручного
оновлення спринтів та задач.
3.3 Програмна реалізація хмарного застосунку для оцінки ефективності
та прогнозування навантаження Agile-команд
Опис реалізації охоплює ключові алгоритми та технічні рішення по кожному
шару гексагональної архітектури.
Маршрутизація та структура App Router. Next.js 16 App Router визначає
маршрути через файлову структуру src/app/. Кожна сторінка може бути Server
Component (за замовчуванням) або Client Component ('use client'). Server Components
мають прямий доступ до Supabase через service role key, недоступний у браузері.
Client Components отримують дані через React Server Actions (src/app/actions/) або
fetch до API routes. AppContext (src/components/AppContext.tsx) зберігає глобальний
стан: teamId, sprintId, список teams та sprints, і через React Context забезпечує доступ
до цих даних у будь-якому client компоненті без prop drilling.
Server Actions та паттерн взаємодії з Supabase. Server Actions декларуються
директивою 'use server' і виконуються на сервері при виклику з клієнтського коду.
Приклад: getTeams() отримує список команд з Supabase та повертає типізований
масив. createTeamAction(name) вставляє новий рядок у таблицю teams та повертає
об'єкт з id та name. Помилки Supabase перехоплюються та перетворюються на
exception, що сповіщає клієнтський компонент. Singleton getSupabaseAdmin()
(src/infrastructure/db/supabase-client.ts) повертає один клієнт на весь serverless
invocation, уникаючи множинних підключень.
Алгоритм computeHealthScoreMath. Реалізовано як чисту функцію у
src/domain/services/health-score.service.ts. Приймає масив HealthIssueData та
HealthSprintContext, повертає SprintHealth { score, breakdown }. При totalIssues = 0
повертає score = 0 та нульовий breakdown без виконання обчислень. Кожен штраф
обчислюється незалежно та додається до загального значення. Мismatch penalty
обчислюється лише при capacitySec > 0 для уникнення ділення на нуль. Фінальний
47
результат обмежується діапазоном [0, 100] через Math.max та Math.min.
Алгоритм runMonteCarlo. Реалізовано в src/domain/services/forecast.service.ts.
Параметри: velocities (масив historical velocity), remainingPoints, simulations (за
замовчуванням 5 000), targetSprints (3). Захисні перевірки: при порожньому velocities
або remainingPoints <= 0 повертається нульовий результат без симуляцій. Cap: cap =
min(simulations, 5 000) – жорстке обмеження для гарантії часу < 50 мс на Vercel. У
кожній ітерації цикл while(remaining > 0 && sprints < 50) обирає випадкову Velocity з
масиву; якщо обраний v <= 0, підставляється 1 для захисту від нескінченного циклу.
Відсортований масив sprintCounts дає P10, P50, P90.
Алгоритм computeCapacityMath. Реалізовано в src/domain/services/
capacity.service.ts. Спочатку підраховуються робочі дні (пн-пт) між start_date та
end_date спринту. Для кожного члена команди викликається
countOverlapWorkingDays(), що повертає кількість робочих днів відпустки, що
перетинаються зі спринтом (виключаючи вихідні). Ємність члена команди.
Committed seconds per member підраховуються шляхом агрегації
original_estimate_sec задач спринту, де assignee_jira_id збігається з jira_account_id
члена. Задачі без assignee накопичуються у unassignedCommittedSec. При відсутніх
датах спринту або порожньому складі команди функція повертає emptyResult із
попередженнями у масиві warnings.
withRetry та resilience pattern. Функція withRetry<T>
(src/infrastructure/resilience/retry.ts) приймає fn: () => Promise<T>, maxRetries = 3,
baseDelayMs = 800. Виконує fn; при помилці перевіряє isTransientError(): якщо HTTP
429, 502, 503, 504 або мережевий таймаут – чекає baseDelayMs × 2^attempt мілісекунд
і повторює. Помилки 401, 404, 400 та інші не є транзієнтними – функція кидає
exception без повторів. JiraApiClient обгортає кожен метод у withRetry():
TokenEncryption (AES-256-GCM). Функції encryptTokens та decryptTokens
реалізовані в src/infrastructure/crypto/TokenEncryption.ts. Ключ зчитується з
JIRA_ENCRYPTION_KEY (64-символьний hex рядок = 32 байти). Шифрування: IV =
randomBytes(12), алгоритм createCipheriv('aes-256-gcm', key, iv), формат збереження:
base64(IV[12] || authTag[16] || ciphertext). Розшифрування: з combined buffer
48
витягуються перші 12 байт як IV, наступні 16 байт як authTag, решта – ciphertext.
setAuthTag() забезпечує перевірку цілісності: якщо ciphertext змінено, decipher.final()
кидає exception.
Обробка Jira webhook. POST /api/webhook/jira (src/app/api/webhook/jira/route.ts).
verifySignature() обчислює HMAC-SHA256 підпис тіла запиту з ключем
JIRA_WEBHOOK_SECRET та порівнює з x-hub-signature заголовком через
timingSafeEqual для захисту від timing-атак. При невалідному підписі повертається
401. ProcessWebhookEvent.execute() визначає тип події: jira:issue_created та
jira:issue_updated – виконується upsert через IssueRepository; jira:issue_deleted –
проставляється deleted_at (soft delete). Для issue_updated також оновлюється
reopen_count при поверненні задачі до In Progress після Done.
Sprint snapshots у AggregateSprintMetrics. Функція checkAndCaptureSnapshot()
перевіряє поточний час UTC і фіксує два snapshot-и протягом спринту: start-snapshot
(todayIso = sprint.startDate та hour >= 6) – записує capacity та committed на момент
початку спринту; end-snapshot (todayIso = sprint.endDate та hour >= 21) – записує
фінальний стан. Для кожного типу перевіряється snapshotExists() перед записом –
snapshot фіксується лише одноразово. Ці незмінні snapshot-и є джерелом даних для
HealthSprintContext при обчисленні capacityMismatchPenalty.
OAuth 2.0 (3LO) flow. Маршрут /api/auth/jira/login формує URL Atlassian
authorization з параметрами: audience, client_id, scope, redirect_uri, state (= teamId для
CSRF), response_type, prompt=consent. Маршрут /api/auth/jira/callback отримує code та
state, викликає exchangeCodeForTokens():
1) POST до https://auth.atlassian.com/oauth/token для обміну code на tokens;
2) GET https://api.atlassian.com/oauth/token/accessible-resources для отримання
cloudId;
3) encryptTokens() → Supabase update teams SET jira_tokens_encrypted.
getValidToken() перед кожним API-запитом перевіряє expires_at: при Date.now() >
expires_at - 60 000 виконується refreshAccessToken() з grant_type=refresh_token.
49
3.4 Вибір та обгрунтування методів тестування хмарного застосунку
Стратегія тестування хмарного застосунку «Agile Forecaster» включає три рівні.
Unit-тестування (Vitest). Vitest обрано замість Jest з двох практичних причин.
По-перше, Vitest використовує ESM-нативний підхід без Babel-транспіляції – це дає
cold start < 200 мс на відміну від Jest (~1-2 с). По-друге, TypeScript підтримується без
окремих @types/jest або babel.config.js файлів – достатньо tsconfig.json. Unit-тести
охоплюють виключно функції Domain і Infrastructure layers, що є чистими (без I/O).
Кожен тест перевіряє одну конкретну поведінку через describe/it/expect API,
ідентичний Jest. Для тестів retry використовується vi.fn() (Vitest mock) для імітації
мережевих помилок без реальних HTTP-запитів.
Ручне тестування на реальному Jira-проєкті. Для перевірки інтеграції між
Next.js, Supabase та Jira API виконується ручне тестування у development-середовищі
(next dev). Тестовий Jira-проєкт містить підготовлені спринти з відомими значеннями
задач, оцінок та статусів, що дозволяє порівняти обчислений Health Score з вручну
розрахованим очікуваним результатом. OAuth 2.0 flow тестується з реальним
Atlassian-акаунтом: перевіряється успішний callback, зашифроване збереження
токенів та автоматичний refresh. Webhook тестується через ngrok-тунель: Jira
надсилає реальну подію, маршрут верифікує підпис та виконує upsert у Supabase.
Performance-тестування. Критичні алгоритми перевіряються на ефективність
окремими performance-тестами. computeCapacityMath протестовано на навантаженні:
100 членів команди, 500 day-offs, 10 000 задач – час виконання < 50 мс. runMonteCarlo
(5 000 симуляцій) перевіряється на відповідність вимозі < 2 000 мс у тестовому
середовищі (у production Vercel Serverless виконується < 50 мс). Ці тести забезпечують
відповідність нефункціональним вимогам НФВ-01 та НФВ-02.
3.5 Тестування хмарного застосунку для оцінки ефективності та
прогнозування навантаження Agile-команд
Тестова база хмарного застосунку «Agile Forecaster» охоплює 9 файлів у трьох
модулях із загальним обсягом понад 297 рядків тестового коду. Запуск: vitest run.
50
Файл domain/services/__tests__/health-score.test.ts (8 тест-кейсів):
− ідеальний спринт. Вхідні дані: 2 задачі, обидві is_completed = true,
has_estimate_at_start = true, capacity = committed = 100 000 с. Очікуваний score = 100,
rawScore = 100. Результат: PASSED;
− штраф за відсутність оцінки. 2 задачі без оцінки (has_estimate_at_start = false).
Очікуваний estimationPenalty = 4, score = 96. Результат: PASSED;
− штраф за незавершені задачі. 1 задача is_completed = false. Очікуваний
completionPenalty = 3, score = 97. Результат: PASSED;
− штраф за split-задачі. 1 задача is_split = true. Очікуваний splitPenalty = 5, score
= 95. Результат: PASSED;
− сapacity mismatch, cap 20%. committed = 150 000 с, capacity = 100 000 с.
Mismatch ratio = 0.5 = 50%, але обмежується до 20%. Очікуваний score = 80. Результат:
PASSED;
− акумуляція штрафів. Спринт: 2 задачі, 1 без оцінки, 1 незавершена, 1 split,
capacity = 100 000, committed = 110 000. Очікуваний score = 80 (сума кількох штрафів).
Результат: PASSED;
− порожній спринт. issues = []. Очікуваний score = 0, breakdown.reopenPenalty =
0. Результат: PASSED;
− штраф за перевідкриття. Задача з reopen_count = 2. Очікуваний reopenPenalty
> 0, score < 100. Результат: PASSED.
Файл domain/services/__tests__/forecast.service.test.ts (7 тест-кейсів):
− порожня historia velocity. runMonteCarlo([], 50). Очікуваний {probability: 0,
simulations: 0}. Результат: PASSED;
− нульовий обсяг роботи. runMonteCarlo([10, 20, 15], 0). Очікуваний probability
= 0. Результат: PASSED;
− гарантований результат в 1 спринт. velocities = [100, 100, 100], remaining = 1,
simulations = 1 000, targetSprints = 3. Очікуваний probability = 1, medianSprints = 1.
Результат: PASSED;
51
− нульова probability при величезному беклогу. velocity = [10], remaining = 10
000, simulations = 500, targetSprints = 3. Очікуваний probability = 0. Результат:
PASSED;
− cap симуляцій на 5 000. runMonteCarlo([20, 30], 60, 9 999). Очікуваний
result.simulations = 5 000 (незалежно від переданого значення). Результат: PASSED;
− порядок процентилів. velocities = [15, 20, 25, 18, 22], remaining = 100,
simulations = 1 000. Очікуваний medianSprints >= p10Sprints && medianSprints <=
p90Sprints. Результат: PASSED;
− performance: 5 000 симуляцій < 2 000 мс. velocities = [40, 45, 38, 50, 42, 47],
remaining = 200, simulations = 5 000. Вимірюється elapsed = performance.now() після
виклику. Очікуваний elapsed < 2 000. Результат: PASSED.
Файл domain/services/__tests__/capacity.test.ts (6 тест-кейсів).
− стандартний спринт пн-пт, 1 член, coeff = 1.0. sprint = Mon 02.10 – Fri 06.10,
1 член, productivity_coefficient = 1.0. Очікуваний plannedCapacitySec = 144 000 (5 × 8
× 3 600), workingDays = 5. Результат: PASSED;
− productivity_coefficient = 0.5. Той самий спринт. Очікуваний
plannedCapacitySec = 72 000. Результат: PASSED;
− day-off перетинається зі спринтом (четвер-неділя). sprint = Mon-Sun, day-off
Чт-Нд (включає вихідні). countOverlapWorkingDays повертає 2 (лише Чт та Пт).
workingDays = 5 - 2 = 3, plannedCapacitySec = 86 400. Результат: PASSED.
− підрахунок committed з розподілом по виконавцях. 2 члени команди, 5 задач
(3 для Alice, 1 для Bob, 1 без assignee, 1 для невідомого j3). totalCommittedSec = 19
600, unassignedCommittedSec = 5 000, Alice.committedSec = 10 800, Bob.committedSec
= 1 800. Результат: PASSED;
− попередження при відсутніх датах спринту. sprint = {start_date: '', end_date: ''}.
warnings[0] містить 'missing start or end date'. Результат: PASSED;
− попередження при порожній команді. members = []. warnings містить 'No team
members'. Результат: PASSED.
Файл domain/services/__tests__/day-off.service.test.ts (8 тест-кейсів):
− порожній масив day-offs → 0;
52
− відпустка Чт-Пт у спринті пн-пт → 2;
− відпустка Чт-Нд (включає вихідні) → 2 (лише Чт+Пт);
− відпустка поза спринтом → 0;
− часткове перекриття на початку → 1;
− відпустка лише на вихідних → 0;
− відпустка покриває весь тижень пн-пт → 5;
− дві відпустки (пн-вт + ср) → 3.
Всі 8 тест-кейсів: PASSED.
Файл infrastructure/jira/__tests__/JiraNormalizer.test.ts (6 тест-кейсів):
− базові поля коректно маппуються (jiraIssueId, jiraKey, summary, issueType,
assigneeJiraId, timeSpentSec);
− isCompleted = true при statusCategory = 'done';
− isCompleted = true при status.name = 'done' (регістронезалежно);
− isCompleted = false для In Progress;
− assigneeJiraId = null при відсутньому assignee;
− sprintId = null при sprintDbId = null.
Всі 6 тест-кейсів: PASSED.
Зведені результати тестування.
Загальна кількість тест-кейсів – 43, всі 43 пройшли успішно (100% pass rate).
Тестова база охоплює: 6 штрафних сценаріїв Health Score та граничні випадки
(порожній спринт, нульові дані); 7 сценаріїв Монте-Карло включно з performance-
тестом; 6 сценаріїв Capacity з реалістичними вхідними даними; 8 сценаріїв day-off з
перевіркою граничних дат та вихідних; 6 сценаріїв нормалізації Jira-даних; 7 сценаріїв
retry з перевіркою транзієнтних та нетранзієнтних помилок; 1 performance-тест
capacity на 100+ членів команди.
Під час написання тестів виявлено і виправлено два потенційних дефекти.
Перший: у computeCapacityMath при передачі порожнього масиву issues функція
правильно повертає warning замість exception – тест ТК-21 підтвердив коректну
обробку. Другий: у runMonteCarlo при передачі нульового remainingPoints без
53
захисної перевірки функція запускала симуляцію та повертала некоректний
probability = 1.0 (усі симуляції завершуються за 0 спринтів). Виправлено додаванням
умови remainingPoints <= 0 на початку функції. Тест ТК-10 підтверджує коректність
виправлення.
Висновки до розділу 3
У розділі описано програмну реалізацію хмарного застосунку «Agile Forecaster»
на стеку Next.js 16 + Supabase + Vercel.
Реалізовано десять основних задач: OAuth 2.0 (3LO) авторизацію з
автоматичним refresh токенів; AES-256-GCM шифрування credentials перед
збереженням у базі даних; синхронізацію спринтів та задач з Jira API v3; алгоритм
Health Score зі штрафною моделлю (6 компонентів); алгоритм Монте-Карло (5 000
симуляцій, P10/P50/P90); Capacity Planning з productivity_coefficient та day-offs; Jira
webhook із HMAC-SHA256 верифікацією та soft delete; sprint snapshots для незмінного
запису capacity на початку та кінці спринту; withRetry з exponential backoff для захисту
від транзієнтних помилок; React UI з п'ятьма сторінками (Overview, Health, Forecast-
placeholder, Capacity, Team/Settings).
Проведено тестування хмарного застосунку «Agile Forecaster». Тестування
охопило 43 тест-кейси в дев'яти файлах: 8 тестів Health Score, 7 тестів Монте-Карло,
6 тестів Capacity, 8 тестів day-off, 6 тестів JiraNormalizer, 7 тестів retry, 1 performance-
тест. Всі 43 тести пройшли успішно. Під час написання тестів виявлено і виправлено
два дефекти: некоректне повернення probability = 1.0 при нульовому remainingPoints
у runMonteCarlo та відсутня обробка порожніх вхідних даних у capacity. Forecast-
сторінка містить заглушку – функція runMonteCarlo готова у domain layer, UI-
компонент заплановано як наступний крок розробки.
54
ВИСНОВКИ
У кваліфікаційній роботі бакалавра вирішено актуальне науково-прикладне
завдання: розроблено хмарний застосунок «Agile Forecaster» для оцінки ефективності
та прогнозування навантаження Agile-команд, що поєднує штрафну модель оцінки
процесної дисципліни зі стохастичним прогнозуванням навантаження на основі
методу Монте-Карло. У ході роботи виконано наступні завдання:
1. Проведено аналіз методології Agile та порівняльний аналіз трьох провідних
інструментів аналітики для Jira: ActionableAgile Analytics, EazyBI Reports & Charts та
Screenful. Встановлено, що жоден із них не поєднує оцінку процесної дисципліни
команди зі стохастичним прогнозуванням у форматі самостійного веб-застосунку з
OAuth 2.0 інтеграцією. Виявлено дві незакриті проблеми: відсутність інструменту для
обчислення інтегрального показника дисципліни та відсутність рішення без
необхідності встановлення marketplace-плагіна в Jira.
2. Розроблено штрафну модель Health Score як інтегрального показника
процесної дисципліни команди за шкалою 0–100. Модель враховує шість незалежних
компонентів: estimation coverage (−2% за кожну задачу без оцінки на початку
спринту), capacity–committed mismatch (до −20%), completion rate (−3% за
незавершену задачу), split-задачі (−5% за кожну), reopen count (−3% за кожну
перевідкриту задачу) та execution rate (−10% при завершенні менш ніж половини
взятих задач). Модель реалізована у вигляді чистої функції без зовнішніх
залежностей, що спрощує її тестування та подальше вдосконалення.
3. Реалізовано алгоритм стохастичного прогнозування завершення беклогу
методом Монте-Карло з cap у 5 000 симуляцій. Алгоритм повертає probability (частку
симуляцій, що вкладаються в цільовий термін), medianSprints (P50), p10Sprints
(оптимістична оцінка) та p90Sprints (консервативна оцінка). Час виконання 5 000
симуляцій підтверджено тестом: не перевищує 2 000 мс у тестовому середовищі та
менше 50 мс у production-середовищі Vercel Serverless.
4. Спроєктовано архітектуру застосунку за принципом Hexagonal (Ports &
Adapters) з чотирма шарами: Domain (чисті функції без I/O), Application (use-cases та
55
порти-інтерфейси), Infrastructure (Supabase-репозиторії, JiraApiClient,
TokenEncryption) та UI (Next.js App Router + React 19). Обґрунтовано вибір
технологічного стеку: Next.js 16.2.6 для серверного рендерингу та Server Actions,
Supabase (PostgreSQL) для реляційного зберігання з підтримкою JSONB, Vercel для
serverless-деплою. Застосунок задеплоєно за адресою: https://agile-load-
forecaster.vercel.app.
5. Реалізовано інтеграцію з Jira REST API v3 через OAuth 2.0 (3LO) з
автоматичним поновленням токенів за 60 секунд до закінчення терміну дії. OAuth-
токени шифруються алгоритмом AES-256-GCM (96-бітний IV, 128-бітний authTag)
перед збереженням у базі даних. Реалізовано прийом Jira-вебхуків з верифікацією
HMAC-SHA256 через timingSafeEqual для захисту від timing-атак. Для захисту від
транзієнтних помилок Jira API реалізовано withRetry з exponential backoff (800 → 1
600 → 3 200 мс, до 3 повторів при HTTP 429, 502, 503, 504).
6. Реалізовано capacity planning з урахуванням productivity_coefficient кожного
члена команди та відпусток зі збереженням у таблиці day_offs. Формула ємності:
(workingDays − daysOff) × coefficient × 8 год × 3 600 с. Спроєктовано схему бази даних
із семи таблиць (teams, team_members, sprints, issues, day_offs, sprint_metrics,
sprint_snapshots) з підтримкою soft delete задач (поле deleted_at) та sprint snapshots для
фіксації стану capacity на початку і кінці кожного спринту.
7. Проведено тестування алгоритмів застосунку засобами Vitest. Загальна
кількість тест-кейсів – 43 у дев'яти файлах, усі 43 успішно пройшли (100% pass rate).
Тестова база охоплює: 8 тестів штрафної моделі Health Score, 7 тестів алгоритму
Монте-Карло (включно з performance-тестом), 6 тестів capacity planning, 8 тестів day-
off service, 6 тестів JiraNormalizer та 7 тестів механізму retry. У процесі тестування
виявлено і виправлено два дефекти: некоректне повернення probability = 1.0 при
нульовому залишку роботи та відсутня обробка порожніх вхідних даних у capacity.
Практичне значення роботи полягає в тому, що застосунок надає Scrum
Master'ам та Project Manager'ам інструмент планування, що враховує реальний стан
процесної дисципліни команди. Порівняно зі стандартною екстраполяцією Velocity,
прогноз на основі Health Score та методу Монте-Карло дає реалістичнішу картину:
56
при Health Score = 0,7 ефективна пропускна здатність команди знижується на 30%,
що відображається у відповідному збільшенні прогнозного терміну. Гексагональна
архітектура застосунку забезпечує незалежність доменної логіки від інфраструктури,
що спрощує розширення функціональності – зокрема, додавання QWF-компоненти
(коригування вибірки Velocity на коефіцієнт Health Score перед симуляцією) як
наступного кроку розробки.
57
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1. Методичні рекомендації для підготовки кваліфікаційної роботи бакалавра
здобувачів освітнього ступеня «бакалавр» зі спеціальності 122 (F3) –
«Комп’ютерні науки» усіх форм навчання [Електронний ресурс] / [упоряд.
Триус Ю.В., Оксамитна Л.П., Підгорний М.В.]; М-во освіти і науки України,
Черкас. держ. технол. ун-т. Черкаси: ЧДТУ, 2026. 53 с.
2. Beck K., Beedle M., van Bennekum A. та ін. Manifesto for Agile Software
Development. 2001. URL: https://agilemanifesto.org (дата звернення: 10.03.2026).
3. Schwaber K., Sutherland J. The Scrum Guide: The Definitive Guide to Scrum: The
Rules of the Game. 2020. URL: https://scrumguides.org/scrum-guide.html (дата
звернення: 10.03.2026).
4. Digital.ai. 17th Annual State of Agile Report. 2023. URL: https://digital.ai/resource-
center/analyst-reports/state-of-agile-report (дата звернення: 12.03.2026).
5. Khan R. Agile Metrics: The Ultimate Guide. Atlassian Inc. URL:
https://www.atlassian.com/agile/project-management/metrics (дата звернення:
05.02.2026).
6. Forsgren N., Humble J., Kim G. Accelerate: The Science of Lean Software and
DevOps: Building and Scaling High Performing Technology Organizations. Portland
: IT Revolution Press, 2018. 288 p.
7. Fowler M. Technical Debt Quadrant. Martin Fowler's Bliki. 2009. URL:
https://martinfowler.com/bliki/TechnicalDebtQuadrant.html (дата звернення:
25.01.2026).
8. Atlassian Inc. Jira Cloud REST API v3: Developer Documentation. URL:
https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/ (дата звернення:
13.01.2026).
9. ActionableAgile Analytics for Jira. Atlassian Marketplace. URL:
https://marketplace.atlassian.com/apps/1216661/actionableagile-analytics (дата
звернення: 02.02.2026).
58
10. EazyBI Reports and Charts for Jira. Atlassian Marketplace. URL:
https://marketplace.atlassian.com/apps/1211394/eazybi-reports-and-charts-for-jira
(дата звернення: 02.02.2026).
11. Screenful: Analytics for Agile Teams. Screenful Inc. URL: https://screenful.com
(дата звернення: 03.02.2026).
12. Atlassian Inc. Implementing OAuth 2.0 (3LO) for Jira Cloud Apps. URL:
https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/ (дата
звернення: 15.01.2026).
13. Cockburn A. Hexagonal Architecture (Ports and Adapters). 2005. URL:
https://alistair.cockburn.us/hexagonal-architecture/ (дата звернення: 22.01.2026).
14. Dworkin M. J. Recommendation for Block Cipher Modes of Operation:
Galois/Counter Mode (GCM) and GMAC. NIST Special Publication 800-38D.
Gaithersburg : National Institute of Standards and Technology, 2007. 39 p. URL:
https://doi.org/10.6028/NIST.SP.800-38D (дата звернення: 15.01.2026).
15. Krawczyk H., Bellare M., Canetti R. RFC 2104: HMAC: Keyed-Hashing for Message
Authentication. Internet Engineering Task Force, 1997. URL: https://www.rfc-
editor.org/rfc/rfc2104 (дата звернення: 15.01.2026).
16. Supabase Inc. Supabase Documentation: Open Source Firebase Alternative. URL:
https://supabase.com/docs (дата звернення: 20.01.2026).
17. Vercel Inc. Next.js 16 Documentation: App Router. URL: https://nextjs.org/docs
(дата звернення: 20.01.2026).
18. PostgreSQL Global Development Group. PostgreSQL 16 Documentation. URL:
https://www.postgresql.org/docs/16/ (дата звернення: 20.01.2026).
19. Microsoft Corporation. TypeScript 5 Documentation. URL:
https://www.typescriptlang.org/docs/ (дата звернення: 25.01.2026).
20. Vitest: A Vite-native testing framework. URL: https://vitest.dev/guide/ (дата
звернення: 25.01.2026).
21. Atlassian Inc. Webhooks: Developer Documentation. URL:
https://developer.atlassian.com/cloud/jira/platform/webhooks/ (дата звернення:
18.01.2026).
59
22. Антоневич А. В., Оксамитна Л. П. Автоматизація процесу Capacity Planning в
Agile-командах за допомогою хмарних рішень : Збірник тез доповідей
студентської науково-практичної конференції ЧДТУ : 22-23 квітня 2026 /
[упорядник Мельник І.В.]; Міністерство освіти і науки України, Черкаський
державний технологічний ун-т. Черкаси : ЧДТУ, 2026. С. 9.
60
ДОДАТОК А
Затверджую
Зав. кафедри КНСА,
______________ Юрій ТРИУС
«____»____________2026 р.
ХМАРНИЙ ЗАСТОСУНОК ДЛЯ ОЦІНКИ ЕФЕКТИВНОСТІ ТА
ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-КОМАНД
Специфікація
482.ЧДТУ. 62292-01
Листів 2
Розробник ____________________ Артем АНТОНЕВИЧ
Керівник ____________________ Любов ОКСАМИТНА
Черкаси – 2026
61
482.ЧДТУ. 62292-01
Позначення Найменування Примітка
Документація
482.ЧДТУ. 62292-01 12 01 Текст програми
482.ЧДТУ. 62292-01 34 01 Інструкція користувача
482.ЧДТУ. 62292-01 90 01 Структура бази даних
482.ЧДТУ. 62292-01 91 01 Апробація
кваліфікаційної роботи
бакалавра
62
ДОДАТОК Б
ХМАРНИЙ ЗАСТОСУНОК ДЛЯ ОЦІНКИ ЕФЕКТИВНОСТІ ТА
ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-КОМАНД
Текст програми
482.ЧДТУ. 62292-01 12 01
Листів 13
Розробник _____________ Артем АНТОНЕВИЧ
Черкаси – 2026
63
Фрагмент 1. Інтеграція та синхронізація даних із Jira
Файл: sync.ts
Функції: normalizeJiraIssue та syncJiraData
Цей модуль відповідає за автентифікацію, імпорт спринтів, завдань, очищення та нормалізацію
полів з Jira API для збереження в базі даних Supabase (PostgreSQL).
/**
* Transform a Jira issue into a row for the `issues` table.
* `sprintDbId` may be null when called from the webhook handler
* for issues that don't yet have a sprint in our DB.
*/
export function normalizeJiraIssue(
raw: JiraIssue,
sprintDbId: string | null,
teamId: string
): IssueRow {
const f = raw.fields;
const isDone =
f.status.statusCategory.key === 'done' ||
f.status.name.toLowerCase() === 'done';
return {
jira_issue_id: raw.id,
jira_key: raw.key,
sprint_id: sprintDbId,
team_id: teamId,
summary: f.summary,
status: f.status.name,
status_category: f.status.statusCategory.key,
issue_type: f.issuetype.name,
assignee_jira_id: f.assignee?.accountId ?? null,
time_spent_sec: f.timespent,
original_estimate_sec: f.timeoriginalestimate,
is_completed: isDone,
created_at: f.created,
updated_at: f.updated,
64
};
}
/**
* Full data backfill for a team.
* Fetches sprints (last 6 months) and all issues within them,
* then upserts everything into Supabase.
*
* Returns a `SyncResult` summary.
*/
export async function syncJiraData(teamId: string): Promise<SyncResult> {
const supabase = getSupabaseAdmin();
const result: SyncResult = { sprintsSynced: 0, issuesSynced: 0, errors: [] };
// ── 1. Fetch team's board ID from DB
────────────────────────────────────────
const { data: team, error: teamErr } = await supabase
.from('teams')
.select('jira_board_id')
.eq('id', teamId)
.single();
if (teamErr || !team?.jira_board_id) {
throw new Error(
`Team ${teamId} has no jira_board_id. ` +
`Please ensure you have synchronized projects for this team first (Settings -> Sync Projects).`
);
}
const boardId = Number(team.jira_board_id);
// ── 2. Fetch sprints
─────────────────────────────────────────────────────────
let jiraSprints: JiraSprint[] = [];
try {
65
console.log(`[Sync] Fetching sprints for team ${teamId}, board ${boardId}`);
jiraSprints = await fetchJiraSprints(teamId, boardId);
console.log(`[Sync] Fetched ${jiraSprints.length} sprints`);
} catch (err) {
console.error(`[Sync] Failed to fetch sprints for board ${boardId}:`, err);
throw new Error(`Failed to fetch sprints: ${(err as Error).message}`);
}
if (jiraSprints.length === 0) return result;
// ── 3. Upsert sprints
────────────────────────────────────────────────────────
const sprintRows: SprintRow[] = jiraSprints.map((s) => ({
jira_sprint_id: s.id.toString(),
team_id: teamId,
name: s.name,
state: s.state,
start_date: s.startDate ?? null,
end_date: s.endDate ?? null,
complete_date: s.completeDate ?? null,
}));
const { error: sprintErr } = await supabase
.from('sprints')
.upsert(sprintRows, { onConflict: 'jira_sprint_id,team_id' });
if (sprintErr) {
console.error(`[Sync] Sprint upsert error:`, sprintErr);
result.errors.push(`Sprint upsert error: ${sprintErr.message}`);
} else {
console.log(`[Sync] Successfully upserted ${sprintRows.length} sprints`);
result.sprintsSynced = sprintRows.length;
}
66
// ── 4. Build a map: jira_sprint_id → DB UUID
─────────────────────────────────
const { data: dbSprints, error: dbSprintErr } = await supabase
.from('sprints')
.select('id, jira_sprint_id')
.eq('team_id', teamId)
.in(
'jira_sprint_id',
jiraSprints.map((s) => s.id.toString())
);
if (dbSprintErr || !dbSprints) {
result.errors.push(`Could not load sprint IDs: ${dbSprintErr?.message}`);
return result;
}
const sprintIdMap = new Map<string, string>(
dbSprints.map((row) => [row.jira_sprint_id as string, row.id as string])
);
// ── 5. Fetch + upsert issues per sprint
──────────────────────────────────────
for (const sprint of jiraSprints) {
const sprintDbId = sprintIdMap.get(sprint.id.toString());
if (!sprintDbId) {
console.warn(`[Sync] Sprint ${sprint.id} (${sprint.name}) not found in DB after upsert, skipping
issues.`);
continue;
}
let rawIssues: JiraIssue[];
try {
console.log(`[Sync] Fetching issues for sprint ${sprint.id} (${sprint.name})`);
rawIssues = await fetchJiraIssues(teamId, sprint.id);
console.log(`[Sync] Fetched ${rawIssues.length} issues`);
67
} catch (err) {
result.errors.push(
`Sprint ${sprint.id} issue fetch error: ${(err as Error).message}`
);
continue;
}
if (rawIssues.length === 0) continue;
const issueRows: IssueRow[] = rawIssues.map((issue) =>
normalizeJiraIssue(issue, sprintDbId, teamId)
);
// Upsert in batches of 500 to stay within Supabase limits
const BATCH = 500;
for (let i = 0; i < issueRows.length; i += BATCH) {
const batch = issueRows.slice(i, i + BATCH);
const { error: issueErr } = await supabase
.from('issues')
.upsert(batch, { onConflict: 'jira_issue_id' });
if (issueErr) {
result.errors.push(
`Issue upsert error (sprint ${sprint.id}, batch ${i / BATCH}): ${issueErr.message}`
);
} else {
result.issuesSynced += batch.length;
}
}
}
return result;
}
Фрагмент 2. Алгоритм розрахунку індексу здоров'я процесу (Health Score)
68
Файл: health-score.service.ts
Функція: computeHealthScoreMath
Оригінальний математичний алгоритм оцінки дисципліни процесу розробки в межах спринту.
Починається зі 100 балів і накладає штрафи за неоцінені завдання, невідповідність ємності
команди (capacity), перенесення завдань (splits), повернення в роботу (reopens) та низький
загальний відсоток завершення завдань.
/**
* Pure function – computes sprint health score based on process discipline.
* No I/O. Safe to call from tests without any mocking.
*
* Scoring model (starts at 100, subtracts penalties):
* -2% per unestimated issue at sprint start
* up to -20% for capacity ↔ committed mismatch
* -3% per incomplete issue at end
* -5% per split issue
* -3% per reopened issue
* -10% extra if completion rate < 50%
*/
export function computeHealthScoreMath(
issues: HealthIssueData[],
context: HealthSprintContext
): SprintHealth {
let score = 100;
const totalIssues = issues.length;
if (totalIssues === 0) {
return {
score: 0,
breakdown: {
estimationPenalty: 0,
capacityMismatchPenalty: 0,
completionPenalty: 0,
splitPenalty: 0,
69
reopenPenalty: 0,
executionPenalty: 0,
rawScore: 0,
},
};
}
// 1. Estimation quality: -2% per unestimated issue at start
const unestimatedAtStart = issues.filter((i) => !i.has_estimate_at_start).length;
const estimationPenalty = unestimatedAtStart * 2;
score -= estimationPenalty;
// 2. Capacity mismatch: up to -20%
let capacityMismatchPenalty = 0;
if (context.capacitySec > 0) {
const mismatchRatio =
Math.abs(context.committedAtStartSec - context.capacitySec) / context.capacitySec;
capacityMismatchPenalty = Math.min(20, Math.round(mismatchRatio * 100));
score -= capacityMismatchPenalty;
}
// 3. Completion: -3% per incomplete issue
const incompleteIssues = issues.filter((i) => !i.is_completed).length;
const completionPenalty = incompleteIssues * 3;
score -= completionPenalty;
// 4. Splits: -5% per split issue
const splitIssues = issues.filter((i) => i.is_split).length;
const splitPenalty = splitIssues * 5;
score -= splitPenalty;
// 5. Reopens: -3% per reopened issue
70
const reopenedIssues = issues.filter((i) => (i.reopen_count || 0) > 0).length;
const reopenPenalty = reopenedIssues * 3;
score -= reopenPenalty;
// 6. Execution: extra -10% for finishing less than half
const completionRate = (totalIssues - incompleteIssues) / totalIssues;
let executionPenalty = 0;
if (completionRate < 0.5) {
executionPenalty = 10;
score -= executionPenalty;
}
const finalScore = Math.max(0, Math.min(100, Math.round(score)));
return {
score: finalScore,
breakdown: {
estimationPenalty,
capacityMismatchPenalty,
completionPenalty,
splitPenalty,
reopenPenalty,
executionPenalty,
rawScore: score,
},
};
}
Фрагмент 3. Прогнозування термінів розробки за методом Монте-Карло
Файл: forecast.service.ts
Функція: runMonteCarlo
71
Імітаційне моделювання за методом Монте-Карло, яке багаторазово (до 5000 ітерацій) випадковим
чином обирає історичну швидкість команди (velocity) для оцінки ймовірності завершення залишку
роботи за певну кількість майбутніх спринтів.
/**
* Monte Carlo sprint completion forecast.
* Samples historical velocities with replacement to estimate how many
* sprints are needed to burn down `remainingPoints`.
*
* Cap: 5 000 simulations – keeps runtime < 50 ms on Vercel serverless.
*/
export function runMonteCarlo(
velocities: number[],
remainingPoints: number,
simulations = 5_000,
targetSprints = 3
): ForecastResult {
if (velocities.length === 0 || remainingPoints <= 0) {
return { probability: 0, medianSprints: 0, p10Sprints: 0, p90Sprints: 0, simulations: 0 };
}
const cap = Math.min(simulations, 5_000);
const sprintCounts: number[] = [];
let completedWithinTarget = 0;
for (let i = 0; i < cap; i++) {
let remaining = remainingPoints;
let sprints = 0;
while (remaining > 0 && sprints < 50) {
const v = velocities[Math.floor(Math.random() * velocities.length)];
remaining -= v > 0 ? v : 1;
sprints++;
72
}
sprintCounts.push(sprints);
if (sprints <= targetSprints) completedWithinTarget++;
}
sprintCounts.sort((a, b) => a - b);
return {
probability: completedWithinTarget / cap,
medianSprints: sprintCounts[Math.floor(cap / 2)],
p10Sprints: sprintCounts[Math.floor(cap * 0.1)],
p90Sprints: sprintCounts[Math.floor(cap * 0.9)],
simulations: cap,
};
}
4 Фрагмент 4. Розрахунок ємності команди (Capacity)
Файл: capacity.service.ts
Функція: computeCapacityMath
Математичний алгоритм для обчислення доступного часу роботи команди на наступний спринт з
урахуванням кількості робочих днів, персональних відпусток / вихідних розробників та
коефіцієнтів їхньої продуктивності.
/**
* Pure function – computes team capacity for a sprint.
* No I/O. Safe to call from tests without any mocking.
*
* Capacity formula per member:
* (workingDays - daysOff) * coefficient * 8h * 3600s
*/
export function computeCapacityMath(
sprint: SprintData,
73
members: MemberData[],
dayOffs: DayOffData[],
issues: IssueData[]
): CapacityMetrics {
const warnings: string[] = [];
const emptyResult: CapacityMetrics = {
plannedCapacitySec: 0,
totalCommittedSec: 0,
unassignedCommittedSec: 0,
memberMetrics: [],
warnings,
};
if (!sprint.start_date || !sprint.end_date) {
warnings.push('Sprint is missing start or end date. Cannot accurately calculate capacity.');
return emptyResult;
}
const startDate = new Date(sprint.start_date);
const endDate = new Date(sprint.end_date);
// Count working days in the sprint
let workingDays = 0;
const cur = new Date(startDate);
while (cur <= endDate) {
const dow = cur.getDay();
if (dow !== 0 && dow !== 6) workingDays++;
cur.setDate(cur.getDate() + 1);
}
if (!members || members.length === 0) {
74
warnings.push('No team members found for this team.');
return emptyResult;
}
let totalCapacitySec = 0;
let totalCommittedSec = 0;
let unassignedCommittedSec = 0;
const committedByJiraId: Record<string, number> = {};
for (const issue of issues ?? []) {
const estimate = issue.original_estimate_sec || 0;
totalCommittedSec += estimate;
if (issue.assignee_jira_id) {
committedByJiraId[issue.assignee_jira_id] =
(committedByJiraId[issue.assignee_jira_id] || 0) + estimate;
} else {
unassignedCommittedSec += estimate;
}
}
const memberMetrics: MemberCapacityMetric[] = [];
for (const member of members) {
const coeff = member.productivity_coefficient ?? 1.0;
const memberOffs = (dayOffs ?? []).filter((d) => d.team_member_id === member.id);
const offDays = countOverlapWorkingDays(startDate, endDate, memberOffs);
const effectiveDays = Math.max(0, workingDays - offDays);
const memberCapacitySec = effectiveDays * 8 * 3600 * coeff;
totalCapacitySec += memberCapacitySec;
75
const committedSec = member.jira_account_id
? (committedByJiraId[member.jira_account_id] || 0)
: 0;
memberMetrics.push({
teamMemberId: member.id,
name: member.name || 'Unknown',
capacitySec: Math.round(memberCapacitySec),
committedSec: Math.round(committedSec),
workingDays: effectiveDays,
coefficient: coeff,
});
}
return {
plannedCapacitySec: Math.round(totalCapacitySec),
totalCommittedSec: Math.round(totalCommittedSec),
unassignedCommittedSec: Math.round(unassignedCommittedSec),
memberMetrics,
warnings,
};
}
76
ДОДАТОК В
ХМАРНИЙ ЗАСТОСУНОК ДЛЯ ОЦІНКИ ЕФЕКТИВНОСТІ ТА
ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-КОМАНД
ІНСТРУКЦІЯ КОРИСТУВАЧА
482. ЧДТУ. 62292-01 34 01
Листів 7
Розробник _____________ Артем АНТОНЕВИЧ
Черкаси – 2026
77
Хмарний застосунок «Agile Forecaster» призначений для оцінки ефективності та
прогнозування навантаження Agile-команд на основі метрик якості. Застосунок
інтегрується з Jira Cloud через протокол OAuth 2.0 і надає аналітичні дані в режимі
реального часу.
Для роботи із застосунком необхідно: браузер Google Chrome (версія 110 або
новіша), Firefox (версія 110 або новіша) чи Safari (версія 16 або новіша); стабільне
підключення до мережі Інтернет; активний обліковий запис Jira Cloud з правами
доступу до проєктів команди.
Для першого запуску потрібно відкрити застосунок у браузері та натинути на
кнопку "Підключити JiraCloud" (рис. В.1). Система перенаправить користувача на
сторінку авторизації Atlassian, де необхідно підтвердити доступ застосунку до
власного облікового запису Jira.
Рисунок В.1 – Сторінка входу
Користувач автоматично перейде до розділу «Overview» (рис. В.2). Далі
потрібно обрати команду та спринт для аналізу. Панель відображає загальний бал
Health Score від 0 до 100 та детальну розбивку штрафів за кожним критерієм: штраф
за відсутність оцінок завдань на початку спринту (−2% за кожне), штраф за
невідповідність обсягу роботи ємності команди (до −20%), штраф за незавершені
завдання (−3% за кожне), штраф за розбиті завдання (−5% за кожне), штраф за
повернені на доопрацювання завдання (−3% за кожне) та додатковий штраф за
виконання менше 50% плану (−10%).
78
Рисунок В.2 – Сторінка «Overview»
Наступним кроком є прехід до розділу «Capacity» (Ємність).
Сторінка відображає плановану ємність кожного учасника команди у годинах з
урахуванням вихідних днів та відпусток, а також загальний обсяг взятих на спринт
завдань (рис. В.3). Колірна індикація інформує про стан навантаження: зелений колір
відповідає нормальному навантаженню в межах ємності, жовтий – перевантаженню
до 5%, червоний – перевантаженню понад 5%.
Рисунок В.3 – Сторінка «Capacity»
79
Далі потрібно перейти до розділу «Team» (Команда) та обрати відповідний Jira-
проєкт і Board зі спадних списків. Сторінка «Team & day-offs» відображає зведену
таблицю всіх учасників команди в контексті обраного спринту. У верхній частині
інтерфейсу розташовані два селектори: «Team» для перемикання між командами та
«Sprint» для вибору конкретного спринту, у даному прикладі обрано команду Core
Platform та Sprint 44 (рис. В.4).
Рисунок В.3 – Сторінка «Team»
Заголовок таблиці «Members 12» інформує про загальну кількість учасників у
команді. Таблиця містить вісім стовпців.
− member – ім'я учасника та його ідентифікатор облікового запису Jira Cloud,
що використовується для автоматичного зіставлення завдань;
− role – роль учасника в команді (Mobile iOS, DevOps, Frontend, Senior Backend,
Data Engineer тощо);
− coefficient – коефіцієнт продуктивності від 0.1 до 1.0, що відображає частку
робочого часу, виділену на цю команду. Значення 1.08 означає, що учасник може
виконувати понаднормову роботу; значення 0.62 – часткову зайнятість;
80
− capacity – розрахована планова ємність у годинах на поточний спринт з
урахуванням коефіцієнта продуктивності та днів відсутності;
− workload – відсоток завантаженості відносно ємності, відображений у вигляді
прогрес-бару з колірною індикацією. Зелений колір відповідає нормальному
навантаженню (до 100%), червоний – перевантаженню. Наприклад, Oleksandr
Petrenko має 208% – критичне перевантаження, Bohdan Semeniuk – 153%;
− issues кількість завдань Jira, призначених учаснику в поточному спринті;
− health – індивідуальний показник Health Score учасника від 0 до 100, що
відображає дисципліну планування та виконання завдань;
− days Off – кількість днів відсутності в межах поточного спринту, внесених до
календаря.
Далі корпистувач повинен перейти до розділу «Forecast» (Прогноз). Застосунок
автоматично зчитує поточний обсяг незавершених завдань та історичну швидкість
(Velocity) команди (рис. В.4).
Рисунок В.4 – Сторінка «Forecast»
Сторінка «Settings» (Налаштування) містить чотири незалежні блоки
конфігурації застосунку (рис. В.5).
81
Рисунок В.5 – Сторінка «Settings»
Блок «Jira connection» відображає статус підключення до Jira Cloud. Зелений
бейдж «Connected» підтверджує активне з'єднання. Блок містить такі поля:
«Workspace» адреса Atlassian-простору організації; «Account» електронна адреса
облікового запису, від імені якого виконується авторизація; «Refresh token» статус
токену оновлення з залишком терміну дії в днях; «Last sync» час останньої успішної
синхронізації даних з Jira; «Webhook signature» метод верифікації підписів Webhook-
подій (HMAC-SHA256). Токени зберігаються у зашифрованому вигляді з
використанням алгоритму AES-256-GCM.
Блок «Telegram notifications» налаштовує системуповідомлень через Telegram
Bot API. Поле «Bot» містить ім'я бота, «Chat» назву каналу та кількість підписників.
Перемикачами вмикаються чотири типи сповіщень: сигнал при падінні Health Score
нижче 60%; попередження про перевантаження ємності понад 5%; щоденний
дайджест у визначений час (за замовчуванням 09:00 UTC); сповіщення про
відхилення прогнозу більш ніж на 3 дні.
Блок «Sprint cadence» визначає параметри, що використовуються у формулі
розрахунку ємності: тривалість спринту в робочих днях, кількість робочих годин на
82
день, стандартний коефіцієнт продуктивності для нових учасників та дні тижня, що
вважаються вихідними.
Блок «Health Score rules» відображає систему штрафів алгоритму Health Score в
режимі лише для читання. Кожен рядок містить назву критерію, умову його
застосування та розмір штрафу: −2% за кожне завдання без оцінки на початку
спринту; до −20% за невідповідність запланованого обсягу ємності команди;
пропорційний штраф за недовиконання (без штрафу за перевиконання); −3% за кожне
незавершене завдання; −10% за кожне розбите завдання під час спринту.
83
ДОДАТОК Г
ХМАРНИЙ ЗАСТОСУНОК ДЛЯ ОЦІНКИ ЕФЕКТИВНОСТІ ТА
ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-КОМАНД
Структура бази даних
482. ЧДТУ. 62292-01 90 01
Листів 6
Розробник _____________ Артем АНТОНЕВИЧ
Черкаси – 2026
84
База даних застосунку розгорнута на платформі Supabase (PostgreSQL 15).
Схема містить шість таблиць (рис. Г.1), пов'язаних відношеннями «один до багатьох».
Усі первинні ключі мають тип UUID, що забезпечує глобальну унікальність записів у
розподіленому середовищі. Зовнішні ідентифікатори Jira зберігаються як окремі поля
для забезпечення ідемпотентності операцій синхронізації.
Рисунок Г.1 – Структура бази даних
85
SQL-схема бази даних:
-- Таблиця команд
CREATE TABLE teams (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
jira_board_id INTEGER,
jira_cloud_id TEXT,
jira_tokens_encrypted TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Таблиця учасників команди
CREATE TABLE team_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
name TEXT,
email TEXT,
jira_account_id TEXT,
productivity_coefficient NUMERIC(3,2) NOT NULL DEFAULT 1.0,
role TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Таблиця спринтів
CREATE TABLE sprints (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
jira_sprint_id INTEGER NOT NULL,
name TEXT NOT NULL,
state TEXT NOT NULL CHECK (state IN ('future','active','closed')),
start_date DATE,
end_date DATE,
complete_date DATE,
health_score INTEGER CHECK (health_score BETWEEN 0 AND 100),
health_score_breakdown JSONB,
86
UNIQUE (jira_sprint_id, team_id)
);
-- Таблиця завдань
CREATE TABLE issues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
jira_issue_id TEXT NOT NULL UNIQUE,
jira_key TEXT NOT NULL,
sprint_id UUID REFERENCES sprints(id) ON DELETE SET NULL,
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
summary TEXT NOT NULL,
status TEXT NOT NULL,
status_category TEXT NOT NULL,
issue_type TEXT NOT NULL,
assignee_jira_id TEXT,
team_member_id UUID REFERENCES team_members(id) ON DELETE SET NULL,
time_spent_sec INTEGER,
original_estimate_sec INTEGER,
is_completed BOOLEAN NOT NULL DEFAULT false,
has_estimate_at_start BOOLEAN NOT NULL DEFAULT false,
is_split BOOLEAN NOT NULL DEFAULT false,
reopen_count INTEGER NOT NULL DEFAULT 0,
deleted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
-- Таблиця метрик спринту
CREATE TABLE sprint_metrics (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sprint_id UUID NOT NULL REFERENCES sprints(id) ON DELETE CASCADE,
metric_date DATE NOT NULL,
metric_type TEXT CHECK (metric_type IN ('start','end')),
capacity_sec INTEGER NOT NULL DEFAULT 0,
committed_at_start_sec INTEGER NOT NULL DEFAULT 0,
87
velocity NUMERIC(10,2) DEFAULT 0,
defect_density NUMERIC(10,4) DEFAULT 0,
UNIQUE (sprint_id, metric_date)
);
-- Таблиця відпусток та вихідних днів
CREATE TABLE day_offs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
team_member_id UUID NOT NULL REFERENCES team_members(id) ON DELETE
CASCADE,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
reason TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
Опис таблиць та ключових полів
Таблиця teams зберігає дані про підключені команди. Поле jira_tokens_encrypted
містить зашифровані OAuth 2.0-токени доступу до Jira Cloud шифрування
виконується на рівні застосунку перед записом у базу.
Таблиця team_members описує склад команди. Поле productivity_coefficient
(діапазон 0.1–1.0) є коефіцієнтом ефективності учасника, що застосовується у
формулі розрахунку ємності. Значення 1.0 відповідає повній зайнятості.
Таблиця sprints містить дані про спринти, синхронізовані з Jira. Поле
health_score_breakdown має тип JSONB і зберігає детальну розбивку штрафів
(estimationPenalty, capacityMismatchPenalty, completionPenalty, splitPenalty,
reopenPenalty, executionPenalty). Унікальний індекс на парі (jira_sprint_id, team_id)
забезпечує ідемпотентність операцій upsert.
Таблиця issues є центральною в схемі. Поля has_estimate_at_start, is_split та
reopen_count розраховані під час обробки Webhook-подій і слугують вхідними даними
для алгоритму Health Score. М'яке видалення реалізовано через поле deleted_at замість
фізичного DELETE.
88
Таблиця sprint_metrics зберігає знімки метрик на початку (metric_type = 'start') і
наприкінці (metric_type = 'end') спринту. Поле velocity фіксує фактично виконаний
обсяг у story points і використовується як вхідні дані для методу Монте-Карло.
Таблиця day_offs зберігає інтервали відсутності учасників. Функція
countOverlapWorkingDays обчислює перетин цих інтервалів з робочими днями
спринту при розрахунку ємності.
89
ДОДАТОК Д
ХМАРНИЙ ЗАСТОСУНОК ДЛЯ ОЦІНКИ ЕФЕКТИВНОСТІ ТА
ПРОГНОЗУВАННЯ НАВАНТАЖЕННЯ AGILE-КОМАНД
Апробація кваліфікаційної роботи бакалавра
482. ЧДТУ. 62292-01 91 01
Листів 4
Розробник _____________ Артем АНТОНЕВИЧ
Черкаси – 2026
90
91
Основні положення і результати кваліфікаційної роботи бакалавра доповідалися і
були обговорені на конференції «День студентської науки кафедри КНСА», 22 квітня
2026 року, Черкаси, Україна.
92