skip to content

Builders & Dev Server

The application builder bundles browser and server code, the development server rebuilds and reloads it, and options like outputHashing tune output. Interviewers probe build speed and output.

part ofAngular CLIoverview, primer and where to startread it →
on this pageshow

explore

questions

6

With the Angular CLI, how does `ng serve` differ from `ng build`, and why is the dev server's output never what you deploy?

level: juniorimportance: must knowfreq 70%

answer

  1. two builders, one build pipeline
  2. @angular/build:dev-server wraps application
  3. development vs production configuration
  4. in memory, watched, served by Vite
  5. dist/<project>/browser is the artifact

basics

~20 s

ng serve runs the @angular/build:dev-server builder, which builds the app with its development configuration in memory, watches files and reloads the browser. ng build runs @angular/build:application with the production configuration and writes optimized, hashed files to dist/<project>/browser for deployment.

solid answer

~40 s

Both commands build the application with the same `@angular/build:application` pipeline, but for different purposes. `ng build` (alias `ng b`) runs the `build` target, whose `defaultConfiguration` is `production` in a new project: optimization on, no source maps, `outputHashing: 'all'`, licenses extracted. It writes to `dist/<project>/browser` (plus `server` when SSR is on), and that folder is the deployable artifact. `ng serve` (aliases `ng s`, `ng dev`) runs the `serve` target's `@angular/build:dev-server` builder. It points at `build:development` by default, which turns optimization off and source maps on. It keeps the output in memory, hands it to a Vite-based server, rebuilds only what changed on each save, and reloads or hot-replaces in the browser. It is unminified, unhashed and not written to `dist`, so it is a development tool, never a deployment.

code

json · 16 lines
json
"build": {
  "builder": "@angular/build:application",
  "defaultConfiguration": "production",
  "configurations": {
    "production": { "outputHashing": "all" },
    "development": { "optimization": false, "extractLicenses": false, "sourceMap": true }
  }
},
"serve": {
  "builder": "@angular/build:dev-server",
  "defaultConfiguration": "development",
  "configurations": {
    "production": { "buildTarget": "app:build:production" },
    "development": { "buildTarget": "app:build:development" }
  }
}

go deeper

for a junior

Know that ng serve is for local development with reload on save, and ng build creates the production files in dist to deploy.

for a middle

Explain the build and serve targets, the dev-server's buildTarget, and exactly which options the production and development configurations change.

for a senior

Use ng serve --configuration production to reproduce production-only bugs, knowing what changes (HMR off, optimized code), and keep CI deploying only ng build artifacts.

for a principal

Standardise how every app in the organisation builds and serves, so local, CI and production share one configuration model and differences are deliberate.

## Two targets, two builders In `angular.json` every command maps to a **target** that names a **builder**, the function that does the work. A project created by Angular CLI 22 has: | Target | Builder | `defaultConfiguration` | Used by | |---|---|---|---| | `build` | `@angular/build:application` | `production` | `ng build` / `ng b` | | `serve` | `@angular/build:dev-server` | `development` | `ng serve` / `ng s` / `ng dev` | The dev-server builder does not have its own compiler. Its `buildTarget` option points at the build target with a configuration: `app:build:development` by default, `app:build:production` when you run `ng serve --configuration production`. It then runs that same application build internally. ## What the two configurations change The generated `build` target keeps most values at the builder's schema defaults and overrides a few per configuration: - **production**: `outputHashing: 'all'` and the budgets. Everything else uses the schema defaults, so `optimization` is `true`, `sourceMap` is `false`, `extractLicenses` is `true` and `namedChunks` is `false`. - **development**: `optimization: false`, `extractLicenses: false`, `sourceMap: true`, with `outputHashing` left at its default of `none`. So `ng build` gives minified, tree-shaken, cache-busted files with a separate license file. `ng serve` gives readable code with source maps, which is fast to rebuild and easy to debug. ## What `ng serve` does on top 1. **Builds in memory.** The Angular docs describe the dev server as generating a development build in memory and passing the results to Vite to serve. Vite is used here *only* as a development server and cannot be configured directly. Nothing is written to `dist/`. 2. **Watches and rebuilds incrementally.** `watch` defaults to `true`. On save, only affected files are recompiled. 3. **Updates the browser.** `liveReload` defaults to `true`. Style and template edits are hot-replaced (HMR) where possible; other changes trigger a full page reload. 4. **Prebundles dependencies.** Third-party packages are processed once on first start, so later rebuilds skip them. The `prebundle` option controls it. 5. **Serves on a local address.** By default it serves on `localhost` port `4200`. Host, port, HTTPS and proxying to a backend are dev-server options too. ## Why you never deploy the dev server - The output is **unoptimized**: no minification, no tree-shaking beyond what the bundler does by default, and large source maps. - File names are **not content-hashed** for entry bundles, so browsers and CDNs cannot cache them safely. - It exists only **in the memory of a process** built for fast rebuilds and debugging. It has development-only behaviour such as live reload and prebundling, and it is not designed or hardened to serve real traffic. - `ng build` produces a **static folder** that any web server or CDN can host (or a server bundle for SSR). That is what CI should build and ship. ## Commands you actually type ```bash ng serve # development build, in memory, http://localhost:4200 ng serve --configuration production # check production settings locally ng build # production build into dist/<project>/browser ng build --configuration development # unminified build written to disk ``` `ng serve --configuration production` is handy for reproducing a production-only bug. Note that it serves the production build settings through the dev server, and that HMR turns itself off because of `outputHashing: 'all'`. ## Side by side | Aspect | `ng serve` | `ng build` | |---|---|---| | Builder | `@angular/build:dev-server` | `@angular/build:application` | | Default configuration | `development` | `production` | | Output | In memory | `dist/<project>/browser` (and `server` for SSR) | | Minified | No | Yes | | Source maps | Yes | No | | Hashed entry file names | No | Yes (`outputHashing: 'all'`) | | Watches and reloads | Yes | No, unless `--watch` | | Purpose | Local development | The deployable artifact | The table is a summary of the *generated* defaults. Every row can be changed per configuration, which is why teams sometimes add a `staging` configuration that serves or builds with production-like settings. ## Common interview traps - Saying `ng serve` "uses Webpack". New projects have used the esbuild-based application builder since v17, taken directly from the `@angular/build` package since v20, and the webpack builders in `@angular-devkit/build-angular` are deprecated in v22. - Thinking `ng build` defaults to a development build. In generated projects `defaultConfiguration` is `production`. - Looking for the output in `dist/<project>` directly. The application builder writes browser files to a `browser` subfolder.

  • Where does `ng build` put its output, and why did that surprise teams moving from the old builder?
    By default the application builder writes to `dist/<project>/browser`, plus `dist/<project>/server` when SSR is enabled. The old `browser` builder wrote straight into `dist/<project>`, so deploy scripts that copied that folder broke. Adjust the scripts, or configure `outputPath` so the browser folder name is empty.
  • Why might `ng serve` show a brief flash of unstyled content that `ng build` output never shows?
    The dev server defers processing stylesheets until they are first used, to keep rebuilds fast. The Angular docs note this can cause a small flash of unstyled content on startup. It does not happen in builds outside the dev server.

saying these in an interview costs you the question

  • Deploys the output of ng serve or copies files out of the dev server
  • Believes ng build produces a development build unless --prod is passed
  • Says ng serve in a new project runs a webpack dev server
  • Expects the built browser files directly in dist/<project> with the application builder
  • Thinks ng serve has its own compiler separate from the application builder
open as a page

In Angular's application builder, what do `optimization`, `sourceMap`, `outputHashing` and `namedChunks` control, and how do production and development builds set them?

level: middleimportance: must knowfreq 55%

basics

~20 s

optimization minifies, tree-shakes and inlines critical CSS and fonts; sourceMap emits maps; outputHashing hashes file names; namedChunks names lazy chunks. Production keeps optimization on, maps off and hashing all; development turns optimization off and maps on.

open as a page

With the Angular CLI dev server, which edits are hot-replaced without a page reload, which cause a full reload, and what can switch HMR off?

level: middleimportance: should knowfreq 40%

basics

~10 s

ng serve hot-replaces global styles and component templates and styles; TypeScript changes reload the page. HMR is off with --no-hmr, without live reload, in JIT builds, or when outputHashing is all or bundles.

open as a page

How do you give an error-reporting service readable stack traces for an Angular CLI production build without letting browsers download your source maps?

level: middleimportance: should knowfreq 35%

basics

~10 s

Set the production sourceMap to { "scripts": true, "hidden": true }. The builder writes .map files without the sourceMappingURL comment. CI uploads them to the error-reporting service and deletes them before deploying.

open as a page

An Angular app still builds with `@angular-devkit/build-angular:browser`; how do you migrate it to the application builder, and what breaks along the way?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Run ng update @angular/cli --name use-application-builder: it switches to the application builder, renames main to browser and drops webpack-only options. Then fix the new dist/<project>/browser output path, non-ESM imports and any custom webpack setup.

open as a page

When is writing a custom Angular CLI builder justified, and how do you create one that `ng run` can execute?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

It is justified for a repeatable project task that needs angular.json options and configurations. Write a createBuilder() handler returning { success }, declare it in builders.json, point the package.json builders field at it, and run ng run project:target.

open as a page