In a Next.js project, what is the difference between running `next dev` and running `next build` followed by `next start`, and why can you not judge caching or bundle size from `next dev`?
answer
- dev trades production fidelity for speed
- routes compiled on demand in dev
- prerendering happens at build, not dev
- start replays a finished build
- caching and bundle numbers only from build
basics
~20 snext dev compiles routes on demand with optimizations and caching turned off for fast feedback. next build produces the optimized production artifacts once, and next start serves them without compiling. Only the build-and-start pair reflects real caching, prerendering and bundle sizes.
solid answer
~50 s`next dev` runs a development server: it compiles each route the first time you request it, keeps React in development mode, skips minification, and wires up Fast Refresh. To make edits visible immediately it also short-circuits the production caching behaviour, and it renders every route per request, so nothing is prerendered. `next build` is the one-shot production compile — it bundles and minifies client JavaScript, runs the prerender pass that produces HTML and the RSC payload for statically renderable routes, and prints a per-route report of sizes and whether each route ended up static or dynamic. `next start` boots the production server over that finished build and does no compiling at all; without a prior build it exits with an error. So any judgement about caching, prerendering, first-load JS or latency has to be made against `next build && next start` or a real deployment, never against dev.
go deeper
Know the three commands and what each one is for: dev for the edit loop, build to produce production output, start to serve it. Say plainly that performance and caching must be checked on a production build.
Explain the mechanics: on-demand compilation and disabled production caching in dev, the prerender pass and static-versus-dynamic decision at build, and a start server that only replays artifacts.
Show that you read the build output as a signal — catching a route that silently became dynamic or a first-load JS regression before it reaches production, and reproducing reported issues against build-and-start rather than dev.
Own the guardrail: make the production build part of CI on every change so route status and bundle budgets are enforced by the pipeline, not by whoever remembers to run it locally.
## Two different programs, not two modes of one It is tempting to treat `next dev` as "the app, but slower". It is closer to a different program that happens to render the same routes. Development optimizes for the edit-save-see loop; production optimizes for what a user gets. Almost every property a candidate is asked about — is this route static, is this response cached, how big is the client bundle — is a property of the production build only. ## What `next dev` does The dev server compiles routes lazily: nothing is built until you navigate to it, and then only that route's module graph is compiled. React runs in development mode, which keeps warnings, prop checks and extra bookkeeping that never ship. Output is unminified, source-mapped, and carries the hot-reload client. Because a stale cached render would defeat the whole point of hot reload, development deliberately does not reuse rendered output between requests the way production does — every request re-renders. The practical consequence: "my data isn't caching in dev" is not evidence of anything, and neither is "my page re-renders on every request in dev". Equally, a route that will be prerendered at build time in production is indistinguishable in dev from one that renders per request, because in dev both render per request. ## What `next build` does `next build` is where the real decisions get made: - it compiles and minifies the client bundles and tree-shakes what nothing imports; - it runs a prerender pass, actually executing every statically renderable route so it can emit HTML plus the React Server Components payload; - it decides, per route, static or dynamic — a route that reads request-scoped data cannot be prerendered and is marked dynamic; - it fails the build on type errors and on errors thrown during prerendering. It then prints a per-route table with each route's status and its first-load JavaScript. That table is the artifact of record: it is the fastest way to notice that a route you expected to be prerendered became dynamic, or that one page's first-load JS jumped after a dependency change. Nothing in dev surfaces either fact. ## What `next start` does `next start` boots the production Node server against the finished build. It compiles nothing; it replays what `next build` produced, serving prerendered HTML where it exists and rendering on demand where it does not. Run against an empty or missing `.next` directory, it exits with an error telling you to run `next build` first — which is exactly the error people hit in a Dockerfile that forgot the build step. ```bash next build # produce the production output next start # serve that output, no compilation ``` ## Why this matters at deploy time Every deployment target ultimately runs the output of `next build`. A managed platform runs the build for you and serves the result; a container image runs the build during `docker build` and starts the server in the container. Either way, `next dev` never runs in production, so a workflow where the only place anyone looks at the app is dev will keep discovering problems after deploy: a component that reads request data and quietly turned a whole route dynamic, a heavy library that only shows up in the first-load column, a caching assumption that only holds in production. ## The habit to build Before anything that matters — a performance claim, a caching question, a deploy — build and start locally. It takes seconds on a small app and it is the only local signal that matches what users get. Read the route table each time; treat an unexpected dynamic marker or a jump in first-load JS as a review comment, not a curiosity.
- What happens if you run `next start` in a directory where `next build` has never been run?It refuses to start and tells you no production build was found in the `.next` directory, pointing you at `next build`. It never falls back to compiling on demand — `next start` only ever serves an existing build, which is why a Dockerfile or CI job that skips the build step fails at container start rather than at build time.
- Where would you look to find out whether a given route will be prerendered in production?The `next build` output. It lists every route with a marker for static versus dynamic, so you can see at a glance which routes were prerendered at build time and which will render per request. That table also reports first-load JavaScript per route, which is the same place you catch an accidental bundle regression.
- A page renders correctly with `next dev` but the production build fails. What kinds of failure only appear at build time?Type errors, lint or compile errors that dev tolerates because it only compiles the route you visited, and errors thrown while prerendering — a component that throws when executed without a request, or a data call that fails against a build-time environment lacking credentials. Dev never runs the prerender pass, so none of these surface there.
saying these in an interview costs you the question
- Claims next start rebuilds the app if the build is missing
- Thinks next dev shows real caching behaviour
- Measures bundle size or page speed against the dev server
- Assumes a route static in dev is static in production
- Believes next build only compiles and does not render anything