Technical Debt, On Purpose
A late-night note about why this site runs on plain MDX files, what that is going to cost me, and the small CMS I think I am going to build to pay it back.
It’s late and I’m doing the thing where I plan instead of write. The site has been in its current shape for about a week, there are a couple of posts on it, and I’m already sketching a database schema. I know how that sounds. Hear me out anyway. Mostly so I can hear myself.
What I mean by debt
Every time I say “technical debt” at work, someone hears “bad code.” That’s not it. Debt is a trade. You take the cheaper design on purpose because you want to learn faster, and you accept that you’ll pay interest later in coordination, complexity, and data you can’t bend. It turns toxic when you can’t change anything without a risky manual step, or you can’t explain the system to someone in a day.
The fix isn’t “no debt.” It’s debt you chose with your eyes open: a shortcut with a timer on it, a note about what “paid off” looks like, and the discipline to retire it when the interest gets loud.
The debt I’m carrying right now
This site is a folder of MDX files. Frontmatter at the top, prose underneath, Git for history. I love it. It’s portable, it’s reviewable, and I can edit it from any machine with a text editor.
I can also see exactly where it’s going to bite.
When there are more posts and projects, I’ll want filters, counts, and search, and crawling a folder at build time will start to feel silly. I’ll want drafts I can leave half-finished without them sitting in a branch, and some record of what changed and when. And honestly, mostly, I want to edit on the go, with a preview, without opening a terminal.
So what I’m sketching is a small CMS. Keep MDX for the body, because I’m not giving up portability. Move the metadata and the workflow into something with an admin.
The plan I keep rewriting
Keep MDX as the body. Put the metadata, meaning title, slug, category, tags, status, image, in a relational store so I can query it. Build a focused editor that previews with the exact same component map the site uses, so there are no surprises between the editor and production. Add real auth. Add a draft, review, publish flow with revisions. Serve it with RSC, cache public reads at the edge, invalidate on publish.
I’ve written some version of that list four times this week. Each time it gets shorter, which I’m choosing to take as a good sign.
The part I actually care about: not getting owned
If I put an editor behind a login on my own domain, the login is the whole game. Here’s what I keep coming back to.
Sessions, not long-lived tokens stuffed in the browser. A short random secret server-side, an HTTP-only cookie, done.
Passkeys first. WebAuthn tied to the device, with Face ID or Windows Hello. Email magic link and TOTP as fallbacks, both rate-limited.
Step-up before anything scary. Re-confirm with the passkey before publishing, deleting, or changing roles.
CSRF and origin checks on every write. A real CSP around the preview. Lock the MDX component map so the editor can’t smuggle in a script tag.
Audit everything. Who did what, to which record, when.
Tooling-wise I’m leaning toward Auth.js or Lucia for sessions, the SimpleWebAuthn packages for passkeys, and Zod on every input. None of this is exotic. It’s just the stuff I’d be embarrassed to skip.
How I’d move without breaking anything
Parse the existing MDX, pull the frontmatter out, seed the database. Render old and new side by side and diff a sample. Run both loaders behind a flag for one release. Then cut over and delete the file loader. Keep daily snapshots, because I don’t trust myself.
The debt I’m keeping on purpose
MDX stays the body, even with a database. The frontmatter stays mirrored in the files so local rendering still works. And there’s always a plain export, JSON plus MDX, so I can walk away from the whole thing any time I want.
What I’m watching for
Is the editor actually faster than opening the file? Is publishing seconds, not minutes? Does the security hold up to my own paranoia? Is the filter UX worth the query layer under it?
If the answer to the first one is no, this whole post is just an expensive way to procrastinate. I’ll report back either way.