What it is
An internal CRM used inside the bank to manage customers and clients. It is staff software: dense, grid-heavy, used all day by people who know it well, and judged almost entirely on how quickly it responds to them.
Performance
The clearest problem was change detection. Typing a single character into one input triggered 17 or more change-detection runs across the application. That cost is invisible in isolation and brutal in aggregate — it lands on every keystroke, and in an app whose main surface is a large grid, every run means re-checking a great deal of the view.
Getting it to 3 was a matter of finding what was subscribing the application to work it did not need to do, rather than optimising any single component. The effect showed up everywhere at once: the grid rendered faster without the grid being touched, because the grid was never the cause — the number of times it was asked to re-check itself was.
Keeping it current
I also owned the framework upgrades, moving the app from Angular 14 to Angular 18 over the course of the work — periodically, one version at a time, rather than as a single migration project.
That cadence is the whole point. An application that skips upgrades does not stay still; it accumulates a migration that gets more expensive every release until it is a project nobody can schedule, and in the meantime the team is locked out of everything the framework has shipped since. Doing it periodically keeps each step small enough to absorb alongside feature work.
The rest of it
Alongside the performance work I redesigned existing screens and shipped new features. A lot of the durable value was in a reusable component layer: the same interaction patterns had been solved separately on different screens, and consolidating them made the app more consistent for its users and cheaper for the team to extend. I also mentored other developers on the team.
There is no public link — this is internal software and it stays that way.