Web development has not moved in a straight line. It swings between doing the work on the server and doing it in the browser, and every swing fixed the worst problem of the last one while creating a new problem of its own. Read the swings properly and most predictions about where the web goes next stop being guesses.
Articles about where web development is heading usually skip the history, which is backwards, because the history is the only evidence anyone has. Server rendering did not return because a vendor decided it should. It returned because a decade of single page applications produced slow first loads, broken back buttons and pages that search engines saw as empty divs. Typed JavaScript won no argument; it won by attrition, one codebase at a time, because refactoring untyped JavaScript at size is miserable work.
This piece does both halves: what actually changed, shift by shift, and for each one the problem it solved, the thing it broke and the part that survived. Then a test for telling a durable shift from a fashion, which is worth more than any prediction, and what that means if you are picking a stack for a product you must keep alive for five years.
Sapient Codelabs is a product engineering studio in Surat, India. We build client software and run our own products, so a fair amount of what follows comes from maintaining web applications years after launch rather than only shipping them.
Server-rendered pages and the jQuery era
The first web was documents. A request arrived, a program on the server produced a full HTML page, the browser painted it, and interaction meant links and form posts. CGI scripts, then PHP, then ASP, then Rails and Django. State lived in a database and a session cookie, and the browser was a rendering surface and almost nothing else.
The model was crude and it was right about several things. Every screen had a URL. The back button worked because the browser was doing the navigation. Search engines got HTML because HTML was all there was. A page that failed halfway failed visibly, at a URL you could reproduce.
What it broke was interaction. Every state change cost a full page load, so filtering a table meant a round trip and a repaint. The workaround was the frameset, then the hidden iframe, then real asynchronous requests once XMLHttpRequest shipped everywhere.
What jQuery was actually for
jQuery is remembered as a DOM convenience library. Its real job was patching over browsers that disagreed about everything: event models, box sizing, the AJAX object, whether an element could be selected by class. The pattern of the era was a server-rendered page with islands of behaviour on top, and it failed in one specific way. State ended up duplicated between the server template and the DOM with nothing keeping them honest, so bugs came from disagreement rather than logic.
What made jQuery obsolete was not a framework. It was browsers converging, plus querySelectorAll, fetch and classList landing natively. Note that, because it is the most common way a tool dies: the platform absorbs it.
The single page application, and what it actually cost
Gmail and Google Maps proved a browser could hold a real application. Backbone, then Angular, then React made it the default. The server became a JSON API, the browser owned the UI, routing moved client side.
The problem it solved was genuine. Interactions became instant with no page reload. State lived in one place instead of smeared across a template and the DOM. Component models made large interfaces tractable in a way string templates never did. For software people keep open all day, an internal dashboard, a scheduling console, an editor, the model is still correct today.
What it broke took years to become obvious:
- First load. Nothing renders until JavaScript downloads, parses and executes. On a mid-range Android phone on a mobile network, that is not a rounding error.
- Bundle growth. Bundles only grow. Every feature adds weight to the first paint of every page, including the pages that do not use the feature.
- Browser behaviour. The back button, scroll restoration, in-page search and open-in-new-tab all had to be reimplemented, usually worse than the browser's own.
- Crawlability. Content that exists only after JavaScript runs is content a crawler may or may not see, and nobody tells you which.
- Failure mode. A broken server-rendered page shows a broken page. A broken client-rendered page shows a white screen and an error in a console no user will open.
The deepest cost was cultural. Teams began treating a marketing site, a documentation page and a live trading console as the same kind of artefact, because the tooling made them look the same. A page read once and a workspace open for eight hours have almost nothing in common. What survived was components, declarative rendering and a single source of truth for state, and all three are now available under several rendering models.
Server rendering came back, and Jamstack made half a point
The correction started as a patch. Render the first view on the server so something is on screen quickly, ship the JavaScript, then let the client take over. Hydration was the join, and it turned out to be the expensive part: you send the HTML, send the data again inside the bundle, then rebuild the component tree in the browser to attach event handlers. You pay twice for one screen.
Everything since has tried to make that join cheaper. Partial and selective hydration. Islands, where only the interactive parts ship JavaScript. Streaming, so the page arrives in pieces instead of waiting on the slowest query. Server components, drawing a line between code that runs on the server and code that ships to the browser. Implementations differ and will keep differing; the direction has been consistent for years. Send less JavaScript, render more of the first view where the data already is.
What Jamstack got right and where it stopped
Jamstack made a sharp argument: pre-build the HTML, put it on a CDN, talk to APIs for the dynamic parts. For content sites it was plainly correct, because static files on a CDN are fast, cheap and close to impossible to break, and no runtime means no runtime to attack or patch.
It stopped at content volume and freshness. With a large catalogue, rebuilding everything to change one price is absurd, and build times grow with the catalogue. If prices change hourly, a build step is a stale cache with a deploy pipeline attached. Personalised and authenticated content never fitted.
So the industry kept the useful half: incremental and on-demand regeneration, per-route caching decisions, stale-while-revalidate at the edge. The durable idea was never "everything is static". It was "decide freshness per route, and serve from a cache by default", which is now embedded in the frameworks and was in HTTP all along. For a product rather than a blog, that choice belongs in the same conversation as the data model and hosting cost, which is why web application development is better treated as one decision than several separate ones.
Build tools churned, and typed JavaScript won by attrition
The build layer has been the least stable part of web development and the least rewarding place to spend attention. Grunt, Gulp, Bower, Browserify, webpack, Rollup, Parcel, esbuild, Vite. Each generation was genuinely faster or simpler than the last, and each cost every team a configuration migration that delivered nothing a user could see.
The lesson is not "avoid build tools". It is that anything between your source and your output is a tax you pay again every time it changes. The teams that suffered least were the ones whose application code did not know what the bundler was: no framework-specific import magic in business logic, no build-time plugins doing semantic work.
Types were the exception
CoffeeScript, Flow and a long line of transpilers came and went. TypeScript stayed, and it is worth being precise about why. It was gradual, adoptable one file at a time with no rewrite. It was structural, describing JavaScript that already existed instead of demanding a new style. It paid off where JavaScript hurt most: renaming things in a large codebase, and knowing what a function returns eighteen months after someone else wrote it. And the editor made the benefit visible on day one, not at the end of a migration.
Compare a tool that needs a full rewrite, has no editor payoff and has one company behind it. The difference in adoption is not taste, it is the shape of the migration path. The open question now is only where type checking runs, since that work keeps moving into faster native tooling.
Edge, serverless, and where AI assistance actually sits
Serverless solved a real operational problem: nobody wants to run a server that is idle most of the day, or be paged because a box filled its disk. Per-request billing and automatic capacity are genuinely useful for spiky, stateless work.
What it broke was the assumptions underneath ordinary application code. Cold starts put latency where you did not expect it. Pooled databases do not enjoy hundreds of short-lived functions each opening a connection, which is why connection poolers became mandatory infrastructure rather than an optimisation. Long-running jobs, websockets and anything holding state needed another home, and local development stopped resembling production.
Edge compute pushed execution close to the user, which is excellent for work that needs no data: redirects, rewrites, token checks, feature flags, geolocation, A/B assignment. It is far less interesting when your database sits in one region, because you have moved compute away from data and added a round trip to every query. The pattern that emerged is boring and correct: request-shaping logic at the edge, data-heavy logic next to the data, and honesty about which one a given route is.
AI assistance changes the cost of writing, not the cost of being wrong
Model-assisted coding is the current large shift, and it deserves a precise description rather than dismissal or overclaiming. What it makes cheap is producing code: boilerplate, a first implementation, tests for code that already exists, a migration across many similar files, the unfamiliar API you would otherwise read docs for.
What it does not make cheap is what was already expensive. Deciding what to build. Knowing a requirement is wrong. Reviewing a change well enough to be responsible for it in production. Debugging a system nobody has in their head. When writing gets cheaper and reviewing does not, review becomes the bottleneck, and codebases that were already hard to reason about get harder faster.
So the things that make a codebase legible are worth more now, not less: consistent structure, types at the boundaries, tests that state intent, small modules with clear names, written conventions. When we scaffolded Nestlet, an in-house marketplace for short-term residential leases that is still in build, we wrote a full design guideline document before the screens so the work could go to developers or to coding agents and come back consistent either way. That document does more for output quality than any prompt.
What survived every shift, and how to spot the next one
Line up thirty years of churn and a short list has never stopped being true. This is the part worth learning deeply, because it outlasts whatever framework you are currently arguing about.
The list that never stopped being true
| Survived | Why it keeps winning | What kept failing against it |
|---|---|---|
| URLs that name a state | Shareable, bookmarkable, cacheable, restorable, crawlable | Client-only routing that forgets where you were |
| HTML that means something | Accessibility, search, and browser features come free | Divs plus JavaScript reimplementing native controls |
| Relational data with constraints | Wrong data is stopped at write time, not discovered later | Schemaless stores that pushed integrity into app code |
| One source of truth for state | Disagreement between copies is the bug factory | State duplicated between template, DOM and store |
Notice that none of these are frameworks. They are properties of the web platform and of data modelling, and every framework that lasted made them easier rather than replacing them.
Questions that fashions answer badly
You cannot evaluate a new tool by reading its landing page, because every landing page describes a durable shift. You can evaluate it by asking questions a fashion answers badly. These are the ones that have held up.
| Question | Durable shift | Fashion |
|---|---|---|
| What cost does it remove? | A cost you can name and measure: bytes shipped, queries per request, hours of on-call | Feels nicer to write |
| What is the migration path? | Incremental, file by file or route by route | Rewrite, all at once |
| Who maintains it? | A standards body, several vendors, or an ecosystem with many paying users | One company, one funding round, one maintainer |
| What happens when the platform catches up? | The tool still has a job | The tool was the missing browser feature |
Three patterns are worth stating plainly. The platform absorbs tools: jQuery, most of Lodash, Moment and a long tail of polyfills lost to native features rather than to competitors, so anything whose whole value is filling a platform gap is on a clock. Shifts that make the hard part easier survive, and shifts that move the hard part somewhere less visible do not; hydration moved work to the browser and got years of correction, while types made refactoring safer and stayed. And anything that speeds up the first load keeps winning, because the binding constraint is not your laptop. It is a mid-range phone on a congested network, and that has proven more stable than any framework.
Picking a stack you have to maintain for five years
Most stack arguments proceed as if the build is the whole cost. Software you keep needs upgrades, dependency bumps, security patches, browser changes and a slow accumulation of small fixes, for as long as it is alive. Our published planning number from running Denti360, our dental practice management product, is ongoing engineering of roughly 15 to 20 percent of the original build cost per year to keep it current, before new features. Whatever you pick, you are agreeing to that bill in that technology.
So the question is not "what is fastest" or "what is newest". It is "what will still be sane in five years, with a team that is not the team that started it". Work through these in order.
- Classify the product honestly. Content site, transactional application, or long-session workspace. Each wants a different rendering model, and the expensive mistake is building a content site as an application, or an application as a set of pages.
- Pick the data model before the framework. Schema shape, ownership, tenancy and access boundaries outlive every UI decision. In Denti360, branch scoping went into the schema in the first migration rather than being retrofitted, which is why multi-branch behaviour was not a rewrite later.
- Choose boring where boring exists. A relational database, a well-known runtime, a hosting model your team has operated before. Spend the novelty budget on the one or two places where the product is genuinely unusual.
- Check the hiring market, not the hype market. In five years someone else maintains this. If you cannot hire for it where you hire, you have bought a permanent dependency on whoever wrote it.
- Prefer tools with an exit. Standard SQL over proprietary query layers, portable containers over vendor-shaped deploy formats, a data export you have actually run. Lock-in is fine when you chose it deliberately and priced it.
- Count the upgrade surface. Every framework, plugin, build tool and hosted service demands attention on its own schedule. Fewer moving parts, fewer forced migrations.
- Write the conventions down. Whether the next code comes from a new hire or is generated, written structure is what keeps a codebase consistent after the original authors leave.
As a concrete example, Nestlet uses Next.js App Router with TypeScript and Tailwind on the front end, and NestJS with PostgreSQL behind it. That is not a claim this combination wins. It is a claim that each piece has a gradual migration path, a large hiring pool and an exit, which is what the five-year test asks for.
If the product will carry payments, documents, multi-tenant data or compliance obligations, the stack decision and the delivery plan are the same decision, which is the argument for treating it as one engineering engagement from scoping through launch. And if what you are really trying to size is budget rather than architecture, the ranges in our web and web app cost guide beat a framework comparison.
So where is web development heading?
The honest version, with no dates and no winners named.
- Rendering becomes a per-route choice. Not "SPA or server rendered" project-wide, but a decision per route based on data freshness and interactivity. Frameworks are converging on exactly this.
- JavaScript payloads keep shrinking because mobile hardware and networks stay the binding constraint. Any technique that ships less JavaScript for the same interface finds an audience.
- Types stay, and checking gets faster and moves closer to the runtime. Annotations written now outlive the tooling that reads them.
- The platform keeps eating libraries. Dialogs, popovers, view transitions, container queries, date handling. Bet on browser features and stop maintaining wrappers.
- Compute splits by whether it needs data. Request-shaping work moves to the edge, data-heavy work stays beside the database, and the boundary gets more explicit.
- Model assistance moves the bottleneck to review and specification. The teams that gain are the ones whose codebases were already legible and whose requirements were already written down.
What is missing matters as much as what is on the list. No framework is named as the winner, because the history says frameworks are how ideas travel, not where they end up. Ideas end up in the platform, in the language, and in the habits of people who have kept something running for years. Hence the operating rule: learn the layer that does not churn, which is HTTP, HTML semantics, the data model and the browser's own behaviour, and treat everything above it as replaceable. Teams that do that change frameworks in a quarter. Teams that do not, rewrite.
Choosing a stack for a product you will still be running in five years? A Scoping Sprint ($2,300, two weeks) ends with a rendering and data-model decision written down for your specific product, a clickable prototype, and a fixed quote for the build. Or just start a conversation.


