The Angular CLI is the ng command, and it does four genuinely different jobs: scaffolding files, running a development server, producing a production build, and - the one people underuse - migrating your code across framework versions.
The command reference is long and most of it is rarely touched. The working set is about eight commands, and one of them saves more time than all the others combined.
Table of contents
- Getting started and the daily loop
- Generating code, and why it is worth using
- Building for production
- The update command, which is the underused one
- Testing, linting, and the rest
- How this fits the rest of the stack
- FAQ
Getting started and the daily loop
Install the CLI globally, then create and run a workspace.
npm install -g @angular/cli
ng new my-app # create a workspace, answers a few prompts
cd my-app
ng serve # dev server with rebuild on change
ng serve --open # and open a browser
ng serve --port 4300 # when 4200 is taken
ng serve has aliases s and dev. It builds in memory and rebuilds on file changes; it is not what you deploy.
Argument names use dash-case, and a command takes an optional target followed by options - ng build my-app -c production. That pattern is consistent across the whole tool, which makes the reference easier to read than its length suggests.
Generating code, and why it is worth using
ng generate, aliased g, creates files from templates called schematics. It is not just typing-saving: it registers things correctly, follows the project’s configured style, and produces the spec file alongside.
ng g component features/user-profile
ng g service core/auth
ng g guard core/auth
ng g pipe shared/truncate
ng g module features/admin --routing
ng g component foo --dry-run # show what would happen
ng g component foo --skip-tests # no spec file
--dry-run is the flag worth remembering. It prints the files that would be created or modified without touching anything, which is how you check a path before generating into the wrong folder and then cleaning up by hand.
Building for production
ng build compiles into an output directory, defaulting to dist/. Production configuration is the default for builds in current versions, applying ahead-of-time compilation, minification, and tree shaking.
ng build # production build into dist/
ng build -c staging # a named configuration
ng build --base-href /app/ # when served from a subpath
ng build --source-map # source maps for error tracking
Two flags cause most deployment problems. --base-href must match the path the application is served from - get it wrong and every asset request 404s from a subdirectory. And build configurations are defined in angular.json, so a configuration name that works locally must actually exist for CI to use it.
The output is static files. That means any static host with a CDN serves it, and it also means the routing needs a fallback rule - every path that is not a real file must serve index.html, or a deep link refreshed in the browser returns 404 instead of loading the app.
The update command, which is the underused one
ng update is the most valuable command in the tool and the one people avoid out of caution, which is backwards.
ng update # what is available
ng update @angular/core @angular/cli # update the framework
ng update @angular/core@18 --allow-dirty # target a version
It does not just bump versions in package.json. It runs migration schematics that rewrite your code for breaking changes - renamed APIs, changed signatures, deprecated patterns. A framework upgrade that would be days of manual work is frequently a command and a review of the diff.
Two rules make it reliable. Commit everything first, so the changes it makes are visible as a clean diff. And update one major version at a time rather than jumping several, because the migrations are written to run in sequence and skipping versions skips the migrations that were meant to run between them.
Teams that avoid this end up several major versions behind, at which point catching up genuinely is the multi-week project they were afraid of. Running it every release cycle keeps it boring.
Testing, linting, and the rest
ng test # unit tests, watch mode
ng test --watch=false --browsers=ChromeHeadless # for CI
ng lint # lint the project
g e2e # end-to-end, once configured
ng cache clean # when the build behaves strangely
ng version # versions of everything, for bug reports
For CI, the important part is disabling watch mode and using a headless browser - the default configuration waits for a browser and for file changes, which in a pipeline means a job that hangs until it times out.
ng cache clean is the first thing to try when builds behave inexplicably. The persistent disk cache is a large speed improvement and occasionally holds something stale.
How this fits the rest of the stack
An Angular build is a folder of static files, which makes hosting it simple and makes the cost mostly about bandwidth and whatever API sits behind it. The RunxBuild hosting calculator prices static hosting alongside the backend service and database so the whole application has one figure. RunxBuild builds static sites straight from a GitHub repository with SPA fallback, custom domains, headers, redirects and rewrites, and includes 120GB of bandwidth at $0.10/GB after.
Useful related references:
- Deploy an Angular App for Free on RunxBuild
- WP-CLI: The Commands That Make WordPress Administration Bearable
- redis-cli: Connect, Monitor, and Cluster Mode
- Services on RunxBuild
FAQ
What is the Angular CLI?
The ng command, which scaffolds projects and files, runs a development server, produces production builds, runs tests and linting, and migrates your code across framework versions. Commands take an optional target followed by dash-case options, consistently throughout.
What does ng update actually do?
More than bump versions. It runs migration schematics that rewrite your code for breaking changes - renamed APIs, changed signatures, deprecated patterns - so a framework upgrade becomes a command and a diff review rather than days of manual editing.
Why does my Angular app 404 when deployed to a subdirectory?
Because --base-href does not match the path it is served from, so every asset request resolves to the wrong URL. Build with ng build --base-href /your-path/ matching exactly where the application lives, including the trailing slash.
Why does a deep link 404 after deploying an Angular app?
Because the build output is static files and the host is looking for a real file at that path. You need a fallback rule serving index.html with a 200 status for anything that is not an existing file, so the router can handle the URL client-side.
How do I run Angular tests in CI?
Disable watch mode and use a headless browser: ng test --watch=false --browsers=ChromeHeadless. The default configuration waits for a browser and for file changes, which in a pipeline produces a job that hangs until it times out.