@Ankit Blog
Angular Blog
Upgrading Angular Applications: Lessons from v12 to v18 at OpenText
profileAnkit Raj
22nd February, 2025Calendar

Introduction

At OpenText, I've been part of teams that upgraded multiple enterprise Angular applications from an IoT Platform (v14 → v18), a Shipment Tracking dashboard (v12 → v15), and an Ops Console (AngularJS → Angular v16). Each migration surfaced its own set of pitfalls, performance wins, and architectural decisions. This post distils those lessons into a practical playbook.

Why Bother Upgrading?

Staying on an older major version means missing out on:

  • Standalone components (v15+) eliminates NgModule boilerplate
  • Signals (v16+) fine-grained reactivity without Zone.js overhead
  • Esbuild/Vite builder (v17+) up to 87% faster cold builds
  • Deferred loading @defer (v17+) lazy load UI blocks declaratively in templates
  • Long-term security patches and official LTS support

Migration Strategy: One Major at a Time

The Angular team mandates sequential upgrades you cannot jump from v12 → v18 in one shot. The ng update command handles most of the mechanical changes, but knowing what it won't fix is where the real work begins.

The pattern that worked for us:

  1. Run ng update @angular/core @angular/cli for each major
  2. Fix all compiler errors before moving to the next version
  3. Run full E2E and unit test suites at each step
  4. Review the Angular Update Guide for the specific version pair

Common Pitfalls We Hit

1. Deprecated HttpClientModule

From v15+, HttpClientModule in AppModule is replaced by the standalone provideHttpClient().

1// ❌ Old (v14 and below)
2imports: [HttpClientModule]
3
4// ✅ New (v15+)
5providers: [provideHttpClient(withInterceptorsFromDi())]

2. Removed ComponentFactoryResolver

Dynamic component creation via ComponentFactoryResolver was fully removed in v16. The modern API uses ViewContainerRef.createComponent() directly.

1// ❌ v14
2const factory = this.resolver.resolveComponentFactory(MyComp);
3this.viewRef.createComponent(factory);
4
5// ✅ v16+
6this.viewRef.createComponent(MyComp);

3. ngcc Gone in v16

The Angular Compatibility Compiler (ngcc) is removed in v16. Any third-party library that hasn't published Ivy-compatible builds will break. We audited all 60+ dependencies before starting the v16 hop, and pinged maintainers of 3 internal libraries to ship updated builds.

4. Zone.js & Change Detection in v17+

Signals drastically change the mental model. We migrated a few performance-critical components to usesignal() + computed(), removing manual ChangeDetectorRef.markForCheck() calls entirely.

1// Signal-based component (v17+)
2@Component({ changeDetection: ChangeDetectionStrategy.OnPush })
3export class DashboardComponent {
4  count = signal(0);
5  doubled = computed(() => this.count() * 2);
6
7  increment() {
8    this.count.update(v => v + 1); // no markForCheck needed
9  }
10}

Spring Boot Microservices Integration

Our Angular apps talk to Spring Boot microservices. A few integration notes that saved us debugging time:

  • CORS: With standalone components and new bootstrapping, ensure your Spring Boot @CrossOrigin config covers the correct origins. The Angular dev proxy changed its default port in v17.
  • Interceptors: HTTP interceptors with withInterceptorsFromDi() still work fine with Spring Security JWT validation no backend change needed.
  • SSE / WebSockets: We use Server-Sent Events from Spring Boot for real-time IoT data. The Angular EventSource wrapper is unchanged across versions.
1// Spring Boot CORS config
2@Configuration
3public class CorsConfig implements WebMvcConfigurer {
4    @Override
5    public void addCorsMappings(CorsRegistry registry) {
6        registry.addMapping("/api/**")
7            .allowedOrigins("http://localhost:4200", "https://your-prod.domain")
8            .allowedMethods("GET", "POST", "PUT", "DELETE");
9    }
10}

Before / After Metrics

Measured across the IoT Platform (v14 → v18) upgrade:

MetricBefore (v14)After (v18)
Cold build time~3m 20s~26s (esbuild)
Hot reload (HMR)4–8s< 500ms
Initial bundle size2.1 MB1.4 MB (−33%)
Lighthouse Perf Score6184
Unit test suite time~4m 10s~2m 30s

CI/CD: TeamCity → GitLab Pipelines

We also migrated CI/CD from TeamCity to GitLab as part of this effort, reducing manual pipeline configuration by ~40%. Here's a minimal GitLab CI job for an Angular v18 project:

1# .gitlab-ci.yml
2stages: [install, test, build]
3
4install:
5    stage: install
6    script: npm ci --legacy-peer-deps
7    cache:
8        key: $CI_COMMIT_REF_SLUG
9        paths: [node_modules/]
10
11test:
12    stage: test
13    script: npx ng test --watch=false --browsers=ChromeHeadless
14
15build:
16    stage: build
17    script: npx ng build --configuration production
18    artifacts:
19        paths: [dist/]

Conclusion

Angular upgrades at enterprise scale aren't glamorous, but the gains are real faster builds, smaller bundles, and a codebase that developers actually enjoy working in. The keys: upgrade one major at a time, fix all compiler errors before moving on, and invest in a solid test suite so you're not flying blind.

If you're staring down a similar migration, feel free to reach out happy to share our internal runbook or answer questions.

Thank You For Reading 🙏

JWT Blog
Date 21st Nov, 2021
Authentication API with JWT Token
Authentication and authorization are used in security, particularly when it comes to getting access to a system. Yet, there is a significant distinction between gaining entry into a house (authentication) and what you can do while inside (authorization)...
Read More › › ›
©2026 Design & Developed By
Ankit Raj | All Rights Reserved.