With the Angular CLI, why does ng build produce an optimized, hashed bundle while ng serve does not, in a newly generated angular.json?
answer
- targets, options, configurations
- each target names a default
- build defaults to production
- serve points at a build configuration
basics
~10 sIn 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# 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:developmentgo deeper
Recall that ng build defaults to production and ng serve to development, and that -c picks another named configuration.
Explain targets, options, configurations and defaultConfiguration, and how serve reuses build through buildTarget strings.
Reason about where each effective option comes from, including builder schema defaults, when a build behaves unexpectedly in one environment.
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.