Bootstrapping and DI in the v22 Era In short: With modules gone and guards reduced to functions, your whole app setup fits in one list. That list is worth learning, because it is now the only place to find out what an Angular app is made of. The app config The normal pattern is a separate , which keeps to one line: Everything your app is set up with fits on one screen. Compare that with tracing → → → three calls just to answer "do we have interceptors?" The provideX / withX pattern The pattern is the same across Angular: a function sets up a feature, and takes optional functions for extras. This looks like a style choice and it is not. Each is a separate function, so the build tool can see which ones you called. If you never call , that code is not in your app. could never do that — a module refers to all its features, so all of them shipped, and options were just switches flipped at run time. This is the clearest example of the idea behind this whole chapter: a function can be combined and trimmed. A class registration cannot. Where providers can live Four levels, from narrowest to widest: Route-level providers are the under-used one. They give you a service tied to a feature, created when you arrive and destroyed when you leave — without a module. Exactly what a lazy module's used to give you, but attached to the route instead of to a class you had to invent. One warning, recently fixed: services registered at route level were not always destroyed when you navigated away. v21.1 added a way to opt in: Worth turning on if you use route-level state. Worth knowing about if you are chasing a memory problem in an app that does not. — new in v22 is by far the most common case, so v22 gave it a shorter name: is app-wide by default. To register it somewhere else instead, opt out: is not going away. is simply a clearer default for new code — and makes "this is deliberately not shared" obvious. Before, a bare with no looked like something someone forgot, whether or not it was. — new in v22 Normal needs the service to already be in your downloaded code. lets a service be downloaded separately: The heavy PDF library is not in the first download. It arrives the first time someone exports. The obvious cost is a delay on that first click. fixes it — download while the browser is idle, so the code is usually already there: This is the service version of in templates. Both delay code , both take prefetch triggers, and both exist because loading on demand stopped being something only routes could do. Use it for genuinely heavy, genuinely optional things — a PDF maker, a chart library, a rich text editor. Using it for a normal service just adds a promise for no gain. Injection tokens Unchanged, and still the right answer for settings: The option means the token has a default and needs no registration. The architect's view The pattern is a bundling decision, not a style one. Each is a separate function, so the build tool can see which ones you actually called. Features you do not ask for are not included. A module referenced all of its features, so all of them shipped and options were switches flipped at run time. When you are deciding whether a pattern is worth adopting, that is the kind of difference to look for: does it change what the compiler and bundler can know , or only how the code reads? Choosing a provider level. This is the decision juniors get wrong most often, and the cost is either wasted memory or surprising shared state: Level One instance per Reach for it when --- --- --- / The app Stateless services, API clients, shared caches Route The route subtree Feature state that should not outlive the feature Component Component instance Per-instance state — a draft, an editor session Route-level providers are the under-used one. They give you a service created on entry and destroyed on exit, scoped to a feature, with no module. Before standalone, getting that meant a lazy-loaded . The scalability argument: app-level state accumulates and is never released, and on a long-lived single-page app that grows all session. Feature-scoped state is released when the user leaves. If you use route providers heavily, turn on (v21.1), which fixes cases where those injectors were not destroyed on navigation. When to use . Only for dependencies that are both genuinely heavy and genuinely optional — a PDF generator, a charting library, a rich-text editor. Two questions before reaching for it: 1. Is this large enough that its absence changes the initial download meaningfully? 2. Will a meaningful share of users never trigger it? If either answer is no, you have added a promise and some complexity for nothing. And when you do use it, pair it with — otherwise you have moved the wait to the moment the user clicks, which is where they notice it most. On versus . is not deprecated, so this is a readability choice rather than a migration. The genuine gain is that states "this is deliberately not shared" — where a bare with no looked like a…