МенеджментJunior

Changelog

Changelog — це впорядкований, зрозумілий людині запис того, що змінилося в кожному релізі. Уся його цінність — у тому, що він написаний для читача, який вирішує, чи оновлюватися. Саме тому згенероване звалище комітів, хай навіть повне, не є changelog. На цій сторінці — шість заголовків, що обіцяє номер версії, депрекація і як далеко чесно може завести автоматизація.

Написаний для
Людей, не інструментів
Схема версій
MAJOR.MINOR.PATCH
Конвенція
Keep a Changelog

Git log — це не changelog

Історія комітів фіксує, що робила команда, у порядку подій і власною термінологією команди. Changelog фіксує, що змінилося для того, хто користується софтом, згруповано за значущістю. Це різні документи для різних аудиторій, і генерація одного з іншого без редагування дає щось технічно точне й практично нечитабельне.

Запис у changelog

  • Cookie сесії тепер живуть 30 днів замість 7
  • Виправлено збій експорту для файлів понад 50 МБ
  • Видалено ендпоінт пошуку v1 — використовуйте /v2/search

Повідомлення коміту

  • fix: bump TTL constant
  • chore: refactor stream handler
  • feat: drop legacy route
  • Merge branch develop into main

Прочитайте праву колонку очима користувача софту. Жоден запис не каже, чи треба вам щось робити, — а останній не каже взагалі нічого.

Три документи, які плутають

Значна частина поганих changelog — це один файл, що намагається бути трьома. Changelog повний і стислий: кожна зміна, по рядку на кожну, для читача, що вирішує, чи оновлюватися. Реліз-ноти вибіркові й рекламні: три речі, варті анонсу, для того, хто не стежив. Гайд з міграції інструктивний: для кожної ламкої зміни — старий код і новий, для того, хто саме посеред оновлення, яке щойно впало.

У них різна довжина, різний тон і різна тривалість життя, а спроба вдовольнити всі три одразу дає документ, задовгий для перегляду й заповерхневий для дій. Замість цього — посилайтеся: рядок changelog називає ламку зміну й веде на розділ гайду з міграції, що її пояснює.

Номер версії — це обіцянка

Semantic Versioning перетворює три числа на контракт сумісності. Річ не в охайності, а в тому, що читач може вирішити, чи безпечне оновлення, не читаючи diff, а менеджер залежностей може вирішити це замість нього.

У семантичному версіонуванні кожен сегмент є обіцянкою. Підняття major означає, що наявним користувачам треба щось змінити. Minor додає можливості, зберігаючи сумісність. Patch виправляє поведінку, не змінюючи контракту, — тож читач може вирішити, чи безпечне оновлення, не читаючи diff.

Правило, на якому все тримається: якщо наявним користувачам треба щось змінити, щоб усе далі працювало, це підняття MAJOR — незалежно від того, наскільки дрібною зміна здається зсередини. Перейменування поля, суворіша валідація, перетворення необов’язкового параметра на обов’язковий і прибирання значення за замовчуванням — усе це ламкі зміни, і випуск їх патчем є тим, як залежність заробляє собі репутацію.

Нуль, пререлізи та метадані збірки

Усе, що починається з 0., явно поза обіцянкою: під час 0.y.z специфікація каже, що будь-що може змінитися будь-коли. Це чесно, поки дизайн ще рухається, і нечесно, коли від нього залежать реальні користувачі. Проєкт, що чотири роки сидить на 0.x зі споживачами в production, використовує нуль як спосіб уникнути зобов’язання, а не як сигнал нестабільності.

Пререліз позначають дефісом — 2.0.0-rc.1 — і він сортується перед релізом, якому передує, тож менеджер залежностей, що просить 2.0.0, не візьме його випадково. Метадані збірки позначають плюсом — 2.0.0+build.482 — і при порівнянні версій їх повністю ігнорують, тож це правильне місце для хешу коміту й неправильне для будь-чого, що користувачеві треба знати.

Що містить хороший запис

Конвенція Keep a Changelog групує кожен реліз під шістьма заголовками. Однакові шість скрізь означають, що читач може одразу шукати потрібний — найчастіше це Removed і Changed, бо саме вони створюють роботу.

ЗаголовокДля чогоЧитач питає
AddedНові можливостіЧи з’явилося щось, що я тепер можу?
ChangedНаявна поведінка, що зміниласяЧи поводитиметься моє поточне використання інакше?
DeprecatedЩе працює, буде видаленоЗ чого і до якого часу треба мігрувати?
RemovedЗникло в цій версіїЩо зламається одразу після оновлення?
FixedВиправлені дефектиЧи потрібен ще мій обхідний шлях?
SecurityУсунені вразливостіЧи треба оновлюватися терміново?

Решта конвенції механічна, і її варто дотримуватися точно, бо саме вона дозволяє читачеві щось знайти. Найновіша версія — угорі. Один розділ на випущену версію з датою за ISO — 2026-08-19, а не 19/08/26, що означає різне залежно від країни читача. Розділ Unreleased над усіма. І примітка, якої схеми версіонування дотримується проєкт, щоб ніхто не вгадував, що означає підняття minor.

Дві звички роблять більшу частину решти роботи: наповнюйте розділ Unreleased у міру появи змін, щоб записи писалися, поки зміна свіжа, і формулюйте кожен рядок як наслідок для користувача, а не як правку в коді. Changelog, зібраний напередодні релізу, завжди виходить худим, бо той, хто його збирає, відновлює місяць за темами комітів.

Депрекація — те, що купує довіру

Removed — заголовок, що створює роботу; Deprecated — заголовок, що дає час її зробити. Депрекація — це обіцянка з трьох частин, а повідомлення про депрекацію без якоїсь із них є просто скаргою на власний код.

  • Що зникає — названо достатньо точно, щоб читач міг пошукати це у власному коді.
  • Що використовувати натомість — і заміна вже доступна, а не обіцяна на потім.
  • Коли це буде прибрано — вказано версією, а не порою року.

Робоча політика вміщається в рядок: усе, що задепрековано в minor-релізі, прибирається не раніше наступного major і ніколи між ними. Опублікуйте це одного разу — і кожне повідомлення про депрекацію це успадкує. Ціна — нести мертвий код один цикл; віддача — оновлення вашого софту перестає бути тим, що відкладають, а це різниця між залежністю, яку тримають свіжою, і тією, що тихо форкається в шести організаціях.

Як далеко чесно заходить автоматизація

Conventional Commits стандартизує тему коміту так, щоб її міг прочитати інструмент: тип, необов’язкова область, необов’язковий знак оклику для ламкої зміни, далі опис — «feat(auth): accept refresh tokens», «fix!: reject unsigned webhooks». Ламку зміну можна також оголосити у футері, що починається з «BREAKING CHANGE:», і це краще місце, коли потрібне речення пояснення.

КомітОзначаєПотрапляє під
fix:Підняття PATCHFixed
feat:Підняття MINORAdded
! або BREAKING CHANGE:Підняття MAJORChanged або Removed
chore:, docs:, refactor:Без підняттяЗазвичай нікуди — немає видимого ефекту

Це справді допомагає: інструмент може вивести наступний номер версії й зібрати чернетку, згруповану під правильними заголовками, — а це два найнудніші кроки. Чого він не може — вирішити, чи є зміна ламкою, бо це залежить від того, як люди насправді користуються продуктом, а це знання живе поза репозиторієм. Коміт із міткою fix:, що змінює значення за замовчуванням, є ламким; інструмент назве це патчем і буде впевнено неправим.

Тому робочий поділ такий: чернетку збирає машина, перед релізом редагує людина. П’ятнадцять хвилин редагування перетворюють правильний список на читабельний: три коміти зливаються в один запис, шість непомітних користувачеві видаляються, а два, сформульовані зсередини коду, переписуються.

Як це виявляється в реальній поставці

Перевірка changelog — чи відкриває його хтось під тиском. Після оновлення о 16:40 щось зламалося; людина, яка ніколи не читала вашого коду, відкриває файл і має менш ніж за хвилину знайти, чи змінювала версія, на яку вона перейшла, те, на що вона зараз дивиться. Усе в конвенції — новіше вгорі, шість фіксованих заголовків, рядок на зміну, наслідки замість правок — існує, щоб зробити цю хвилину можливою.

Де він вироджується

  • Згенерований дамп комітів, опублікований без редагування, ніхто не читає двічі.
  • Ламкі зміни, випущені як minor чи patch, руйнують контракт версії.
  • Немає заявленого публічного API — тоді будь-який рефакторинг начебто ламкий, і ніхто цього не розрізнить.
  • Роками сидіти на 0.x зі споживачами в production — це використання нуля, щоб уникнути зобов’язання.
  • Повідомлення про депрекацію без версії видалення — за ними ніхто нічого не робить до самого видалення.
  • «Виправлення помилок і покращення» як увесь запис — не-відповідь на кожне питання читача.
  • Написаний у момент релізу по пам’яті — і саме дрібна ламка зміна виявляється забутою.

Коли це застосовувати

Застосовуйте, коли

  • Інші люди чи команди залежать від вашого софту й вирішують, коли оновлюватися.
  • Ви публікуєте бібліотеку, API або будь-що версійоване.
  • Підтримці треба знати, до якої версії належить повідомлена проблема.
  • Користувачі встановлюють оновлення самі, а не отримують їх непомітно.

Уникайте, коли

  • Внутрішній сервіс з одним споживачем, що сидить поруч, — повідомлення дешевше.
  • Вебзастосунок із безперервним деплоєм, де користувач ніколи не обирає версію.
  • Він генерувався б і ніколи не читався — це супровід без читача.
  • Реліз-ноти для кінцевих користувачів це вже покривають — не ведіть два документи, що дублюються.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.