BandaBets
One frontend platform, six countries, shipped by a team that stopped colliding
- Nuxt
- Vue 3
- TypeScript
- Node.js
- Docker
- Kubernetes
25%
faster shipping
- Role
- Frontend architecture · backend services
- Period
- 2024 — 2026

Context
BandaBets runs in six countries — Kenya, Nigeria, Uganda, Zambia, Malawi and the DRC — off a single frontend platform. Each market brings its own currency, its own payment providers, its own regulator and, in several cases, its own language.
The codebase had grown the way frontends do: one Nuxt application that started small, accumulated a sportsbook, a casino, a payments flow, an account area and six markets' worth of localisation, and never got reorganised along the way.
The problem
Two problems, and they compounded each other.
The deployment unit. Everything shipped together, so everything carried the risk of everything else. A one-line copy change meant rebuilding and redeploying the whole platform. That produces a predictable spiral: releases get batched because they're risky, batches get bigger, bigger batches are riskier, and shipping slows until the frontend is the bottleneck for work that was finished days ago. Two engineers touching different features regularly collided in the same files.
The markets. Multi-market platforms usually fail in one of two directions. Fork per market and you get six codebases drifting apart, where every bug fix is six pull requests and the sixth one gets forgotten. Hardcode one market and bolt the others on, and every new country becomes an archaeology expedition through assumptions nobody wrote down — that the currency has two decimal places, that phone numbers start with a particular prefix, that the copy fits the button.
The goal was one deployable platform where a market is a configuration, not a branch.
Architecture
The split followed the product's natural seams rather than a technical taxonomy:
- Sportsbook — fixtures, odds, bet slip, live updates
- Casino — game catalogue, provider integrations, session handling
- Wallet & payments — deposits, withdrawals, transaction history
- Account — auth, profile, verification, responsible-gaming controls
- Shared core — design system, i18n, session context, API client
Each domain became an independently buildable and deployable Nuxt layer with its own routes and components. The shared core was extracted into a versioned internal package, so common UI and the API client had exactly one definition rather than several slightly diverged copies.
Layers were the right primitive here specifically because they compose at build time — the user still gets one coherent application and one navigation, not six apps stitched together with full page reloads.
Market differences live in configuration: currency and locale per deployment, payment providers resolved per market, and locale bundles loaded on demand so a user in Nairobi never downloads French copy. Layouts were built to survive translation — Swahili strings routinely run longer than the English they replace, and a fixed-width button is a rendering bug waiting for a translator.
What I built
- The module boundaries, and the migration path that moved existing code across them without freezing feature work — the split happened incrementally alongside ongoing delivery.
- The shared design-system and i18n package, covering English, Swahili and French.
- Per-module CI pipelines, so a change to the casino builds and deploys the casino.
- Backend services supporting the split — API contracts scoped per domain instead of one endpoint returning everything any screen might need.
- Rendering strategy per route: static generation for marketing and catalogue pages, server-side rendering for anything session- or odds-dependent.
Impact
- Shipping time down 25%. Mostly structural rather than clever: smaller deployment units mean a smaller blast radius, which means less batching and less pre-release ceremony.
- Multiple engineers work in parallel without merge collisions in shared files.
- Acquisition routes prerender to static HTML — a real difference on the mid-range Android devices that make up most of the traffic.
- Adding a market became configuration and a provider integration rather than a fork.
What I'd revisit
The shared core is doing a lot of work, and shared packages have gravity — every convenience added to one makes it harder to change. It needs an explicit rule about what earns a place in it, enforced in review rather than left to judgement, or it slowly becomes the monolith it replaced.
Got a system that needs to hold up
under real traffic?
I'm looking for full-stack and backend roles where the hard part is the load, the latency or the money moving through it. Happy to talk through anything you're building.