Next.js 15 landed with some opinionated changes — async request APIs, stricter caching defaults, Turbopack graduating to stable. We've been running it in production for six months across three client projects. Here's the unfiltered version.
The caching model finally makes sense
The biggest mental shift in App Router is moving from getStaticProps / getServerSideProps thinking to per-fetch caching decisions. In Next.js 15, fetches are not cached by default. You opt in.
// Cached, revalidates every 60 seconds
const data = await fetch('/api/data', { next: { revalidate: 60 } })
// Always fresh (like getServerSideProps)
const data = await fetch('/api/data', { cache: 'no-store' })
// Cached indefinitely (like getStaticProps)
const data = await fetch('/api/data', { cache: 'force-cache' })
This is more explicit and less magical than the previous model. We've stopped reaching for unstable_cache in most cases.
Turbopack is real now
Local dev with Turbopack is noticeably faster on large projects. Our biggest client app — 200+ routes, 400+ components — went from 8 seconds cold start to under 2. Hot reload is instant.
We haven't hit a Turbopack compatibility issue in three months. Ship it.
What we'd do differently
One regret: we colocated too much logic in Server Components early on. Some of that is now hard to test. The rule we use now: Server Components fetch data and pass it down. Client Components handle interaction. Logic lives in plain functions that neither knows about.
