The situation
The group’s customer service portal had run on AngularJS 1.5 since 2016, inside a Java monorepo, built with tooling that no longer ran on a current Node version. One application served two installations on separate backends, and the larger one hosted a growing set of business-unit portals, each with its own branding, languages and access rules.
The constraint that shaped everything
Some decisions had to be fixed at build time: which backend to call must be known before the first request, login included. Others could only be made at runtime, because one build serves every portal and the active one changes as the user navigates. The legacy app blurred the two. We separated them: one build per environment, the portal resolved at runtime.
Fixed at build
→ 6 builds: three environments × two installations
Resolved at runtime
→ Any number of portals, one build
How we worked
We ran the technology assessment, set up the project and its architecture, then led a two-person frontend team through delivery.
Every feature started as a written specification in the repository (requirements, technical design, UI) before any code existed. AI agents implemented against those specs, and every change went through human review. Seshat, our internal toolkit for spec-driven agentic development, keeps the specs, the code and the decisions in step.
A migration guide mapped each legacy pattern (role hierarchy, route authorisation, session handling, HTTP interception) to its modern equivalent, so neither people nor agents had to reverse-engineer the old code twice. Login, the ticket list with filters and export, and ticket creation were running within the first two weeks.
Result
In production five months after work started. About 60% less JavaScript on first load, because only the first screen loads upfront and the rest arrives on demand. The specifications stayed in the repository, with the client’s team.