Ssteven_copy
case_study · 05 / 06·Energy · large enterprise·delivered · production

Angular 21 Refactor.

From-scratch rebuild of an Angular 16 app to Angular 21 (Signals, Standalone Components). Migrated 150+ endpoints GET → POST for security, introduced Swagger documentation, chose a clean new repo over in-place migration.

16→21
Angular versions skipped
150+
Endpoints migrated GET → POST
1yr
Mission duration
0
Legacy bad practices carried over
Angular 21RxJSTypeScriptSignalsStandalone ComponentsJava 21Spring BootSwagger / OpenAPIGitLab CI/CDJenkinsGrafanaCheckmarxSonarQube

// 01 · the-context

The context

A critical internal administration application for a major French energy group, used daily by system administrators to manage infrastructure.

Tech-side: an aging Angular 16 codebase, inherited from partial migrations, with accumulated bad practices (monolithic components, overloaded services, weak typing, inconsistent state management). On the backend, Java with identified performance issues on certain operations (poorly optimized queries, nested loops).

The mission goal: modernize the UI to benefit from the latest Angular features (Signals, Standalone Components, new control flows), strengthen endpoint security, and lay clean foundations for future product evolution.

// 02 · the-solution

The solution

The main project I worked on autonomously: the from-scratch UI rebuild in Angular.

The key decision: new repo over in-place migration

The classic approach to migrate Angular 16 → 21 is to bump versions one by one in the existing repo. It's slow, drags inherited bad practices, and ties up a large chunk of dev time for limited gain.

I made a different call: create a brand new Angular 21 repo from scratch, start on clean foundations, and progressively port features. The reasoning: the time that in-place migration would consume wasn't worth the result. Better to invest the same budget into a modern, scalable, maintainable codebase for the long run.

Concretely, on the new repo:

  • Standalone Components everywhere (no more NgModules)
  • Signals for reactive state management
  • New control flows (@if, @for, @switch)
  • Strict TypeScript (no any)
  • Modular and reusable component architecture
// code
// Typical pattern on the new repo
@Component({
  selector: "app-import-list",
  standalone: true,
  imports: [ImportRowComponent],
  template: `
    @for (imp of imports(); track imp.id) {
      <app-import-row [import]="imp" />
    } @empty {
      <app-empty-state />
    }
  `,
})
export class ImportListComponent {
  private service = inject(ImportService);
  imports = toSignal(this.service.getImports(), { initialValue: [] });
}

Security refactor of 150+ endpoints (GET → POST)

The application had more than 150 GET endpoints passing sensitive data in query strings — visible in server logs, browser history, and exposed to third-party monitoring tools.

I led the full migration of these endpoints to POST, with payloads in the body. Along the way, I also:

  • Improved several server-side queries (filtering useful data only, pagination)
  • Set up Swagger / OpenAPI so API documentation stays in sync with the code automatically

It was more than just renaming an HTTP verb: it was an opportunity to audit each endpoint's security, fix vulnerabilities flagged by Checkmarx and SonarQube, and rationalize API contracts.

Contribution to backend optimization

I also contributed (without being the main owner) to identifying backend bottlenecks: poorly optimized queries, nested loops, Java operations that could be rewritten cleanly. Optimizations were carried out in collaboration with the backend team.

// 03 · technical-challenges

Technical challenges

Selling the "new repo" approach. This is never the default decision on an ESN mission — the culture leans toward in-place migration. I made the case: equivalent dev time, a final codebase 10x cleaner, ability to lay real scalable foundations, less technical debt carried forward.

Ensuring coherence during coexistence. During the transition, the old app kept running in parallel. New features had to integrate with the same backend APIs, with the same contracts. I documented the correspondences precisely to prevent drift between the two worlds.

150+ endpoints to migrate without breaking production. Each GET → POST = a potential behavior change on both client and server. Systematic automated tests, progressive delivery, real-time Grafana monitoring to catch anomalies within minutes.

// 04 · results

Results

  • From-scratch Angular 21 codebase: Standalone Components, Signals, control flows, strict TypeScript
  • 150+ endpoints migrated from GET to POST with secured payloads
  • Swagger documentation set up and synced with the code
  • Foundations laid for future product scalability
  • Vulnerabilities flagged by Checkmarx and SonarQube fixed along the way

// 05 · tech-stack

Tech stack

LayerTechnologies
FrontendAngular 21, RxJS, strict TypeScript, Standalone Components, Signals
BackendJava 21, Spring Boot (collaboration with backend team)
API docsSwagger / OpenAPI
Security & qualityCheckmarx, SonarQube, systematic code reviews
CI/CDGitLab CI, Jenkins
MonitoringGrafana (dedicated dashboards)

// 06 · what-im-taking-away

What I'm taking away

This mission taught me to arbitrate between the easy solution and the right solution. In-place Angular 16 → 21 migration would have been the default reflex. Starting from a new repo was more structural, riskier on internal communication — but the result is a codebase that will hold for 5 years without needing to be rewritten.

It's also this experience that gave me the taste for clean rebuilds as a freelance mission focus today: when you inherit code that's slowing the team down, sometimes the best answer isn't "let's optimize", it's "let's start over on clean foundations, carrying forward only what's worth carrying".