← writing

Boring tech wins again in 2026

My current production stack is Postgres, Go, server-rendered HTML, a sprinkle of HTMX, and one AI call when something genuinely needs it. It is not exciting on Twitter. It is a delight to maintain. Here is why boring keeps winning in 2026.

My current production stack is Postgres, Go, server-rendered HTML, a sprinkle of HTMX, and one AI call when something genuinely needs it. That is the whole list. It is not exciting on Twitter. It does not photograph well in an architecture diagram. It is a complete delight to maintain, and three years from now it is still going to run.

I keep waiting to feel bad about this. The industry keeps waiting for me to feel bad about this. Neither of us is making progress.

So here is the friendly defense of the boring stack in 2026, written from inside a codebase that ships features faster than anything I have built in the last decade.

Postgres, the one database for ten use cases

Postgres is the part of the stack I would defend the hardest, because every year it quietly absorbs another tool from my dependency list.

JSONB ate my document store. Full-text search ate my Elasticsearch ambitions for anything smaller than Wikipedia. pgvector ate the dedicated vector database I was about to install last year. LISTEN and NOTIFY ate a small Redis. Foreign data wrappers let me pull from another database without writing a sync job. Materialized views ate a surprising amount of caching infrastructure.

One database. Ten use cases. One backup strategy, one set of credentials, one query language, one place to look when something is slow. The operational tax of running five specialized stores is enormous, and most teams underestimate it until they are paying it. Postgres lets you postpone that bill until you genuinely need the specialization, which for most apps is never.

Go, the language that will still compile in 2029

Go gets called boring like it is an insult. It is the highest compliment I can pay a language.

Single binary deploys. The whole app ships as one file. No container is technically required, though I use one anyway. No runtime to install on the server. No version manager dance. Copy the binary, run it, done.

Compile times that feel like an interpreter. I change a line, I rebuild, I am running again before my brain has finished the thought. That tight feedback loop changes how you work.

No node_modules. I will say this again because it deserves emphasis. No node_modules. My entire dependency tree is in go.sum and it is small enough that I have actually read it.

And the part that matters most for a long-running business. Three years from now, this code will still compile and run on whatever Go is current. The language designers have an almost religious commitment to backward compatibility, and it shows. I have Go code from 2017 that builds clean against the latest toolchain. Try that with most JavaScript frameworks.

Server-rendered HTML, the original good idea

Server-rendered HTML is having a quiet revival, and I think the reason is that a generation of developers finally ran into the wall the SPA model builds for you.

There is no hydration drama. The page that arrives is the page. No mismatch warnings in the console. No second render that shifts your layout. No suspicious flash of content that is not interactive yet.

There is no client and server state split. The state lives on the server, where the database is. The browser displays it. When something changes, the server sends new HTML. The whole category of bugs that comes from two copies of your state disagreeing simply does not exist in this architecture.

Performance is free. Not cheap. Free. The browser is rendering HTML, which is the thing browsers were built to do, faster than any framework can do it. The first paint is the only paint. You did not have to optimize anything.

HTMX, or just nothing, for interactivity

Here is the take that gets me in the most trouble. 90 percent of the times a team reaches for React, what they actually needed was a partial page reload.

HTMX is the boring answer. You annotate a button with where to send the request and where to put the response. The server returns a fragment of HTML. HTMX swaps it in. That covers an astonishing percentage of what real apps need. Filtering a list. Adding a row to a table. Submitting a form without a full reload. Toggling a setting. None of these needed a single-page app. They needed a partial reload, and we collectively built an industry to avoid admitting that.

The honest version is that a lot of pages do not even need HTMX. A plain form post and a redirect is fine. Browsers are good at this. They have had a decade of work poured into making it smooth.

AI, used sparingly and on purpose

I am not anti-AI. I work with these tools every day. But I have stopped sprinkling LLM calls through code paths that do not need them.

Most application code is deterministic logic over structured data. Validating an order. Calculating a total. Sending an email. Updating a record. None of that benefits from a model in the loop. It just gets slower, more expensive, and less predictable.

The places I do reach for AI are the ones where the task is genuinely fuzzy. Classifying a free-text support ticket. Drafting a first version of something a human will edit. Pulling structured data out of an unstructured blob. Those are real wins, and the model earns its place in the stack. Everywhere else, a switch statement is faster, cheaper, and easier to debug at 2am.

The honest pushback

This is not right for every app. SPAs exist for a reason. If you are building Figma, you need a SPA. If you are building a real-time collaborative editor, server-rendered HTML is not going to cut it. If your interaction model genuinely lives in the browser, build it in the browser. There is no shame in that.

The shift I am noticing is in the default. For a long time the default was a SPA with a separate backend, and you justified going simpler. The default is sliding back the other way. You start with server-rendered HTML and you justify reaching for more complexity. That feels right to me. Complexity should be earned, not assumed.

Bet on boring

The boring stack is faster to build, because there are fewer moving parts. It is cheaper to run, because each piece does more. It is easier to hand off, because the next developer has heard of all of it. It is more pleasant to maintain, because nothing in it is breaking weekly to chase a trend.

I am not telling you to throw out your framework. I am telling you that if you are about to start something new in 2026, the boring choice is probably the right one, and you will be writing the same post I am writing now in three years.

Bet on boring. It keeps paying.

Want more like this?

Occasional, opinionated, no listicles.
all writing →