The return of boring web development
Why server-rendered apps, monoliths, HTML-first thinking, and simpler stacks are becoming attractive again.

A few years ago, web development felt like a race to make every app more distributed, more reactive, more bundled, more hydrated, and more complicated.
A simple dashboard could become a maze of client state, server state, API routes, GraphQL schemas, edge functions, background workers, managed queues, Redis, object storage, frontend build pipelines, hydration bugs, auth callbacks, preview deployments, and five different places where the same type had to be defined.
Some of that complexity was useful.
A lot of it was not.
Now something interesting is happening. Developers are not going back to 2008. They are not rejecting React, TypeScript, serverless, edge platforms, or modern frameworks. But many are asking a quieter question:
What if most web apps do not need this much machinery?
That question is bringing boring web development back.
Server-rendered pages. Monoliths. HTML-first thinking. Postgres. SQLite. Rails. Laravel. Django. Phoenix. HTMX. Alpine. Server components. Fewer microservices. Fewer moving parts. Fewer background services. Fewer things to monitor at 2 AM.
This is not a trend about being old fashioned.
It is a trend about being tired of accidental complexity.
Boring does not mean bad
The word boring needs defending.
In software, boring is often used as an insult. It sounds slow, stale, and unimaginative. But boring technology usually means something else.
It means the failure modes are known.
Dan McKinley's classic essay, Choose Boring Technology, made this point clearly. New technology has a cost. You need to learn it, operate it, test it, debug it, upgrade it, secure it, and teach it to the rest of the team. That cost does not disappear just because the tool is exciting.
Boring technology is not always the best technology. It is the technology whose tradeoffs are visible.
That matters because most software teams do not fail from lack of novelty. They fail from too much complexity, too little time, unclear ownership, fragile systems, and slow feedback loops.
Boring web development is not about avoiding modern tools. It is about asking every tool to justify itself.
The return of boring web development is really the return of this mindset.
Before adding a queue, ask if a database table is enough.
Before adding a separate frontend app, ask if server-rendered templates are enough.
Before adding a microservice, ask if a module inside the monolith is enough.
Before adding a new database, ask if Postgres is enough.
Often, the answer is yes.
And when the answer is yes, you should enjoy that.
The old frontend bargain got expensive
Modern frontend development solved real problems.
It made interfaces more interactive. It improved component reuse. It gave teams stronger patterns for large applications. React, Vue, Svelte, Solid, Angular, and the ecosystems around them changed what developers could build in the browser.
But the bargain had a cost.
The browser became an application runtime. The server became an API. The client became responsible for routing, rendering, state, caching, validation, error handling, retries, authentication state, and sometimes even business logic.
For many apps, that was too much.
This architecture can work well. It is also easy to overuse.
A marketing site does not always need it.
An admin panel does not always need it.
A CRUD app does not always need it.
A documentation portal does not always need it.
An internal tool definitely does not always need it.
The performance cost is also real. Modern server-side rendering often sends HTML first, then hydrates it on the client. That improves first paint, but hydration can still add JavaScript work before the page becomes fully interactive. Research on adaptive hydration in React and Next.js describes hydration as a performance bottleneck that modern rendering strategies try to reduce, not ignore.
The ecosystem knows this.
That is why we now have React Server Components, islands architecture, resumability, partial hydration, streaming SSR, server actions, and edge rendering. These are attempts to claw back simplicity and performance after years of pushing too much work to the client.
That is not a failure of frontend engineering.
It is a correction.
The web is remembering that HTML is already a good delivery format.
HTML-first is not nostalgia
HTML-first development sounds like a throwback until you look closely.
The argument is simple. Start with the web platform. Use HTML for structure, CSS for presentation, and small amounts of JavaScript for behavior. Move business logic to the server when possible. Add client-side complexity only where it improves the product enough to justify the cost.
That does not mean no JavaScript.
It means JavaScript is used with intent.
The 2026 paper The Case for HTML First Web Development argues that the gap between the web platform and framework-heavy development has narrowed as web standards have improved. It also notes that HTML-oriented development can reduce code size and conceptual complexity, especially when combined with hypermedia and server-side logic.
That matches what many developers are feeling.
HTML is not weak. It is stable, cacheable, accessible by default when used correctly, easy to inspect, and understood by every browser.
HTMX became popular because it gave developers a way to build dynamic interfaces without turning the whole app into a client-side application. Instead of sending JSON and rebuilding UI in JavaScript, the server sends HTML fragments.
This looks almost too simple.
That is the point.
In many apps, the server already knows how to render the UI. The server already has access to the session, permissions, data model, translations, and validation rules. Sending HTML directly can remove an entire layer of duplication.
Compare the two approaches.
| Client-heavy app | HTML-first app |
|---|---|
| Server returns JSON | Server returns HTML |
| Client owns UI assembly | Server owns UI assembly |
| Client duplicates validation | Server validation is central |
| More JavaScript state | Less JavaScript state |
| More hydration work | Less hydration work |
| Better for highly interactive apps | Better for content, CRUD, dashboards, admin tools |
Neither is always right.
The boring web development argument is not that HTML-first wins every time. It is that HTML-first should be considered first more often.
Server rendering never disappeared
Server rendering did not come back because it never fully left.
Django, Rails, Laravel, Phoenix, ASP.NET, Spring MVC, WordPress, and many other systems kept shipping server-rendered applications while the frontend world moved through wave after wave of JavaScript architecture.
What changed is perception.
For a while, server-rendered apps were treated as less modern. A "real" app was a single-page app. A "modern" team separated frontend and backend. A "scalable" architecture split things early.
That belief is weaker now.
The State of Django 2025 report from JetBrains says React and jQuery are still the most common JavaScript frameworks used with Django, but HTMX and Alpine.js have gained momentum. It also says Django usage has shifted back toward server-side templates since the survey began in 2021, with HTMX growing from 5 percent in 2021 to 24 percent and Alpine.js from 3 percent to 14 percent.
That is a useful signal.
Django developers did not forget how to build SPAs. They are choosing smaller tools where they fit.
Rails has been moving in a similar direction with Hotwire, Turbo, Stimulus, and Rails 8 defaults. Rails 8 includes an authentication generator, comes preconfigured with Kamal 2 for deployment, and ships with database-backed Solid Queue, Solid Cache, and Solid Cable options. The Rails 8 release notes describe Solid Cache as a database-backed cache store and Solid Cable as a database-backed Action Cable adapter.
Laravel has also leaned into a flexible middle path. Its starter kits support Livewire for server-driven UI and Inertia for teams that want React, Vue, or Svelte while keeping Laravel routing and controllers. Laravel's documentation describes Livewire as a starting point for building Laravel applications with a Livewire frontend, and Inertia as a way to build modern single-page React applications using classic server-side routing and controllers.
The pattern is not one framework.
The pattern is a return to integrated stacks.
Integrated does not mean primitive.
It means fewer seams.
Fewer seams often mean faster development.
The monolith is having a quiet comeback
Microservices were never supposed to be the default for small teams.
They were a solution to specific organizational and technical problems: independent deployment, team autonomy, scaling boundaries, fault isolation, and large systems with many domains.
But the pattern became fashionable. Many teams split systems before they had the problems that microservices solve.
The result was predictable.
Distributed systems arrived early.
Now one feature required multiple repositories, multiple deployments, multiple API contracts, multiple monitoring dashboards, multiple network calls, and multiple ways for things to fail.
A monolith avoids many of those costs.
Not always. But often.
A good monolith is not a ball of mud. It can still have modules, boundaries, tests, clear domain concepts, background jobs, and good deployment practices.
The difference is operational simplicity.
One deploy.
One runtime.
One primary database.
One place to follow a request.
One codebase to search.
One local development environment.
This matters more than teams admit.
Local development is part of architecture. If a new developer needs two days to run the app, the architecture has a cost. If every feature requires coordinating three services and two deployment pipelines, the architecture has a cost.
Boring web development treats those costs as first-class concerns.
A modular monolith is often the right default because it keeps complexity local until the system earns the need to split.
This is not anti-microservice.
It is anti-premature-distribution.
The database is getting simpler too
For years, web stacks tended to accumulate specialized data systems.
Postgres for relational data.
Redis for cache.
Elasticsearch for search.
Kafka for events.
A queue service for jobs.
A separate analytics store.
A vector database for embeddings.
Object storage for files.
Some products need this. Many do not, at least not at the beginning.
The boring web development movement asks another uncomfortable question:
Can the database we already have do more?
Postgres has become the obvious answer for many teams. It handles relational data, JSON, full-text search, constraints, indexing, transactions, extensions, geospatial workloads, and vector search through pgvector. It is not the best tool for every one of those jobs, but it is good enough for many of them.
SQLite is also getting more serious attention, especially for small apps, local-first apps, single-server deployments, embedded use cases, and simple self-hosted products. SQLite's own site describes it as small, fast, self-contained, high-reliability, full-featured, and the most used database engine in the world.
Rails 8 made this conversation louder by embracing database-backed pieces like Solid Queue, Solid Cache, and Solid Cable. That does not mean every Rails app should use SQLite in production. It means the framework is actively exploring how far a database-backed default can go before teams need extra infrastructure.
This is the boring database stack:
The benefit is not that one database is magically perfect.
The benefit is fewer systems to operate.
| Need | Boring first option | Add specialized tool when |
|---|---|---|
| Relational data | Postgres | Almost never need to replace early |
| Small app data | SQLite | Write concurrency or team needs grow |
| Full-text search | Postgres FTS or SQLite FTS5 | Search relevance becomes product-critical |
| Background jobs | Database-backed queue | Throughput or isolation demands it |
| Cache | HTTP cache, DB-backed cache, simple memory cache | Cache scale or eviction needs grow |
| Events | Outbox table | Event volume or consumers grow |
| Vectors | pgvector | Scale, recall, or latency demands specialized search |
This is not about refusing infrastructure.
It is about delaying infrastructure until the problem is real.
Every extra database adds backups, monitoring, access control, data modeling, local setup, migrations, network behavior, failure modes, and security concerns.
Sometimes that cost is worth it.
Sometimes it is just resume-driven architecture in a nicer jacket.
Deployment got too clever
Deployment used to be boring in a good way.
Build the app. Put it on a server. Run it behind a web server. Point a domain to it. Monitor it. Back it up.
Then deployment got easier and more complicated at the same time.
Modern platforms gave us preview deployments, serverless functions, edge runtimes, global caching, managed databases, automatic HTTPS, autoscaling, branch deploys, and push-to-deploy workflows. These are real improvements.
But many teams also ended up with deployment architectures they barely understood.
One app might include:
Static assets on a CDN
Serverless API routes
Edge middleware
A managed database
A queue service
A cron service
A background worker platform
Object storage
Secret management
Build caching
Preview environments
Multiple regions
Observability add-ons
That can be fine for a product that needs it.
It can be absurd for a small SaaS, an internal dashboard, a content site, or a simple marketplace.
The return of boring web development is also a return to simple deployment.
Rails 8's Kamal integration is part of this story. Kamal helps deploy containerized apps to servers, including cloud VMs or your own hardware. The point is not that everyone should use Kamal. The point is that "just deploy to a server" is becoming respectable again.
This architecture is not glamorous.
It can run a real business.
A single server is not enough for every app. But it is enough for more apps than people think, especially early on.
The same is true for platform choices. A managed platform can be a good boring choice if the team understands it and it reduces operational load. A VPS can be a good boring choice if the app is simple and the team wants control.
Boring is contextual.
The key is not the server. The key is whether the deployment model is understandable.
If no one on the team can explain how a request reaches production and where it can fail, the deployment is too clever.
AI is making boring more valuable
AI coding tools are another reason boring web development is coming back.
This may sound backwards. AI feels like the least boring thing in software right now. But AI makes boring stacks more useful because boring stacks have clearer patterns.
LLMs are better when the framework has strong conventions.
They are better when there is one obvious place for a controller, one obvious way to validate a form, one obvious way to write a test, one obvious way to query data, and one obvious way to render a page.
A convention-heavy Rails, Laravel, Django, or Phoenix app can be easier for AI tools to navigate than a custom stack with clever architecture scattered across many services.
That does not mean AI writes perfect code in these frameworks. It does not. But boring structure reduces the search space.
This matters because AI-generated code already creates review work. The more custom and distributed the architecture is, the more context the model needs and the more ways it can be subtly wrong.
Boring stacks help humans too.
If the app follows standard conventions, a human reviewer can spot mistakes faster. A new teammate can learn the code faster. A bug can be traced faster. Documentation can be shorter.
AI does not remove the need for architecture.
It rewards architecture that is easy to explain.
Boring frontend does not mean ugly frontend
One objection comes up often:
But users expect rich interfaces.
Yes. They do.
Boring web development is not an argument for bad UX. It is an argument for choosing the simplest implementation that delivers the UX.
Sometimes that means React.
Sometimes that means server-rendered templates with small JavaScript.
Sometimes that means HTMX.
Sometimes that means Livewire.
Sometimes that means Inertia.
Sometimes that means a full client-side app because the product is actually interactive enough to need one.
The mistake is assuming the most complex frontend architecture is the most professional.
A polished UI can be server-rendered.
A fast dashboard can use HTML fragments.
A form-heavy app can feel modern without becoming an SPA.
A marketing site can be mostly static.
A SaaS admin panel can use progressive enhancement.
The decision should come from the product.
Not from status.
If the user cannot tell whether the app uses React, HTMX, Livewire, or templates, the stack choice should optimize for the team.
That means speed, reliability, accessibility, maintainability, hiring, testing, and deployment.
Users care about outcomes.
Developers should too.
The cost of complexity shows up later
Complexity is easy to ignore at the beginning.
At the beginning, every tool feels like progress. The build passes. The demo works. The architecture diagram looks serious.
The cost arrives later.
It shows up when:
A dependency breaks the build.
A deployment fails in only one environment.
A small UI change touches four packages.
A bug crosses frontend and backend boundaries.
The test suite needs too much mocking.
Local development becomes slow.
Onboarding becomes a week-long task.
The team avoids changes because the system is fragile.
Every incident requires knowledge of five services.
Nobody knows whether a feature belongs on the client or server.
Boring web development is a bet against that future.
It asks teams to optimize for the full lifecycle, not the first sprint.
Most code is maintained longer than it is written.
That is why boring choices often win.
A familiar stack with known limits can be better than a perfect stack no one understands.
When boring is the wrong choice
Boring is not a religion.
Sometimes boring technology is the wrong choice.
If you are building a highly interactive design tool, a real-time multiplayer editor, a complex offline-first app, or a browser-based IDE, you probably need serious client-side architecture.
If you are building global low-latency infrastructure, you may need edge compute.
If you are processing huge event streams, you may need Kafka or another specialized system.
If search quality is core to the product, you may need a real search engine.
If your workload has independent scaling boundaries, microservices may be correct.
If your team has deep expertise in a tool, that tool may be boring for your team even if it looks exotic to others.
That last point matters.
Boring is local.
Elixir and Phoenix may be boring for a team that has used them for years. Rails may be risky for a team with no Ruby experience. Kubernetes may be boring for a platform team and absurd for a two-person SaaS.
A good decision depends on context.
| Question | Why it matters |
|---|---|
| Does the team know this tool? | Familiarity reduces hidden cost |
| Can we operate it at 2 AM? | Production is the real test |
| Does it solve a real product problem? | Novelty is not enough |
| Can we test it easily? | Untestable systems become fragile |
| Can we hire for it? | Team growth matters |
| Can we migrate away later? | Lock-in is not only vendor lock-in |
| Is the complexity earned? | Complexity should buy something specific |
The best boring web developers are not stubborn.
They are disciplined.
They know when to keep things simple, and they know when the problem has outgrown the simple answer.
A boring stack for a modern app
What does boring web development look like in practice?
There is no single stack, but here is a reasonable default for many small and medium web apps:
One monolith
Server-rendered pages by default
Small JavaScript where needed
TypeScript only where JavaScript grows large
Postgres as the primary database
Database-backed jobs at the start
Basic full-text search before adding search infrastructure
File uploads to object storage
Simple caching with HTTP cache, database cache, or framework cache
One deployment pipeline
One main production environment
Good backups
Good logs
Good error tracking
A boring CSS system the team can maintain

This stack can carry a lot.
Not every product, but many.
And because it is simple, it is easy to change later.
If the search feature becomes important, add Meilisearch, Typesense, OpenSearch, or Elasticsearch.
If background jobs grow, move from database-backed jobs to a dedicated queue.
If one domain needs independent scaling, split that module into a service.
If one UI becomes highly interactive, build that part with a heavier frontend.
The boring stack does not block future complexity.
It makes future complexity easier to justify.
The new rule is progressive complexity
Progressive enhancement is a classic web idea. Start with a working baseline, then add richer behavior where supported.
Boring web development applies the same idea to architecture.
Start simple. Add complexity when the system earns it.
Call it progressive complexity.
This approach changes team behavior.
Instead of asking, "What is the best architecture?"
Ask:
What is the simplest architecture that can handle the next stage of the product?
That question is more useful.
It respects the future without overbuilding the present.
It also creates a healthier engineering culture. Teams stop collecting tools as identity markers. They start treating tools as costs and capabilities.
That is maturity.
What this means for new developers
For new developers, boring web development is good news.
It means you do not need to learn every new framework before you can build useful software.
Learn the web platform.
Learn HTML well.
Learn forms.
Learn HTTP.
Learn CSS.
Learn SQL.
Learn how sessions work.
Learn server rendering.
Learn accessibility.
Learn caching.
Learn deployment.
Learn how to read logs.
Learn how to back up a database.
These skills age well.
Frameworks change. The web platform stays.
A developer who understands request-response, relational data, progressive enhancement, and server-side rendering can move across Rails, Laravel, Django, Phoenix, ASP.NET, Spring, Express, Next.js, Remix, and many other stacks.
A developer who only knows how to wire together the latest frontend library may feel productive, but the foundation is thinner.
Boring skills are portable skills.
That matters even more in the AI era. AI tools can generate code, but developers still need to know whether the code is right. The better your fundamentals, the more useful AI becomes.
What this means for teams
For teams, boring web development is a strategy.
It is a way to ship faster by choosing fewer things.
A team can make this practical with a few rules.
Pick a default stack
Choose the stack you reach for when nothing unusual is required.
That might be Rails and Postgres. Laravel and MySQL. Django and Postgres. Phoenix and Postgres. ASP.NET and SQL Server. Next.js and Postgres. The specific choice matters less than the fact that it is shared.
Require a reason for new infrastructure
Do not add Redis, Kafka, Elasticsearch, Kubernetes, a vector database, or a separate service because it might be useful someday.
Write down the current limitation.
Then write down why the current stack cannot handle it.
Keep frontend complexity local
If one page needs React, that does not mean the whole app must become a client-side app.
Use rich frontend islands where they help.
Keep the rest simple.
Optimize onboarding
A boring stack should be easy to run locally.
A new developer should be able to clone the repo, install dependencies, run one command, and see the app.
Own production
Boring does not mean unprofessional.
You still need logs, backups, monitoring, error tracking, security updates, tests, and deployment discipline.
Boring web development is not lazy.
It is calm.
The future is not less modern
The return of boring web development does not mean the future is less modern.
It means the future may be more selective.
Modern web development is not one thing anymore. It is a menu.
You can use server-rendered pages.
You can use React Server Components.
You can use HTMX.
You can use Livewire.
You can use Inertia.
You can use Astro islands.
You can use a full SPA.
You can use edge functions.
You can use a monolith.
You can use microservices.
The skill is not knowing all of them.
The skill is knowing when each one is worth its cost.
That is why boring web development is coming back. It gives developers a way to say no without being anti-progress.
No, we do not need a separate frontend app for this.
No, we do not need a new database yet.
No, we do not need a microservice.
No, we do not need a queue service for five jobs per minute.
No, we do not need Kubernetes for a two-person project.
No, we do not need to hydrate the entire page.
No, we do not need to make the simple thing complicated to prove we are modern.
That kind of no is valuable.
It protects the team's attention.
Boring is how software lasts
The web is old enough now to have memory.
We have seen stacks rise and fade. We have seen libraries become ecosystems, ecosystems become platforms, and platforms become legacy. We have seen simple apps survive for years while clever architectures collapsed under their own weight.
That does not mean we should stop experimenting.
It means experimentation should have a budget.
Use new technology where it creates real advantage. Use boring technology everywhere else.
The best web apps of the next decade may not be the ones with the most impressive architecture diagrams. They may be the ones that are fast, accessible, understandable, easy to deploy, easy to change, and boring in all the right places.
Boring web development is not a retreat.
It is a correction.
It is the industry remembering that the goal was never to build the most complex stack.
The goal was to build useful software that people can trust, use, maintain, and afford to keep running.
That is still a modern goal.
Maybe the most modern one.



