Keep it Simple, Stupid (KISS)
Looking back from 2026. The thesis below aged perfectly. This site is still static, still simple, still boring, still a pile of markdown I commit and push. What changed is the plumbing behind it, and that change is itself a KISS story, because the old setup violated this post’s own principle in a way I did not fully own up to at the time. The 2015 post stands as the period record. The two takes after it are the reckoning.
One of the most often-broken engineering principles is Keep It Simple Stupid (KISS). Engineers love to use cool technologies to build cool solutions that are complex, elegant, and future-proof. The problem is, of course, the actual situation frequently just doesn’t call for anything that fancy.
That is why I am writing this blog as simple, static HTML Markdown. Dynamic
blog solutions, like wordpress,
suffer from exploits and security holes. Managing them is a pain.
Other solutions like gitit
(a git-backed wiki and my wiki of choice which I use for various things) don’t
look very nice (and as a person who tells computers what to do for a living, I
had better at least have a passable web site that doesn’t look like it
transparently proxies the wayback machine). Twitter bootstrap made making a
nice looking website easy, but because everything is static, I can’t easily
display my blog entries.
Interestingly, I made a different choice with our family cookbook. For this project, it is important that non-technical users be able to edit the recipes via the web UI. It was also important to me, however, that the recipes themselves be stored in git, and that I am able to edit the markdown directly using only git. To achieve both of these ends, gitit seemed like the best option (and you can see the results here).
Some day, I may reinvestigate the best way to run this blog, but for now, let’s keep it simple. When I go to write a new blog entry, I simply spit out some markdown, commit and push. For now, this is the right balance of simplicity and features.
Take 2: still static, new plumbing (July 2026)
Well. “Some day” arrived.
Here’s the confession the 2015 post danced around: the site was static, but the way it got static was anything but simple. My blog and personal site were “compiled” by QBT - jinja2 templates, my resume, the post index, the works - and the compile ran literally on the production box. To publish, the prod server checked out source, ran a full QBT build in place, and served whatever fell out.
I love QBT. I helped build it. But using it to build a personal website on the prod host was a mad-hatter setup that broke every rule this very post preaches. The production box needed a whole toolchain installed just to render some HTML. A build failure and a deploy failure were the same event. There was no artifact I could inspect before it went live, and no way to build the site anywhere the prod toolchain didn’t exist. That is not KISS. That is a Rube Goldberg machine wearing a KISS t-shirt.
The rebuild I have in mind - the same pattern I already use for the ConsoleArtisan storefront - fixes it by doing the obvious boring thing:
- Build locally, not on prod. The site is rendered on my laptop (or any machine) into a plain directory of static files. The production server does exactly one job: serve files. It has nginx and nothing else. No build toolchain, no templating engine, no surprises.
- Versioned, user-space tooling that runs on Linux and Mac. The generator is a small, pinned, self-contained thing I can run anywhere without root and without a bespoke server environment. That “runs on Mac too” bit matters more to me than it used to - see the laptop post for why my next machine might not be a Linux box at all. If publishing my website depends on one specific server’s installed packages, I’ve built myself a cage.
- Push fast: rsync a built directory to nginx. Deploy is
rsyncof the built output to the nginx document root over SSH. That’s the whole deploy. On cmyers.org - which is an Amazon Lightsail box where host nginx terminates TLS via Let’s Encrypt - a pure static site is justroot /var/www/<site>and an rsync. No container, no CI round trip to production, no build-on-deploy. If I ever want a rollback, the previous built directory is the rollback.
The upshot is that “build” and “deploy” are now two separate, dumb steps instead of one clever tangled one. I can build the site, look at exactly what I’m about to ship, and then ship it with a command that any sysadmin from 1998 would recognize. The prod box got dumber and the whole system got more reliable, which is almost always the trade you want.
So the 2015 thesis stands, stronger than ever: keep it simple. I just had to notice that my definition of “simple” had quietly grown a build server.
Take 3: one site, one build (August 2026)
Take 2 was the plan. This is what actually shipped, a month later, and the interesting part is that it went further than the plan did.
The plan was to stop building on the prod box. Fine, done, and the two-minute cron job that used to check out source and run a QBT build in place on the production server is switched off. But once I actually looked at the whole thing, the build host was not the only place I had quietly accumulated complexity.
Here is what the site was made of before: a QBT package that ran jinja2 over some templates, a pile of hand-written Bootstrap HTML stitched together at request time by nginx server-side includes, a LaTeX resume, and a Hugo blog that was Hugo only for the blog. The blog’s posts, meanwhile, mostly lived in a completely different repository on GitHub, because at various points I had convinced myself the blog wanted to be its own thing, or maybe wanted to be on Medium. Four different mechanisms produced one small website, and the pieces were spread over two repos and one server.
Now it is one Hugo site. The pages, the blog, and the resume build together with one command and deploy with another. The blog is not a separate property anymore, it is a directory in the same repository as everything else, which is where it should have been all along. Bootstrap and jQuery are deleted, about three hundred kilobytes of framework replaced by a single stylesheet I wrote and can read in one sitting. The server-side includes are gone, so the production nginx config no longer needs to do anything clever. There is a staging site that is the same build with a different base URL.
That is the part I want to underline, because it is the actual KISS lesson and I missed it for a decade. I did not simplify this by finding a better tool. I simplified it by deleting three of them. Every one of those mechanisms was individually reasonable when I added it. QBT is a good build system. SSI is a fine way to share a nav bar. Keeping the blog separate made sense on the day I decided it. The complexity was not in any single choice, it was in the four of them coexisting, and no single decision ever felt like the one that made the system complicated.
So the 2015 thesis stands, with an amendment. Keeping it simple is not a thing you decide once. It is a thing that decays, quietly, one locally-sensible decision at a time, and every few years you have to go back and look at the whole pile rather than the piece in front of you. When I go to write a new blog entry, I still spit out some markdown, commit, and push. It just took me eleven years to make the plumbing as simple as the writing actually was.