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.

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 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:
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 buildThe 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.
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.
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:
npm install --save-dev semantic-release
npx semantic-releasenpx 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.
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 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.
Beyond logs, collect metrics — numbers showing the application's condition: request counts, latency, memory usage. prom-client exposes metrics in the Prometheus format:
npm install prom-clientnpm 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.
The /health endpoint tells the orchestrator whether the application is healthy and ready to accept traffic. When stopped, the application must close connections gracefully:
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.
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.
Maintainability isn't the result of one big decision, but a collection of small habits:
.env.example.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.
Here's what to take away:
npm ci, npm test, and the build on every push./health are the three pillars of observability.SIGTERM.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!