HomeResources › Boring on Purpose
Case Study · Practical AI

Boring on Purpose: choosing Mission Control's stack with Fit-First

The interesting decision behind my operations command center wasn't what it does — it's what I built it with. Faced with the modern default stack, I chose Python's standard library and SQLite, added AI only on top, and deliberately left a lot of technology out. Here's that decision, run through Fit-First.

Framework: Fit-First ↗ Practical AI · Technology Strategy Self-built & self-hosted

01Executive summary

Mission Control is my private, self-hosted operations command center (what it does — pulling a dozen tools into one view — is a separate case study). This one is about a different question: what to build it with. The default answer in 2026 is a framework backend, a separate database server, an ORM with migrations, and a compiled JavaScript front-end. I chose the opposite: Python standard library, SQLite, no framework, no ORM, no build step — with AI added only where it genuinely helps. Fit-First is why. And the same reasoning is why my multi-user products use the heavier stack: fit is contextual, not a slogan.

02Business context

I use a dozen tools every day — mail, calendar, tasks, finances, a few monitors. Mission Control unifies them into one place only I can see. Two facts about this business decided everything: it has exactly one user (me), and it has to run for years, largely untouched, on a modest server where I am the only person who will ever maintain it. That is a very different business than a product with paying customers, a team, and a support burden — and my Poppy product suite, which does run the conventional Next.js + framework-backend + Postgres stack, reflects that. Fit-First doesn't say "use less technology." It says "use the technology that fits this job."

03The challenge

Build an operations dashboard that one person can maintain, that survives long stretches with no attention, that deploys in seconds, and that uses AI where it actually earns its place — without the tool itself becoming a second job of dependency upgrades, database administration, and build tooling.

04Initial options considered

Three honest paths were on the table — this wasn't a foregone conclusion:

A · The modern SaaS stack

A framework backend, PostgreSQL with an ORM and migrations, a compiled Next.js front-end. The default — and what my products use.

Rejected — on fit

B · A hosted low-code dashboard

Buy, don't build — a Notion/Retool-style tool wired to my data. Fastest to start.

Rejected — on fit

C · The minimal path

Python stdlib HTTP server, vanilla-JS per-tab modules, one SQLite file per feature, run with a process manager.

Chosen

05Why the common solutions were rejected

Not out of dogma — each was rejected on fit and cost-over-time for a one-person, run-for-years tool:

06The Fit-First evaluation

I ran each layer down Fit-First's ladder — process → automation → AI, and its build-vs-buy cousin — taking the lowest rung that actually solved the job:

the modern default Framework backend PostgreSQL + ORM Migration tool Compiled JS front-end AI woven through Fit-First lowest rung that solves it the fit — what shipped stdlib http.server SQLite, one file per feature CREATE TABLE IF NOT EXISTS vanilla JS, no build AI on top, only where it helps deliberately left out web framework · ORM · database server · migration tool · build pipeline — every one is a future maintenance decision, declined
Fit-First applied per layer — the fit was consistently the lower rung, and a whole column of technology was declined on purpose.

07Decision process

For every capability I asked the same Fit-First questions: is this job real and frequent? is it rule-clear (→ plain code) or judgment / language (→ maybe AI)? would a simpler thing win? and does the heavier option genuinely improve it net of maintenance? The answer kept landing on the smaller rung — not because small is virtuous, but because the deciding lens for this business was cost-over-time for a solo maintainer, and every dependency I added would be a cost I'd pay in that column for years.

08Implementation

A single stdlib server.py plus one *_store.py module per tab, each owning a small SQLite file; a vanilla-JS front-end with one module per tab; deployed with a process manager — no container, no orchestration, no cloud services. The AI features are additive modules that sit on top of the same stores, so removing or replacing them never touches the reliable base. It was built incrementally across 255 commits, and every new feature follows the same boring pattern — which is exactly the point: it stays maintainable because it stays uniform.

09Results

It runs. One person maintains it; it survives long stretches untouched; a deploy is a file copy and a process restart; there is no dependency-upgrade treadmill and no database server to tend. AI assists where it helps, and the dashboard keeps working when the model is unavailable. I won't put a number on "hours saved" — I haven't measured that, and I won't invent it. The outcome I can speak to first-hand is the one the whole decision was optimizing for: the maintenance cost is close to zero, and the tool is still mine to change. The proof is that it's still running.

10Lessons learned

11Honest limitations

Fit-First isn't "use less technology." It's "use the technology that fits." Sometimes — like here — the fit is a lot less, on purpose.

12How this validates Fit-First

This is Fit-First end to end: real options genuinely considered, the hyped default rejected on fit rather than fashion, the smallest rung that solved each job chosen, a whole column of technology declined on purpose — and the same framework producing the opposite answer for a different business (the products). A framework that only ever says "less" is a bias; one that says "less here, more there, for these reasons" is a decision method. That's the point of it.

13Related frameworks & case studies

Weighing a build-vs-buy or "should we add AI" decision? I'll help you choose the fit — including the smaller answer, when that's the right one.
Let's decide what fits →