Building Your Own Content Management System
By Sam Reynoso
A technical write-up of why I decided to make my own content management system for my personal website.
Prompt Command A function assigned to run just before the prompt is displayed in the terminal. I use one that adds the current Git branch to my prompt in a deep purple that looks great with the yellow cursor, the red arrow I use as the prompt symbol, and green terminal text.
What is a CMS even for?
Before we get into why I built my own content management system (CMS), we have to establish why you’d want one in the first place. A CMS is a tool that allows you to create, manage, and modify content on a website without needing specialized technical knowledge.
I don’t know much more about them than that. I can write HTML and have the technical knowledge to build and maintain a static website. If you’re someone who doesn't finds it enjoyable to SSH into a server to make live edits, then should use a CMS.
But wait — there’s more!
For a simple site, it's completely manageable to manually edit a few pages. I find it fun. What I don’t find fun is jumping around to change the same thing in multiple places — which is exactly what I’d be doing if I wanted SEO, rich social preview meta tags, or JSON-LD.
If you want multiple copies of the same content on different domains, thats definitely an "I ain’t doing that" situation — and a use case for a CMS.

My Requirements
I wanted the ability to fetch content, head tags, sitemap.txt, and robots.txt from a single source.
I wanted a TurboTax-like experience for adding titles, descriptions, etc. I wanted visibility on missing fields, and a publishing workflow that includes drafts and revisions.
Above all else, I wanted a last_modified
field that updates automatically. This one is personal: if
there is a last_modified
field present, I want it to be accurate, but I will remember to update it
manually approximately never.

The Game Plan
The core system is built on top of Git. Initializing the CMS creates a parent directory named
canonical
with two subdirectories: the publish
directory (the main repository), and a
working
directory (a worktree of publish).
This allows me to run the CMS in two modes. In the first mode, I can run the CMS in server mode, which serves files and metadata from the publish directory. In the second mode, I can run it in edit mode, which exposes the web UI for editing content in the working directory.
An Opinionated Git Wrapper
Then I went off script and made a few very un-Git-like decisions.
I built a uni-directional merge workflow, where a new project creates a branch off the empty working branch. After the files are added and ready to publish, the user can merge the branch’s files into the publish tree. This means that each project branch only contains files for that project, and the working directory only has one project at a time.
This acts like a lock that makes it impossible to accidentally publish changes to two projects at the same time.

Wait, what does it do? I still don't get it.
The publish directory contains the main repository, always has the publish branch checked out, and contains all the directories and files served by the API. Each project branch starts empty and only files for that project will be created. New projects are branched off the initial empty commit which is still the head of the working branch.
If you’re familiar with Git, this might seem odd. If you're not don't worry about it. It feel just like you’re copying the files, editing them, and then moving them in to publish — which is a totally valid way to manage static textual content that’s independent of each other.
Does it work?
Yes. I navigate to the working directory and only see the one project. I have that prompt command, so the current branch is always visible, so I appreciate that the naming convention is consistent and descriptive.
Other benefits: I get the simplicity of cp
and mv
, and I get Git. I've added rendered
diffs that inline the changes into a preview of the page. Deployment (I mean publishing) is just a merge. And I get
versioning and rollbacks for free.
Conclusion
This system serves my needs, and with a few tweaks the basic workflow could scale to hundreds of documents — even for non-technical users.
I’m using the system to manage this site, and it’s not too bad. I may choose to smooth out some rough edges or add more features, but for now it works well enough.
This project was so much fun to build. I think that’s because I knew nothing about the domain going in. Learning and trying to think through problems I’d never encountered before scratched an itch — and reminded me why I got into programming in the first place.