· Architecture

Shipping 12+ coffee brands from one Kotlin/Compose codebase

White-label Android is easy to sell and hard to maintain. Here is how we ship Barns, Kyan, Caribou and nine other live F&B apps without maintaining twelve repositories.

Why forks fail

The first white-label client always ships fast — copy the template, swap colours, publish. The twelfth client is where teams drown: twelve Play policy updates, twelve payment SDK bumps, twelve security patches. A bug fixed in Barns doesn't reach Kyan until someone remembers to cherry-pick.

Our constraint at COFE App was explicit: one Android codebase, many branded Play listings, with per-brand menu, payments, loyalty and analytics.

Layer 1 — Vendor configurators

Each brand is a build flavour plus a vendor configuration object: theme tokens, logo assets, supported payment methods, feature flags (subscriptions on/off, catering module, drive-thru), and Apollo GraphQL endpoint/schema selection.

Onboarding a new brand is primarily configuration work — not a new Git repository. The Barns case study (100K+ installs) is one flavour of this system.

Layer 2 — Feature modules, not monolith packages

Cart, checkout, order tracking, wallet top-up, rewards, referrals and coupons each live in dedicated Gradle modules with Clean Architecture boundaries: presentation (Compose + MVI), domain, data.

Benefits:

Layer 3 — Seams for things that vary by market

Two seams matter most across GCC brands:

Compose + MVI at scale

UI is Jetpack Compose with MVI-ish unidirectional flow: intents → reducer → StateFlow → composables. Order and cart state must survive process death and configuration changes — state hoisting and explicit error channels per screen, not ad-hoc LiveData in fragments (there are no fragments).

What we measure

Hiring or contracting?

Platform engineering (full-time): senior Android hire page.

White-label project delivery: freelance page · services overview.

Ahsan Islam, senior Android developer at COFE App