OnPush Is the Default Now: v22's Biggest Behavioural Change In short: For nine years, a component that did not choose an update strategy got "check this every time, forever." In v22 that flipped. This is the change most likely to surprise you, and the failure is silent. The change Same code, different behaviour. Along with this, the old was marked as going away and renamed: is a better name. It describes what the strategy does rather than claiming it is what you should want. And it is now clearly the exception rather than the silent normal. Upgrading is safe. What comes after needs attention. walks every component in your project and writes onto any that did not choose one. So upgrading changes nothing at run time. Existing components behave exactly as before. The risk is entirely on the other side. Every component you create after that is . If the team still thinks "Angular checks everything, so changing a value just works," you get components that quietly do not update. You will also have a project where hundreds of files now say . That is a to-do list, not a finished job — and a useful one, because each entry is a component nobody ever checked. How OnPush works An component is skipped during an update unless something marks it as needing attention. Four things do: 1. An input's reference changes. The reference, not the contents. 2. An event fires from inside the component's template — , , and so on. 3. An pipe in its template produces a value. 4. A signal read in its template changes — including signal inputs. Plus calling yourself. Number 4 is why and signals fit together so well. In a fully signal-based component, is invisible: every value driving the template is a signal, every signal change marks the component, and there is no case where the screen fails to update. The strategy only bites when your values are not signals. The three bugs to expect Changing an object that came in as an input The child's input reference has not changed, so nothing marks it. Under this worked, because the component was checked anyway. The fix is to replace the object — which you should have been doing regardless: Changing an array in place Same problem, and more common: A delayed callback setting a plain property This is the one that confuses people most. Zone.js notices the callback and Angular runs an update — but the update skips this component , because nothing marked it. Fixes, best first: marks this component and its parents for the next update. updates this component immediately and skips Angular's scheduling — it is a bigger hammer and almost never what you want. If you find in app code, it is usually working around a missing signal. Migrating away from Do it in batches, starting where it is easiest: Leaf components first. A component with only signal or replaced inputs, and no delayed callbacks setting properties, is usually a one-line change. Then containers. These are where in-place changes live. Look for , , direct assignment on input objects, and delayed callbacks setting plain properties. A quick check of the highest-risk pattern: Run that second command even if you never migrate a single component. Every hit is a value that will surprise someone eventually. Check by clicking, not by reading. bugs do not crash and do not log. The only reliable check is using the component — especially the paths that update data after the first draw. Angular's MCP server has a helper for exactly this: It suggests rather than guarantees, so review everything. But on a large project it is much faster than checking by hand. "But isn't OnPush a performance optimisation you add later?" That is how it was taught for years, and why so many teams have on exactly the three components someone once measured. The better way to see it: is not an optimisation. It is a promise . It says: what this component shows depends only on its inputs and its own signals. Nothing else can change it. That is not a speed trick. It is the same discipline as fields or immutable data. The speed is a result of the promise, not the point of it. Two things follow. Why is genuinely worse, not just slower. A component that needs "check always" is a component whose output depends on something invisible — a changed object, a property set from a callback, a value that moved without telling anyone. That is not a speed problem. It is a finding-the-cause problem. When it shows the wrong thing, there is nothing written down to inspect. Why "OnPush breaks my component" is a diagnosis, not a complaint. If turning on stops a component updating, you have found a real, already-existing bug: a value changing without telling anyone. Under it worked by luck. did not create the bug. It made an invisible one visible. The related myth: "OnPush means it only updates when inputs change." Not true, and this makes people avoid it unnecessarily. Five things mark it, and the fourth — a signal it reads changing — covers essentially everything in a modern component. The architect's view…