Day#4: ๐ Releasing the First Version โ Lessons in Developer-Facing Documentation
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.