Fazea
Rally Your Game
Designed, built, and shipped a multi-sport meetup app to both app stores — alone. It's live on both stores, and I owned every part — the brand, the product, the React Native app, the Django backend, the CI/CD, and the releases.
- Role
- Solo founder · Product · Full-stack · Release engineering
- Where
- Global · Live on iOS & Android
- React Native
- Expo
- TypeScript
- Django
- PostgreSQL

Why “Fazea”
The name comes from the Gulf tradition of فزعة (fazʿa) — answering the call when people need you, showing up. That's the whole product in one word: someone needs one more player, you show up. The brand is styled FAZ3A with a reversed “E” as a flourish, and the taglines are *“Rally Your Game”* and *“Answer the call.”* Naming it this way was a product decision, not a cosmetic one.
The problem
Pickup sports coordination lives in scattered WhatsApp groups: no reliable way to find a game near you, no way to know if enough people are actually coming, no history, no fairness. Fazea turns that into a real product — location-based discovery, live rosters, teams, and a competitive layer that makes results mean something.
What makes it technically interesting
Fazea supports eleven sports — football, basketball, volleyball, tennis, padel, cricket, rugby, handball, badminton, baseball, and table tennis — each with its own rules, formats, and pitch. The naive way to build that is eleven copies of every screen. I built it the opposite way.
The SportConfig architecture — one engine, eleven sports
The core of the app is a single config-driven screen engine. Instead of per-sport screens, there's one SportConfig object per sport that declares everything variable — team format, pitch shape, placement rules, feed sections, card layout — and one shared engine that renders against it. The same pattern recurs across formation screens, game cards, and the home feed's typed sections.
The payoff is extensibility: adding a twelfth sport is mostly a new config object, not a new feature branch. It's the difference between a template and a system — and it's the decision I'm proudest of, because it's what let one person ship eleven sports without the codebase collapsing under copy-paste.
Cricket is deliberately flagged as architecturally unique and left open — an honest edge the config model doesn't fully absorb yet, rather than a forced fit.
The interactive formation / lineup screen
The hardest interaction to build. Both teams share one split pitch. Players drag from a collapsible vertical bench onto slots; dropping onto an occupied slot swaps the two players. Per-sport logic — format rounding, goalkeeper and fullback auto-placement, half-court vs. full-court — is all driven by the same SportConfig layer, so the drag-and-drop engine itself stays sport-agnostic. It renders correctly in light and dark, LTR and RTL.
Bench
SportConfig {
sport: "football",
pitch: "full",
slots: 6,
}Switch sport, drag players between slots. The whole board is built from one config object.
Team vs Team — making results mean something
A competitive layer built on a TeamMatch model that owns the full match lifecycle: status states, roster quota enforcement, and results. Fairness is the hard part, so I designed for it explicitly:
- Elo ratings seeded at 1200, with K=32 for a team's first 30 matches and K=16 thereafter — high volatility early so new teams find their level fast, then stability.
- Cross-team result voting so a score isn't self-reported by one side — both teams confirm the outcome.
- MVP voting after matches.
- Standings live in a separate
TeamStandingtable rather than being denormalized onto the team model, keeping ranking independent of team identity.
The data model is defensive by design: partial unique constraints, PROTECT on delete for sport and captain foreign keys, and sport-derived roster defaults, so the competitive record can't be silently corrupted.
Falcons
1200
· K=32 · 0 matches
Panthers
1200
· K=32 · 0 matches
Elo expectation, K=32 for the first 30 matches then K=16. Let the underdog win and watch the bigger swing.
Real-time & location — the “it's actually live” layer
Real-time chat and live game state keep everyone in sync, and discovery is location-based so you find games near you. These are the features that make Fazea feel like a product people use, not a demo.
Localization & theming, as a first-class concern
Fazea ships in English, Arabic, French, and Spanish, with full RTL support and light/dark theming throughout. RTL wasn't bolted on — the layout system and the formation engine were built to mirror correctly from the start. With a global, multilingual user base, first-class Arabic and RTL aren't a nice-to-have — they're core to the experience.


Full lifecycle ownership
I owned the whole path end to end: brand and UX → React Native + Django development → CI/CD on GitHub Actions → store releases on both platforms → beta iteration through TestFlight and Play Internal Testing, folding feedback back into the product. Shipping to two app stores and maintaining the release pipeline solo is its own skill set, separate from writing the code.
One person can take a real product from a naming idea to a live, localized, cross-platform app on both stores — with an architecture designed to scale to more sports, a competitive system designed to be fair, and a release pipeline designed to keep shipping.




