skip to content

With the Angular CLI, why does ng build produce an optimized, hashed bundle while ng serve does not, in a newly generated angular.json?

level: juniorimportance: must knowfreq 62%

answer

  1. targets, options, configurations
  2. each target names a default
  3. build defaults to production
  4. serve points at a build configuration

basics

~10 s

In a new Angular CLI project, the build target's defaultConfiguration is production, which adds budgets and outputHashing: 'all', while serve defaults to development, whose buildTarget turns optimization off and source maps on.

solid answer

~40 s

`angular.json` lists each project's **targets** under `architect`. A target has a `builder`, base `options`, named `configurations` that override those options, and a `defaultConfiguration` used when you pass no `--configuration`. In a project generated by Angular CLI 22, the `build` target uses `@angular/build:application` with `defaultConfiguration: "production"`; the `production` configuration adds `budgets` and `outputHashing: "all"`, and the builder's own defaults already enable `optimization`. The `serve` target uses `@angular/build:dev-server` with `defaultConfiguration: "development"`, and that configuration's `buildTarget` is `my-app:build:development`, which sets `optimization: false`, `extractLicenses: false` and `sourceMap: true`. So `ng build` gives a minified, cache-busted bundle and `ng serve` a fast, debuggable one; `ng serve -c production` or `ng build -c development` swaps them.

code

bash · 14 lines
bash
# build target, defaultConfiguration: production
ng build

# same target with the development overrides instead
ng build -c development

# serve target, defaultConfiguration: development (buildTarget my-app:build:development)
ng serve

# serve an optimized bundle to reproduce a production-only bug
ng serve -c production

# long form: project:target:configuration
ng run my-app:build:development

go deeper

for a junior

Recall that ng build defaults to production and ng serve to development, and that -c picks another named configuration.

for a middle

Explain targets, options, configurations and defaultConfiguration, and how serve reuses build through buildTarget strings.

for a senior

Reason about where each effective option comes from, including builder schema defaults, when a build behaves unexpectedly in one environment.

for a principal

Keep configurations minimal and intentional across many projects so that environment differences stay reviewable in one place.

## The shape of angular.json An Angular CLI **workspace** is described by `angular.json` at its root. Its `projects` map holds one entry per application or library, and each project lists **targets**, the tasks the CLI can run, under the `architect` key (the CLI also accepts the newer spelling `targets`). Commands such as `ng build`, `ng serve` and `ng test` are shortcuts that run the target of the same name. Every target has up to four parts: - **`builder`**: the package and builder that do the work, such as `@angular/build:application`. - **`options`**: the base option values. - **`configurations`**: named sets of overrides, for example `production` or `development`. - **`defaultConfiguration`**: the configuration applied when the command line names none. When a target runs, the CLI starts from `options` and spreads the selected configuration's values over them. Anything a configuration does not mention keeps its value from `options`, and anything `options` does not mention falls back to the builder's schema default. ## What a new project generates Angular CLI 22's application schematic writes this, abbreviated: ```json "build": { "builder": "@angular/build:application", "defaultConfiguration": "production", "options": { "browser": "src/main.ts", "tsConfig": "tsconfig.app.json" }, "configurations": { "production": { "budgets": [ ... ], "outputHashing": "all" }, "development": { "optimization": false, "extractLicenses": false, "sourceMap": true } } }, "serve": { "builder": "@angular/build:dev-server", "defaultConfiguration": "development", "configurations": { "production": { "buildTarget": "my-app:build:production" }, "development": { "buildTarget": "my-app:build:development" } } } ``` ## Why the two commands differ | | `ng build` | `ng serve` | |---|---|---| | Target run | `build` | `serve` | | Default configuration | `production` | `development` | | Build options actually used | `options` + `production` | `options` + `development`, via `buildTarget` | | Optimization | on (builder default) | off | | Output file hashing | `all` | builder default, `none` | | Source maps | off (builder default) | on | | Budgets checked | yes | no | Note what the `production` configuration does **not** contain: `optimization: true`. It does not need it, because `optimization` defaults to `true` in the application builder's schema. The `development` configuration exists precisely to switch the expensive parts off. ## How serve reuses build The dev-server target does not duplicate build options. Its configurations hold a **`buildTarget`** string in the form `project:target:configuration`, telling the dev server which build target and configuration to compile. That is why adding a new build configuration, such as `staging`, is not enough to serve it: `ng serve -c staging` also needs a `staging` entry under the `serve` target pointing at `my-app:build:staging`, otherwise the CLI reports that the configuration is not set for the `serve` target. ## Switching configurations 1. `ng build` runs `build` with `production`. 2. `ng build --configuration development` (short form `-c development`) builds an unoptimized bundle with source maps. 3. `ng serve -c production` serves an optimized build, useful for checking a production-only bug locally. 4. `ng run my-app:build:development` is the long form that names project, target and configuration explicitly. ## Command-line flags come last On top of `options` and the selected configuration, flags typed on the command line override both. `ng build --source-map` produces source maps even in the production configuration, for that one run only. Two spelling rules apply: - In `angular.json` every option is **camelCase** (`sourceMap`, `outputHashing`). - On the command line the same option is **dash-case** (`--source-map`, `--output-hashing`). The full precedence, lowest to highest, is therefore: the builder's schema default, the target's `options`, each selected configuration in order, and finally the command-line flags. The builder's schema also validates the merged result, so a misspelled option in `angular.json` is reported as an error rather than silently ignored. ## Why this matters in practice Knowing where defaults come from prevents two common mistakes: assuming `ng build` is a development build (it has been production by default since Angular CLI 12), and editing `options` when only one environment should change. Values that differ per environment belong in the named configuration; values shared by all belong in `options`.

  • Why does the generated production configuration not set optimization to true?
    The application builder's schema already defaults `optimization` to `true`, so production inherits it from the builder. The generated `development` configuration is what turns it off, together with `extractLicenses: false` and `sourceMap: true`.
  • How can you change a value in angular.json from a script without editing the JSON by hand?
    Use `ng config <json-path> [value]`. Without a value it prints the current setting; with one it writes it and validates the workspace against the schema, for example `ng config cli.cache.enabled false`. `--global` edits the user-level CLI configuration file instead of the workspace's `angular.json`.

saying these in an interview costs you the question

  • ng build produces a development build unless you pass --prod.
  • The production configuration must set optimization: true or nothing is minified.
  • ng serve builds with the production configuration by default.
  • Adding a build configuration automatically makes it available to ng serve.
  • Options outside configurations are ignored once a configuration is selected.