This post is about the journal itself.

I wanted a place to document engineering work — not a blog in the lifestyle sense, but something closer to a technical notebook. A place to record what I was actually trying to do, why things broke, and what eventually worked. The kind of detail that evaporates from memory within a few weeks.

Rather than reaching for a managed CMS or a hosted platform, I decided to build it myself. That decision created its own interesting engineering problem.


What I wanted

Before writing a line of code, I had a few non-negotiable requirements:

  • Markdown-first. Every post should be a plain text file. No proprietary format, no database lock-in.
  • Obsidian-compatible. Obsidian is already where I take engineering notes. The journal should live close to that workflow, not fight it.
  • Git-native publishing. git push should trigger a deploy. No separate CMS publishing step.
  • Static output. No server to maintain, no runtime to worry about. HTML files that can be hosted anywhere.
  • Free hosting. GitHub Pages exists. There is no reason to pay for hosting a static journal.
  • No database. The files are the database.
  • Reproducible builds. Anyone (or any CI runner) should be able to clone the repository and build an identical site.
  • Custom domain. The site will live at https://hgfs.dev.

“The best infrastructure is the infrastructure you never have to think about.”

That principle shaped every decision here.


The architecture

The full pipeline looks like this:

Obsidian

Markdown files in src/content/posts/

Git commit

GitHub (engineering-journal repository)

GitHub Actions (CI/CD)

Astro build

GitHub Pages

hgfs.dev

Each layer has a clear, narrow responsibility.

Component Responsibility Why
Obsidian Authoring Markdown-first notes environment
Git Version control History and publishing trigger
GitHub Repository Source of truth
Astro Static site generation Fast, simple, TypeScript-native
GitHub Actions CI/CD Automated builds on push
GitHub Pages Hosting Free static hosting
hgfs.dev Public identity Clean professional URL

Why Astro

Astro is a static site generator that understands content collections natively. Posts live in src/content/posts/ as Markdown files. Astro processes them, applies layouts, renders syntax highlighting via Shiki, and emits pure HTML.

There is no JavaScript framework involved — no React, no Vue. The output is clean HTML and CSS. That matches the goal: something that loads fast and never breaks in unexpected ways.

Astro’s content collection API makes it straightforward to list posts, sort by date, and generate dynamic routes. A single [...slug].astro file handles every post.

Why GitHub Pages

GitHub Pages is the obvious choice for a static journal:

  • Free
  • Reliable
  • Already where the code lives
  • Works with custom domains
  • Integrates cleanly with GitHub Actions

No account at a separate hosting provider. No billing configuration. No servers.

Why Obsidian

Obsidian is where I already think. It has a clean Markdown editor, backlinks, local-first storage, and a plugin ecosystem. The goal is for the journal’s src/content/posts/ directory to eventually sit inside the Obsidian vault, so writing and publishing share the same environment.

The publishing boundary is deliberate: not every note gets published. Only posts that are finalized and committed to the repository become public.


How the content system works

Posts are Markdown files with a frontmatter header:

---
title: "Post title"
description: "Short description shown in the post list"
pubDate: 2026-07-29
tags:
  - astro
  - meta
---

The content collection is defined in src/content.config.ts. Astro’s glob() loader picks up every .md file in src/content/posts/ automatically.

Dynamic routing is handled by src/pages/posts/[...slug].astro. When Astro builds, it calls getCollection('posts') to enumerate all posts, then generates a static HTML page for each one.

A typical publishing workflow will look like:

  1. Write a post in Obsidian.
  2. Save the file to src/content/posts/.
  3. Check the output locally with npm run dev.
  4. Stage and commit.
  5. Push to GitHub.
  6. GitHub Actions builds and deploys automatically.
npm run dev
# verify locally

git add .
git commit -m "Publish: Building My Engineering Journal"
git push

That’s the entire publishing operation. No CMS dashboard. No deploy button. No separate upload step.


AI in the development workflow

This project is being built with AI assistance, and it is worth being specific about what that means in practice.

Claude (this tool) operates as the primary coding agent. It can read files, write files, run shell commands, and verify that changes compile. It has full access to the repository during a session and is responsible for implementing concrete changes.

GPT operates as the architectural and design counterpart — planning sessions, reviewing decisions, helping reason about the system before anything is written.

I remain responsible for:

  • Deciding what gets built
  • Reviewing every change before accepting it
  • Testing the actual output in a browser
  • Deciding what gets committed and published
  • Verifying that AI-generated code actually does what it claims

That last point matters more than it sounds. AI-generated changes require inspection. It is entirely possible for a code agent to confidently produce something that compiles, passes a quick test, and still does the wrong thing. The human in the loop is not optional.

The AI tooling improves the rate at which good ideas get implemented. It does not remove the need for engineering judgment.


What is already working

  • Astro project with TypeScript
  • Content collection (src/content/posts/)
  • Markdown posts with frontmatter
  • Dynamic post routing via [...slug].astro
  • Homepage with post listing
  • Base layout
  • IBM-inspired visual identity
  • Custom Shiki syntax highlighting theme
  • Theme-driven CSS variables
  • Global CSS architecture

What remains

  • GitHub Actions deploy workflow
  • GitHub Pages configuration
  • Custom domain (hgfs.dev)
  • About page
  • Tag filtering
  • RSS feed
  • SEO metadata
  • OpenGraph / social preview images
  • Sitemap
  • Obsidian vault integration

Lessons so far

Static is underrated

Every time I have worked on a project with a managed backend, the backend eventually becomes a maintenance burden. Static sites have none of that surface area. The tradeoff is losing dynamic features — but a personal engineering journal does not need dynamic features.

Tooling should earn its place

The temptation in any new project is to add tools because they are interesting or because they are commonly used. I have deliberately resisted adding:

  • A CSS framework (not needed)
  • A JavaScript framework (not needed)
  • A CMS (not needed)
  • A database (not needed)
  • A comment system (not yet decided)

Astro, Markdown, and Git are doing the work. Everything else would be complexity without benefit.

AI collaboration requires a clear mental model

Working with AI coding agents is more productive when you have a clear mental model of what you are building before the session starts. Vague prompts produce vague results. The more precisely I can describe the constraint or the goal, the more useful the output.

The agents are good at implementation. They are not good at deciding what should be built. That judgment stays with the engineer.


Architecture diagram

(This section is a placeholder. The image below is intended to become a proper architecture diagram.)

Engineering journal architecture

Next steps

The immediate priority is getting the GitHub Actions workflow and GitHub Pages deployment working so that a git push results in a live site at hgfs.dev.

After that: the About page, then tag filtering, then RSS.

The Obsidian integration is a longer-term goal. The publishing boundary — which notes become posts, and how they get moved — is still being designed.

The journal will document that process too.