Learning Node.js - CI/CD, Monitoring, and Observability
Episode 22 of 23

Learning Node.js - CI/CD, Monitoring, and Observability

This final episode locks down the production cycle: CI/CD pipelines with GitHub Actions, release automation with semantic-release, observability through structured logs, metrics, and health checks, and summarizing the material to build a Node.js application that's production-ready and maintainable.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Every skill you've learned — from the event loop in episode 1 to containers in episode 21 — finds its final destination in production. And healthy production rests on three things: changes are shipped automatically and safely, the application can be observed while running, and the code stays easy to maintain over time.

Episode 22 closes this series with the last three layers: CI/CD with GitHub Actions, observability through logs, metrics, and health checks, and a summary of building a production-ready, maintainable application. This is the final episode — you'll see the whole series come together.

CI/CD with GitHub Actions

Automated Build and Test Pipelines

CI/CD automates the flow from code push to deployment. Every change on GitHub triggers a workflow that installs dependencies, runs the tests from episode 18, and builds the application:

.github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:
 
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test
      - run: npm run build

The workflow above runs npm ci, npm test, and npm run build for every push and pull request. If a test fails, the pipeline fails and the change can't be deployed — this is the safety net that makes a team brave enough to change code.

Secrets in CI

Environment variables like JWT_SECRET and DATABASE_URL are injected into the workflow through GitHub secrets, not written in files. npm test can use DATABASE_URL from secrets when running integration tests, while NODE_ENV=test marks the testing mode.

Release Automation with Semantic-Release

Versions That Follow Commits

Semantic-release automates the entire release cycle: determining the new version, updating the changelog, creating the tag and release, then publishing the package. The version is determined from commit conventions — feat: bumps the minor, fix: bumps the patch, and a breaking change bumps the major:

Set up semantic-release
npm install --save-dev semantic-release
npx semantic-release

npx semantic-release analyzes commits since the last release, computes the next semver version, and runs plugins to create the release on GitHub. Because you've held the commit conventions since the start of this series, this automation works without intervention.

Prerelease Releases for Staging

Modern teams use prereleases for the staging branch: versions like 1.2.3-rc.1 are released from the staging branch, while main produces stable releases. This way, candidate versions can be tested on staging before being promoted to production — all automated and documented.

Observability: Logging, Metrics, and Health Checks

Structured Logs for Diagnosis

Observability answers "what's happening in my application?". The JSON logs from episode 12 are the foundation — streamed into systems like Loki or OpenSearch, logs can be queried by field, not just read line by line.

Metrics with prom-client

Beyond logs, collect metrics — numbers showing the application's condition: request counts, latency, memory usage. prom-client exposes metrics in the Prometheus format:

Install prom-client
npm install prom-client

npm install prom-client adds the metrics library. Combine it with collectDefaultMetrics to monitor event loop delay and heap usage — two indicators we discussed in episode 19.

Health Checks and Graceful Shutdown

The /health endpoint tells the orchestrator whether the application is healthy and ready to accept traffic. When stopped, the application must close connections gracefully:

JSHealth check and graceful shutdown
app.get("/health", (req, res) => {
  res.json({ status: "ok", uptime: process.uptime() });
});
 
const server = app.listen(3000);
 
process.on("SIGTERM", () => {
  console.log("Menerima SIGTERM, menutup server...");
  server.close(async () => {
    await pool.end();
    process.exit(0);
  });
});

app.get("/health", ...) reports the health status, and the SIGTERM handler closes the server, then cleans up the database connection before exiting. Docker sends SIGTERM when a container is stopped (episode 21) — without graceful shutdown, connections are cut off forcibly and data can be lost.

Building a Production-Ready, Maintainable Application

An Organized Project Structure

A maintainable application starts with a clear folder structure. Separate responsibilities: src/routes for endpoints, src/services for business logic, src/repositories for database access, and src/middleware for shared middleware. Slim controllers, testable services, and centralized data access are the signs of code your team will appreciate a year from now.

Habits That Preserve Quality

Maintainability isn't the result of one big decision, but a collection of small habits:

  • Consistent commit conventions for a readable history.
  • Tests that accompany every feature and run in CI.
  • Dependencies that are updated and audited regularly.
  • Environment variables documented in .env.example.
  • Minimal documentation for endpoints and architecture decisions.

This whole series was built to grow those habits. A production-ready application isn't one that looks perfect — it's one that can be changed safely, observed clearly, and maintained over the long term.

Closing

Here's what to take away:

  • GitHub Actions runs npm ci, npm test, and the build on every push.
  • Secrets are injected via secrets, never committed.
  • Semantic-release determines versions from commit conventions.
  • Structured logs, metrics, and /health are the three pillars of observability.
  • Graceful shutdown closes the server and database on SIGTERM.
  • A tidy folder structure and quality habits preserve maintainability.

The Learn Node.js series is complete! From the prerequisites in episode 0, event-driven architecture, modules, HTTP servers, Express, authentication, databases, testing, all the way to production — you've traveled from zero to a deployable application. Keep going by building real projects, develop the habit of measuring and testing, and keep following the latest Node.js releases. The foundation you build now will be a stepping stone for a career as a Cloud & Software Engineer. Happy building!