Engineering
Next.js 15: What We Adopt Now and What Waits
Next.js 15 shipped this week, and for once the headline feature is a removal. After a year of the framework caching everything by default and developers discovering it one stale page at a time, version 15 flips the defaults: fetch requests are no longer cached unless you ask, GET route handlers are uncached, and the client router cache no longer reuses page components by default. The framework has effectively conceded that implicit caching was a mistake, and we agree loudly enough that this post is half upgrade notes, half victory lap.
We spent the first two days running the upgrade against an internal project and a client staging environment. Here is where we landed.
The caching flip is the reason to go
Our position since the App Router shipped has been that caching should be a decision you can point to in the code. Next 15 finally agrees. The practical consequence of the new defaults: your app gets slower and correct instead of fast and mysteriously wrong, and then you add caching back deliberately, route by route, where you can articulate why.
Auditing a mid-size codebase for the change took us about half a day. The workflow that made it manageable: search every fetch call and route handler, classify each as "must be fresh" or "can be cached," and make the cached ones explicit. Anything you cannot classify is a conversation you needed to have anyway.
Async request APIs: the codemod does the boring part
The other breaking change is that request-scoped APIs, cookies, headers, params, searchParams, are now asynchronous. The migration is mechanical and the codemod handles most of it:
npx @next/codemod@canary upgrade latest
What the codemod cannot do is tell you why this change exists, and the why matters: making these APIs async lets the framework render everything that does not depend on the request before the request arrives. It is groundwork for a faster rendering model, and it nudges you to notice which components actually need request data. In our audit, several components that awaited cookies needed nothing of the sort; they were request-scoped by habit, not necessity. The migration is a decent excuse to fix that.
Turbopack for dev: yes. For builds: not yet offered, conveniently
Turbopack is now stable for next dev, and on our largest project, cold start dropped from roughly nine seconds to under two, with Fast Refresh updates that feel instantaneous. This is the least controversial upgrade in the release; we have enabled it everywhere. Production builds still run through webpack, which suits our appetite for risk fine. Dev speed is a daily compounding dividend with no production blast radius. Take the free money.
React 19 RC in the box
Next 15 pairs with the React 19 release candidate, which raises the obvious eyebrow: RC in production? Our read, given that the App Router has effectively been running on React canary builds all along, is that this is less radical than it sounds, and the new form Actions and useActionState are genuinely nice for the mutation-heavy dashboards we build. Policy: internal tools and new builds get React 19 features now; client production sites wait for stable, which by all signals is close.
Our adoption ledger
For client work starting this quarter:
- Now: the upgrade itself, the caching audit, Turbopack dev, the async API codemod.
- Selectively: React 19 features on projects that will launch after stable lands.
- Waiting: nothing significant. This is the least hesitant we have been about a Next major since the App Router arrived.
Version 15 is what maturity looks like in a framework: fewer magic tricks, more honest defaults, faster tools. The App Router finally feels like something we recommend without a paragraph of caveats. It only took a year of everyone shipping stale pages to get here.
Building something this could apply to?
We take on a small number of flagship projects each quarter.
Start a project