Article

Breaking Down Big Services: Our Strategy for Migrating GWT to React

20 January 2026

By Gytenis Mikulėnas, Marius Grybė, Vijay Chakravarthy, Mambu Engineering Team

In the world of enterprise software, a "rewrite from scratch" is rarely a viable option. At Mambu, our Google Web Toolkit (GWT) legacy technology served us well for years. It allowed us to write backend and frontend code in a single language (Java) within a single repository.

However, as our services have grown in size and complexity, this tight coupling is becoming a bottleneck. To improve build times and accelerate UI feature delivery, we identified the need to decouple our frontend from these heavy services, and also to migrate from GWT RPC to JSON.

We long-wanted to decouple our frontend to ship faster, but we needed a strategy that avoided the risks of a "Big Bang" migration. Thus we’ve chosen to adopt the Strangler Fig pattern — to gradually keep replacing old UI modules/features until the whole frontend is moved away.

Here is the architectural roadmap we are executing to transform our GWT services into a hybrid "Micro-frontend Shell" using React.


The Strategy: GWT as the Host, React as the Guest

We are adopting an approach where an existing GWT application acts as the "Host." It continues to manage the lifecycle, routing, and authentication. We are building new UI features—and migrating existing ones—as "Guest" React applications that the Host loads dynamically.

Our execution plan rests on three architectural pillars:


  • Decoupling the Business Core

Our first step is ensuring our backend is agnostic to the UI technology. We are refactoring our service layers to support dual protocols:

  • Layer Separation: We are actively extracting business logic from GWT Servlets into pure Java Service classes. These classes contain strictly business logic and have no awareness of HTTP or UI contexts.
  • Dual Exposure: While the GWT Servlets continue to handle legacy RPC calls, we’ve chosen to simultaneously introduce new JSON-based endpoints that utilize the shared Service business logic underneath and expose the same models.
  • The Goal: This ensures that as we build new React components, they use the exact same business rules as the legacy views, eliminating logic duplication.
  • The Integration Layer: FFI for the Browser

To render React inside GWT, we had to demystify the "magic" of mixing Java and JavaScript. We treat this strictly as a standard Foreign Function Interface (FFI) problem. Just as you might use JNI to call C++ from Java, we use GWT's tools to break the compiler sandbox and access the global browser scope.

This solves the "Two Worlds" problem:

  • World A (GWT/Java): Lives in an obfuscated sandbox where variables are renamed (e.g., a.b.c()).
  • World B (React/JS): Lives in the global window scope, waiting to be called.

We bridge these worlds using the following mechanism:

  • The Handshake (JsInterop): While older GWT versions used JSNI (writing raw JS strings inside Java), we rely on JsInterop (GWT 2.8+). This allows us to define a Java interface that maps to the global JavaScript object—effectively a "TypeScript definition file" written in Java.
  • The Mounting Flow:
  • Allocating the Slot: GWT creates a SimplePanel (a <div>) and assigns it a unique ID.
  • Pointer Passing: GWT passes the ID string to the global React entry point via the JsInterop bridge.
  • Rendering: React finds the element by ID and takes control of the DOM node.
  • The "Memory Leak" Trap: A critical engineering challenge we faced is that GWT and React have different lifecycles. If GWT removes the container <div> (e.g., on navigation), React is unaware and keeps event listeners attached to the detached node.
  • The Fix: We enforce a bidirectional cleanup. When GWT triggers onUnload(), we explicitly call a corresponding unmount function in React to clear the Virtual DOM and prevent memory leaks.

The JsInterop Contract:

GWT to React
  • Safety Through Feature Flagging

To manage the risk of this transition, we are strictly enforcing feature toggles for every migrated screen.

  • Runtime Evaluation: The GWT entry point of loading UI modules is being updated to check a feature flag service before rendering.
  • Traffic Routing: Based on the flag status, GWT entry point decides whether to load the legacy GWT view or to inject a new React bundle with new UI modules.
  • Mitigation Strategy: This allows us to push new React screens to production while retaining the ability to instantly rollback to the legacy view via a server-side switch if issues arise.

Why We Are Taking This Path

This strategy aligns with industry best practices for modernizing legacy systems, providing us with:

  • Incremental Progress: We can modernize one screen at a time, avoiding the "migration fatigue" associated with multi-year rewrites.
  • Stability: The proven legacy system remains the fallback, ensuring business continuity.
  • Developer Velocity: By moving to React and TypeScript, we are unlocking modern tooling (like Hot Module Replacement) that will significantly speed up our future UI development.

Accelerating with AI

  • We recognize that the manual translation of GWT boilerplate to React is a labor-intensive process. To address this, we are currently heavy users of JetBrains Junie AI and are actively experimenting with workflows to accelerate this migration.

We are exploring how AI agents can handle the bulk of the syntax translation, allowing our engineers to focus on architectural alignment and user experience improvements. We plan to share insights from these experiments in a future update.

Share this post