After a week or so of bootstrapping a modular backend platform using NestJS, TypeORM, and Turborepo, I finally reached a point where I could tag and release the very first version of one of the internal APIs.
It wasn’t just a matter of bumping a version and pushing a commit. I wanted this release to be documented, understandable, and reproducible — for myself and for anyone who will interact with the codebase in the future.
Foundations: Why a Manual RELEASES.md?
I decided early on not to rely solely on autogenerated changelogs like CHANGELOG.md. While useful, they’re geared toward machines and don’t give the clarity or narrative I want for human readers.
Enter RELEASES.md: a structured, manually curated changelog that provides context, highlights technical decisions, and communicates meaningful updates.
This post documents how I structured the release, how I generate and maintain the changelog, and how I integrated it into the workflow alongside changesets.
Setup: Changesets + Manual Releases
The project uses @changesets/cli for versioning packages within a monorepo.
Steps to version a package:
npx changeset # creates a new changeset file
npx changeset version # applies version bump & updates CHANGELOG.md
git commit -am "chore(release): bump <package> to x.y.z"
Once the above is done, I generate a manual release note and append it to RELEASES.md.
Example Entry in RELEASES.md
## 📦 @my-org/example-api — version 0.1.0
### ✨ New Features
- Implemented Address module with:
- Entity + Swagger decorators
- Full CRUD controller
- DTO validation
- Service layer with TypeORM
- NestJS module integration
### 🧪 Technical Highlights
- Endpoints fully documented using `@nestjs/swagger`
- Route parameter validation via `ParseUUIDPipe`
- DTOs are Swagger-integrated
- Ready for composition in higher-level business flows
---
**Commit:** `chore(release): bump @my-org/example-api to 0.1.0`
**Tag:** `example-api-v0.1.0`
**Released:** 📅 2025-06-09
Automating the Format (Optional)
I created a reusable template for RELEASES.md entries:
## 📦 <package-name> — version <version>
### ✨ New Features
- ...
### 🧪 Technical Highlights
- ...
---
**Commit:** `<commit-message>`
**Tag:** `<tag>`
**Released:** 📅 <date>
Stored as:
./RELEASE_TEMPLATE.md
This template can be copied manually or parsed by a custom release script in Node or Bash if automation is needed in the future.
Commit Hooks for Consistency
To ensure every change is well-formatted and won’t break style guides, I added a pre-commit hook:
npm install --save-dev husky lint-staged
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
And in package.json:
"lint-staged": {
"**/*.{ts,js,json,md}": "prettier --write"
}
Summary
Setting up a clean release process takes a bit of effort, but the result is rewarding:
- Reproducible changelogs
- Structured documentation for each release
- Versioned API modules ready for future integration
- A base for automated changelog publishing if needed
This workflow strikes a healthy balance between automation and clarity — machines help, but humans read.

Dodaj komentarz