Shipping the mobile app 7M Cubans use to get online

Cuba's national telecom needed one mobile surface for everything a subscriber touches — account, internet quota, captive portal auth. Built the Flutter app end-to-end on top of the Spring Boot backend the team was migrating to.

Client
Etecsa (Cuban national telecom)
Role
Mobile Engineer · Backend Engineer
Year
2024
Timeline
Multi-quarter mobile build + ongoing backend migration
Flutter Dart Java Spring Boot Jakarta EE PostgreSQL Docker

Problem

Most internet access in Cuba happens through Etecsa’s Nauta service: you authenticate against a captive portal to come online, and you manage your plan, balance, and bonus hours from a separate user portal. For years that meant two desktop browser flows that didn’t share state. Subscribers needed a single mobile surface where they could log in to the captive portal, check what they had left, and manage their account — without juggling browsers, copy-pasting credentials, or interpreting raw HTML responses.

In parallel, the operational platforms behind these surfaces — serving roughly 7 million subscribers — were being rebuilt off a legacy Javax stack onto Spring Boot. The app needed to land on top of a backend that was actively moving under it.

The mobile surface

I built the Nauta mobile app end-to-end in Flutter, as the sole mobile developer. Four primary flows:

Decisions worth calling out:

The backend it talks to

The Etecsa team — myself included — migrated the legacy Javax modules behind these portals to Spring Boot, module-by-module, behind a shared API contract. The non-obvious decision: legacy Javax and new Spring Boot services ran side-by-side in the same operational network, routed by feature flag rather than by endpoint, so a single team could be moved over, validated, and only then expanded.

There was no migration weekend. There was a sequence of safe Tuesdays.

Loading diagram…
The Flutter app sits over the same feature-flag-routed backend the team migrated module-by-module

Outcome

The mobile app gave subscribers a single place for everything they used to do across two browser flows — the everyday tasks (log in, check balance, see bonus hours) became one-screen, one-tap operations. Behind it, the modules already migrated to Spring Boot showed a 3× throughput improvement against the legacy baseline. Operational staff noticed response times getting faster without anyone telling them why. Zero outages during the migration window for the modules we shipped.

The pattern of “no big migration weekend” is now the team’s default for any modernization work — and the app continues to evolve against whichever backend the flag routes it to.

Technologies used

Flutter Dart Java Spring Boot Jakarta EE PostgreSQL Docker
Visuals

Screens

Nauta app entry screen — picker between Portal de Usuario and NavegaciónCaptive portal login — credentials to authenticate against Nauta Wi-FiUser portal login with captcha — access to the personal Nauta accountAccount dashboard — plan type, status, bonus hours, balance
Keep exploring

More work