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:

This workflow strikes a healthy balance between automation and clarity โ€” machines help, but humans read.