Someone shipped a modern, commercial web product — user accounts, Stripe payments, a search dashboard, transactional emails — written entirely in C, served over CGI, the technology we all quietly agreed died somewhere around 2004.
Your first instinct is probably to laugh. Or to call it a stunt, a party trick, the kind of thing a certain type of developer does to prove a point on Hacker News. That was my instinct too.
Then I read why he did it, and the laugh died in my throat.
Because every single reason turned out to be a sober, direct answer to a problem I've watched get worse every year I've been doing this. Not nostalgia. Not contrarianism. A clear-eyed response to forces that are reshaping our entire industry right now — supply-chain attacks, AI-generated complexity, and the quietly rising value of software you can actually understand and trust.
The piece that sent me down this hole is Corey Prophitt's "A Modern Web App in C," about his product marketyour.games. I'd encourage you to read his original — this isn't a summary of it. What I want to do is zoom out from his specific build to the thing it's a signal of, because I think the most "insane" stack of 2026 might be pointed directly at the future, and I want to walk through why.
First, no, it's not a toy
Let's kill the easy dismissal upfront, because it's the reason most people stop reading.
This is not a hello-world. It's a real, shipping, commercial product. From the outside it's indistinguishable from anything built on a modern stack: accounts, a dashboard, folders, Stripe, Postmark. From the inside it's C, CGI, SQLite, and OpenBSD — a single statically-linked binary dropped into a folder, three libraries total, deployed by replacing the binary. A clean build takes seconds.
So we're not talking about whether you can do a weekend experiment this way. Someone is taking payments through it. The question isn't feasibility. The question is whether the rest of us have been wrong about something.
Your first objection is probably out of date
The reflexive response is "but CGI is slow — you fork a whole process on every request." And you'd have been completely right to say that. In 2004.
The thing is, that objection quietly expired and nobody re-ran the test. Modern kernels optimize process creation heavily; forking is nowhere near the cost it was when we all fled CGI. And if it ever did become a bottleneck, FastCGI is a small step away. The performance reason we abandoned this entire approach was real then and mostly isn't now — we just never went back to check, because our industry only ever moves in one direction: toward more.
That's the first crack in the certainty. A lot of what we treat as settled fact is really just un-re-examined reflex. "Everyone knows CGI is dead" is doing a lot of load-bearing work for something nobody's actually measured since flip phones were cool.
But performance was never the interesting part. The interesting part is why someone would choose this on purpose today — and those reasons didn't even exist the last time we had this debate.
Why it's not nostalgia: the reasons are brand new
Here's what changed my mind. The case for radical simplicity isn't "the old days were better." It's that three forces, all of them new, all of them intensifying, have quietly made the minimal stack the rational one. And they happen to be the exact forces reshaping everything else about how we build.
Every dependency became a liability
Start with the one I think about most, because I've written about its ugly edges before.
A modern web app's dependency graph is enormous. You add two or three direct dependencies and a package manager cheerfully pulls in hundreds of transitive ones — each maintained by someone you've never heard of, many unmaintained, some with publish credentials protected by a password that's been reused since 2015. That graph is not a convenience. It's your attack surface.
And that attack surface is on fire right now. Package repositories get hit weekly. Attackers register typo-and-hallucination-shaped package names and wait. Autonomous coding agents install dependencies on your behalf, at machine speed, often without a human ever reading what got pulled in. In that environment, every dependency you add is a small act of trust extended to a stranger, multiplied by hundreds, re-rolled every time you update.
Now look at the minimal stack. Its entire supply chain is a C compiler and three libraries — things like libcurl, which the entire internet watches. As Prophitt puts it, compromising it would mean compromising a project under constant global scrutiny, not "a package with eleven weekly downloads and an owner who left in 2019."
"The most secure dependency is the one you never added" used to be a cute aphorism. In 2026 it's a defensive posture. Minimalism stopped being an aesthetic and became a security strategy, and almost nobody has updated their instincts to match.
AI made complexity cheap — which made understanding rare
Here's the second force, and it's the one I find genuinely beautiful.
For the entire history of software, producing code was the expensive, scarce, valuable thing. So we optimized relentlessly for production: frameworks, generators, abstractions, anything that let us make more software faster. Complexity was an acceptable price for output, because output was hard.
AI inverted that overnight. Producing code — even producing abstraction — is now nearly free. You can generate a thousand lines, or a whole framework's worth of scaffolding, in seconds.
And when production becomes free, the bottleneck moves. It moves to understanding and trusting what you now have. The scarce resource isn't the code anymore; it's a human being who can actually hold the system in their head, reason about it, and be confident it does what they think it does. In a world drowning in generated code, a system small enough to comprehend is worth more than it has been in twenty years — because comprehensibility is exactly the thing the flood destroys.
This is why "the whole app fits in your head" stopped being a humblebrag and became a real advantage. A system you can open file by file and fully understand is a system you can verify. And verification, not production, is the hard part now. The minimal stack isn't behind the AI curve. It's weirdly, accidentally, perfectly adapted to it.
The complexity tax finally came due
The third force is just arithmetic catching up with us.
We spent fifteen years adding layers to solve the problems created by the previous layers. The modern default is genuinely strange when you say it plainly: you run a web server (nginx) in front of a second web server (your app server in Ruby, Python, Go), which also happens to hold your business logic. Then you add a container to run it, an orchestrator to run the containers, a build pipeline to produce the containers, and a language server just to help you navigate the abstraction you've accumulated. Each layer solves a real problem — usually a problem the previous layer introduced.
At some point the cure becomes the disease. The minimal stack isn't missing all of that machinery. It never needed it, because it never created the problems the machinery exists to solve. One web server that calls your logic directly. One file to deploy. One language for your editor to understand. The complexity didn't get managed better — it got refused.
What you actually get
Enough philosophy — here's the concrete payoff for a developer weighing whether any of this is more than a thought experiment.
It fits in your head. You can reason about the whole thing, debug it with confidence, and trust it, because there's no layer you haven't seen. Open a file, read it, know what it does. That sounds small until you remember the last time you could say it about anything you shipped.
Less operational tax. No database server to babysit and upgrade. No dependency churn. No upgrade treadmill where half your week goes to keeping the scaffolding standing. Deploying is replacing one binary. The time you don't spend maintaining complexity is time you spend building.
Durable knowledge. HTTP, SQL, processes, the fundamentals — learn them once and they're good for your whole career. Compare that to the framework-of-the-year churn, where the thing you mastered last cycle is a migration guide this cycle. On the minimal stack, what you learn compounds instead of evaporating.
Trivial debugging. Your application is just a program. Pipe it a request, watch what it writes, step through it with a debugger. There's no distributed-systems detective work to figure out which of your seven services ate the request.
The honest counter (because this isn't a cult)
I'd be doing the exact thing I dislike if I only sold you the upside, so here's the fair case against.
You do rebuild things frameworks hand you for free — routing, request parsing, validation, output escaping. That's real upfront work, and real surface area for your own bugs instead of someone else's. It genuinely doesn't fit every problem: large teams moving fast on a shared codebase, deeply interactive rich frontends, extreme scale, or rapid prototyping where ecosystem velocity is the whole point — those are cases where the "bloated" modern stack is earning its keep, not just accumulating.
And C specifically carries a real cost I don't want to wave away: memory safety. A buffer overflow is a worse failure mode than a slow framework. Which is exactly why I'd argue the language is the least important part of this. The thing worth taking seriously is the broader move — radical simplicity, minimal dependencies, a system you can hold whole — and you can reach for most of it in a single-binary Go service, or in Rust, without writing a line of C. The point was never "write C." The point is "stop reaching for maximum complexity by default."
So the honest claim is narrow and, I think, hard to argue with: the pendulum swung much too far toward complexity, most of the old reasons for that swing have quietly expired, and genuinely new forces have made the minimal path far more worth considering than our reflexes allow. Not mandatory. Just no longer insane.
The actual horizon
Here's why I don't think this is going backward, even though it looks like it.
Going backward would be doing the old thing for old reasons — forking processes in 2026 because that's how we did it in 1994, out of habit or sentiment. That's not what's happening. What's happening is a new synthesis: proven, boring, decades-hardened foundations, picked up by people with modern knowledge, for reasons that did not exist when we abandoned them. Supply-chain attacks at this scale are new. AI making production free is new. The premium on verifiable, comprehensible systems is new. The old stack isn't being revived out of nostalgia — it's being rediscovered because it happens to answer questions we only started asking this year.
That's not a retreat. That's the ground moving under you, and realizing the thing you walked away from is now standing on higher ground than where you've been.
The exciting part isn't C, and it isn't CGI. It's the quiet, liberating realization that "modern" was never actually a synonym for "more" — that some of the most resilient, trustworthy, genuinely interesting software of the next few years might come from the people brave enough to subtract. To build something small enough to understand on purpose. To treat every dependency, every layer, every clever abstraction as a cost to be justified rather than a feature to be collected.
Maybe build one small thing you can hold entirely in your head, just to feel what that's like again. I suspect a lot of us have forgotten. And I suspect that feeling — not the next framework — is where the interesting part of the future actually is.
Two honest questions, and I want the real answers. What's the most "outdated" technology you quietly believe is actually better than its shiny replacement for some real use case — and have you been a little embarrassed to admit it? And the harder one: in an age of supply-chain attacks and infinite AI-generated code, is radical simplicity a genuine engineering strategy now, or am I romanticizing a stack because it's elegant? I honestly go back and forth, and I'd rather be argued with than agreed with.
Top comments (8)
I am resonating with your post. Even I know the ultimate dependency limitation is when we don't using computer. I have practical advice in that question too:

That why I buy this MazdaB2200/1993 pickup, no computer, no overengineered technical solution, even no 4x4. Just a pure functionality, a perfekt workhore for everything.
But back to the coding I think some of my previous post was step on this road:
JSDoc Evangelism - I am arguing why we don't need TypeScript for safety
Javascript great again! - JS game with reactive "framework" ( 3 simple function : proxy ) + CSS based 3D
Rustroke - WASM mini draw - Competition winner vector fill method written in Rust with zero dependency.
JS/TS/TSX terminal with |> - helpfull terminal for coding practice.
Your suggestion is in my head, so I will make a markdown first easy than vim terminal based editor powered by C! Without C dependency! We will see what will happen.
Ha, the 1993 Mazda B2200 is the perfect physical version of the whole argument — no computer, no overengineering, no 4x4 it doesn’t need, just pure functionality that fits in your head and keeps running because there’s barely anything to break. That truck is the minimal-stack philosophy with a tailgate. The best workhorses, in trucks and in code, are the ones simple enough that you can fix them yourself.
And your posts are genuinely walking the same road — JSDoc instead of TypeScript (type safety without the build step and the dependency), a reactive “framework” that’s three functions and a Proxy, zero-dependency Rust/WASM. There’s a clear through-line: get the benefit, refuse the ceremony. That’s exactly the “every layer is a cost to justify, not a feature to collect” instinct, applied for real rather than just admired.
And now a C-powered, Vim-style terminal editor with no C dependencies — starting with markdown because it’s the easier on-ramp before the full editor? That’s a great way to do it, and honestly I’d read the writeup of that build more than almost anything on my feed right now. Build the small thing you can hold entirely in your head, see what it teaches you. Please post it when it exists — and keep the Mazda running. 🙂
When Neovim started trending, I honestly hadn’t even tried Vim. And every tutorial said to start with Vim, so I decided to give Vim a try instead.
Turns out, the latest version of Vim is much closer to a basic code editor than I expected. You get syntax highlighting, the ability to open and edit files, and even a terminal that you can launch from inside Vim!
That’s a great example of the exact thing the terminal/fundamentals conversation keeps circling — reaching for the newer, trendier thing (Neovim) and discovering the “old” tool it’s built on was already most of what you needed. Plain Vim being closer to a real editor than people assume is a nice reminder that a lot of the complexity we pile on is solving problems the base tool had quietly already handled — syntax highlighting, a built-in terminal, split windows, all there without a single plugin. And starting with the thing the newer tool is based on is underrated advice generally, because you learn what’s fundamental versus what’s a convenience layer on top — which means when something breaks, you actually know which layer to look at. Enjoy the Vim rabbit hole; it’s one of the good ones.
I made the same bet one layer up the stack: the browser.
My agent-browser (navette) is a ~660 KB Rust binary driving the WebView your OS already ships — no Chromium download, no 200 MB of Puppeteer luggage per CI run. Same math as Prophitt's C app, same three forces you name, compounding one layer higher: agents installing npm packages at machine speed, AI-generated test code nobody can hold in their head, and a complexity tax measured in gigabytes.
The part nobody believes until they measure it: opening a page in the OS WebView costs ~8 ms warm, and full rendering comes free because the engine shipped with the OS. The "insane" option turns out to be the one downloading 218 MB.
One force I'd add to your list, though — and it's the one your stack answered with C and mine answered with Rust: memory safety. Radical simplicity cuts the dependency graph to zero; it doesn't cut the CVE classes in the language you kept. OpenBSD's mitigations are doing real work in that deployment — worth a paragraph in the next revision.
The comprehensibility point is the one I'll steal, though. In a flood of generated code, "I can read every file" is the new moat. Verified on my own stack: the whole thing fits in your head too.
Navette is the same bet one layer up, and the numbers are the argument: a 660 KB Rust binary driving the OS WebView instead of 218 MB of downloaded Chromium per CI run. Same three forces, compounding higher — agents installing packages at machine speed, AI-generated test code nobody can hold in their head, a complexity tax now measured in gigabytes. And the ~8ms warm open with rendering free because the engine shipped with the OS is exactly the “nobody believes it until they measure it” detail that makes the whole case land — the “insane” option was the one downloading a quarter-gig.
Your memory-safety addition is the right correction, and sharper than my footnote: radical simplicity cuts the dependency graph to zero, but it doesn’t cut the CVE classes in the language you kept — which is the whole reason the language choice (your Rust, Prophitt’s C-plus-OpenBSD-mitigations) is load-bearing, not incidental. Zero dependencies and a memory-unsafe core is a smaller attack surface with the same sharp edges. That’s a paragraph in the revision, credited. And “I can read every file is the new moat,” verified on your own stack, is the line I’ll be stealing back.
Take the paragraph 😉 — the revision reads better with the correction inside it than beside it.
The symmetry between the two bets is my favorite part: yours deletes the runtime, writing to the web's metal; mine deletes the bundle, driving the engine the OS already ships. Same instinct from two directions — remove the layer that grew past what anyone can hold. And your CVE-class point is the honest half of the C case: zero dependencies doesn't remove the language's sharp edges, it makes them countable — and countable is exactly what an audit needs. The moat line comes with the same duty on my side: "I can read every file" is only a moat while the reading actually happens, which is why navette ships with a bench anyone can rerun. Curious how the mitigation paragraph lands in the revision — OpenBSD's hardening story plus C deserves the same receipts treatment as the dependency graph.
The supply-chain argument is the strongest here and deserves to lead. Three libraries you can name beats three hundred transitive dependencies you can't, and that's a real security property rather than an aesthetic preference.
The honest counterpoint is that the dependency count didn't go to zero, it moved. SQLite, a TLS implementation, the OS itself — you still don't audit those. What changed is the number of parties between you and them, which is the part that actually matters.
The thing that doesn't transfer is team size. One person can hold a C codebase in their head, and that's a large part of why this works. At six engineers the calculus flips — not because C got worse, but because the shared understanding did.
Worth separating too: replacing one binary is a bigger win than the throughput argument, which was always going to be fine.