Changelog przydaje mi się najbardziej w sytuacjach, których nikt nie planuje: gdy ruch z Google spada po aktualizacji, gdy klient pyta, od kiedy formularz działa inaczej, albo gdy trzeba ustalić, która wersja wprowadziła błąd. Wtedy dobrze prowadzona historia zmian oszczędza godziny przeglądania commitów. W tym wpisie pokazuję, jak prowadzę changelogi w projektach webowych, jak je automatyzuję i jakich błędów unikam, żeby były czytelne nie tylko dla programistów.
Dla kogo piszę changelog
Zanim ustalę format, ustalam odbiorcę, bo od niego zależy język wpisów:
- Programiści korzystający z biblioteki lub API potrzebują informacji o zmianach niezgodnych wstecz, nowych parametrach i wycofywanych funkcjach.
- Zespół i osoby utrzymujące projekt szukają, kiedy i dlaczego coś się zmieniło, najlepiej z numerem zgłoszenia.
- Klient i osoby nietechniczne chcą wiedzieć, co się zmieniło na stronie i czy mają coś zrobić. Dla nich piszę osobne, krótsze notatki do wydania, zamiast pokazywać techniczny dziennik.
W projektach stron internetowych prowadzonych dla klientów zapisuję w changelogu także zmiany, które nie są kodem, a wpływają na działanie
i widoczność strony: aktualizacje CMS i wtyczek, nowe przekierowania, zmiany w robots.txt, adresach czy danych strukturalnych.
Gdy kilka tygodni później zmienia się ruch, mogę od razu sprawdzić, co działo się na stronie w tym czasie.
Format, którego się trzymam
Korzystam z konwencji Keep a Changelog i numeracji Semantic Versioning. Plik CHANGELOG.md leży w głównym katalogu repozytorium,
najnowsze wersje są na górze, a każda wersja ma datę w formacie RRRR-MM-DD i podział na kategorie:
# Changelog
## [Unreleased]
### Added
- Filtrowanie produktów według dostępności w magazynie.
## [2.4.0] - 2026-03-16
### Changed
- Koszyk zapamiętuje produkty po wylogowaniu (#412).
### Fixed
- Formularz kontaktowy nie wysyłał wiadomości z pustym polem telefonu (#405).
### Security
- Aktualizacja biblioteki do obsługi plików, która usuwa podatność XSS.
## [2.3.1] - 2026-02-28
### Fixed
- Błędne przekierowanie starych adresów kategorii po migracji.
| Kategoria | Co do niej trafia |
|---|---|
| Added | nowe funkcje |
| Changed | zmiany działania istniejących funkcji |
| Deprecated | funkcje, które zostaną usunięte w kolejnych wersjach |
| Removed | funkcje usunięte w tej wersji |
| Fixed | naprawione błędy |
| Security | poprawki bezpieczeństwa |
Numer wersji w Semantic Versioning ma trzy części: zmiana pierwszej oznacza zmiany niezgodne wstecz, drugiej nowe funkcje, trzeciej poprawki. Przy stronach internetowych, które nie są biblioteką, ta zasada jest mniej istotna, ale i tak ją stosuję, bo od razu widać, czy wdrożenie było dużą zmianą, czy drobną poprawką.
Jak piszę pojedyncze wpisy
Każdy wpis opisuje efekt zmiany, a nie jej techniczny przebieg. „Refaktoryzacja modułu płatności” niczego nie mówi czytelnikowi, a „Strona płatności ładuje się o ok. 1 sekundę szybciej” mówi wszystko, co ważne. Szczegóły techniczne zostawiam w opisie pull requesta, do którego prowadzi numer zgłoszenia na końcu wpisu.
Zmiany niezgodne wstecz zaznaczam na początku sekcji wersji, razem z instrukcją, co trzeba zrobić, np. zmienić konfigurację albo uruchomić migrację bazy. Pominięcie takiej informacji to najkosztowniejszy błąd w changelogu, bo skutki odkrywa się dopiero po aktualizacji na produkcji.
Conventional Commits jako źródło changeloga
Changelog jest tak dobry jak historia zmian, z której powstaje. Dlatego w zespołach wprowadzam konwencję Conventional Commits, w której każdy commit zaczyna się od typu zmiany:
feat(koszyk): zapamiętywanie produktów po wylogowaniu
fix(formularz): wysyłka wiadomości bez numeru telefonu
perf(kategoria): leniwe ładowanie zdjęć poniżej pierwszego ekranu
feat(api)!: zmiana formatu odpowiedzi endpointu /orders
BREAKING CHANGE: pole "total" zawiera kwotę w groszach.
Typy feat i fix trafiają do changeloga, a chore, style czy test zwykle
nie, bo nie zmieniają niczego dla odbiorcy. Wykrzyknik lub stopka BREAKING CHANGE oznacza zmianę niezgodną wstecz. Poprawność
komunikatów sprawdza commitlint uruchamiany przed commitem. Cały ten porządek opiera się na tym, jak zespół korzysta z
Gita w zespołowym web developmencie: krótkich gałęziach i przeglądach kodu.
Automatyczne generowanie changeloga
Przy częstych wydaniach changeloga nie piszę ręcznie. Narzędzia czytają historię commitów, wyliczają kolejny numer wersji i aktualizują plik:
- semantic-release: pełna automatyzacja wydania, od numeru wersji przez changelog po publikację paczki,
- release-please: tworzy pull request z wydaniem, który zespół przegląda i zatwierdza, co daje kontrolę nad treścią,
- Changesets: wygodne w repozytoriach z wieloma paczkami, bo opis zmiany dodaje się razem z kodem,
- git-cliff: szybki generator z elastycznymi szablonami, niezależny od języka projektu.
Popularne kiedyś standard-version nie jest już rozwijane, więc w nowych projektach go nie stosuję. Generowanie uruchamiam w CI
po scaleniu zmian do głównej gałęzi, razem z pozostałymi krokami, jakie obejmuje
automatyczne sprawdzanie szybkości i jakości kodu przed wdrożeniem.
Automatycznie wygenerowany plik zawsze czytam przed publikacją i poprawiam wpisy, które bez kontekstu commita byłyby niezrozumiałe.
Błędy, których unikam
- Wklejanie
git logjako changeloga. Lista commitów jest pełna drobiazgów i skrótów zrozumiałych tylko dla autora. - Zmienne nazwy kategorii i formaty dat. Raz „Fixed”, raz „Bugfixes”, raz 16.03.2026, raz „March 16th” utrudniają czytanie i automatyczne przetwarzanie pliku.
- Wpisy bez efektu. „Poprawki” czy „Aktualizacja zależności” nie mówią, co się zmieniło dla użytkownika.
- Changelog aktualizowany raz na kilka miesięcy. Odtworzenie zmian z pamięci zawsze kończy się lukami. Sekcja
[Unreleased]na górze pliku pozwala dopisywać zmiany na bieżąco.
Dobrze prowadzony changelog to też najszybsze narzędzie przy szukaniu przyczyny regresji. Zanim zacznę debugować kod, sprawdzam, co zmieniło się w wersji, od której problem występuje, i zwykle już na tym etapie wiem, gdzie szukać.