This directory used to hold a different project entirely: a multi-tenant BI platform with row-level security, SQL Server data modeling, a dashboard compatibility-check/deploy pipeline, Postgres, Docker — the works. I scrapped it. Everything you're reading right now is a static Next.js site with zero backend, zero database, and zero external services.
That wasn't a shortcut. It was the correct architecture once I asked what this site actually needs to do.
The tell was the requirements list
The old platform existed because it had real constraints: analysts I didn't control, uploading dashboards I hadn't reviewed, into an environment other people depended on. That combination — untrusted input, shared infrastructure, multiple tenants — is exactly when you need RBAC, a sandboxed upload pipeline, and a database to enforce access boundaries at query time.
None of that describes a personal portfolio. There's one author. There's no upload form. The "data" behind each dashboard on this site is an Excel export I already trust, because I made it. Every piece of content — resume, projects, dashboard datasets, now these blog posts — goes through one path: I edit a file, commit it, push it. There is no second path where content enters the site without going through git first.
Once that's true, a database stops being infrastructure and starts being overhead. It's a service to provision, a connection string to secure, a migration history to keep straight, a thing that can be down when the rest of the site isn't. All to store data that changes maybe once a week and fits comfortably in a TypeScript file.
What "static" actually buys
- No staging environment, because there's no environment-specific state to keep in sync. A
push to
mainis live. If I want to preview a change before merging, that's a Vercel branch preview, not a feature I had to build. - No auth, because there's nothing behind a login. Anyone can see everything, because everything here is meant to be seen.
- Every dashboard is real, reviewable code. Each one lives at its own route, with its data as
a co-located file I wrote and can diff. Nothing is a generically-rendered blob of HTML I have to
trust at runtime — if it's wrong,
git blametells me exactly which commit did it. - Deploys can't drift. There's no "the database migration didn't run in prod" class of bug, because there's no database to migrate.
The honest tradeoff
This only works because the assumptions hold: one owner, no live writes, no untrusted input. The moment any of those stop being true — say, I want visitors to leave comments, or a second person starts publishing dashboards through this site — the calculus changes, and some of what I ripped out would come back, deliberately, for a reason I could point to.
Until then, the absence of a database isn't a limitation I'm working around. It's the simplest architecture that's actually true to how this site is used.