Adding a blog

I’ve set up a place to jot down, little by little, the things I work on in day-to-day development — as a notebook. Nothing formal; partly a way to look back on my own work, written at an easy pace.

How it’s built

The homepage at https://nsys.dev prioritizes loading speed, keeping a perfect 100 on every PageSpeed Insights metric. I designed the blog on the premise that adding it would not break that.

Mobile
Desktop

Because it rides on the existing build foundation (Node + Docker), no new runtime or database was added.

Why I didn’t reach for a typical blog setup

When people say “blog,” the common picture is a CMS with a database, where you write and publish from an admin screen. That’s good at multi-author operations and dynamic features like comments and search; where it fits, it’s a very convenient system.

In short — a CMS is good at “running dynamically, with many people.” This time I prioritized “fast, simple, and writing that lasts,” so I chose a light, static setup. It’s not about better or worse, just fit.

The typical setup and this one differ in what they’re good at:

Typical CMS + DB This time (Markdown + static)
Strong at Admin UI, multi-author editing, comments and search Speed, simplicity, long-term preservation of posts
Delivery Generated on each view (dynamic) Generated at build time (static)
Suits Many editors, dynamic features needed Solo writing, a speed-first notebook

“Posts stay intact” really is true: even when I revised the publishing mechanism on the homepage partway through, I didn’t have to touch the posts themselves. It’s not that a CMS is bad — it’s just that for this small notebook, this approach sat more naturally.

Tech stack

The main technologies behind the blog are below. Nothing special; I chose a setup that rides directly on the existing site’s foundation.

Role Technology Notes
Delivery server Go (standard library net/http) Serves static files. gzip and caching. No framework
Build Node.js Handles the pipeline that turns posts into static HTML
Markdown rendering markdown-it Renders post-body Markdown to HTML
Metadata parsing gray-matter Reads the front matter at the top of each post
Code highlighting highlight.js Colored at build time (no client JS)
Diagrams Mermaid CLI Diagrams rendered to SVG at build time (no client JS)
Minification html-minifier-terser / Lightning CSS / esbuild Smaller payloads for faster display
Containerization Docker (multi-stage) The runtime image is minimal, built FROM scratch
CI/CD GitHub Actions Auto build & deploy on push to main

The key point is that coloring code and drawing diagrams are both done “at build time.” Because no extra JavaScript runs at view time, I can broaden what I express without slowing the page down.

How a post is written

You just drop a Markdown file into src/blog/posts/. At the top, you write the minimal metadata (front matter) used by the list and OGP.

---
title: Post title
date: 2026-06-09
description: A short summary used in the list and OGP
tags: [tag1, tag2]
---

After that, you just write the body in ordinary Markdown. Code blocks are colored at build time.

func handleHealthCheck(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

From writing to publishing

From writing a post to it going live looks roughly like this. Drop the Markdown and push, and the rest proceeds automatically. Updating a post doesn’t rebuild the app itself — only the content is built and delivered — so publishing is light and quick.

Flow from writing a post to it going live at nsys.dev/blog

By the way, this diagram itself was written in Mermaid and converted to SVG at build time. Because it’s embedded as an image, there’s no extra client-side JavaScript and the page speed is unchanged.

Going forward

I’d like to write mostly about technical sticking points and how I worked through them. They’re modest notes from the range of work I’ve actually touched, but I’d be glad if they offer a clue to someone who got stuck in the same place.