Back to home
The solo-founder story · فزعة

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
Fazea — app launch

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.

From the app
Fazea — discover games near you on the map
Fazea — set up a match in a few taps
Fazea — game details on the map, join in a tap
Fazea — interactive team formation and lineup
Fazea — post-game props and MVP voting
Fazea — confirm the game happened
01

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.

02

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.

Try it — it's live

Bench

SportConfig {
  sport: "football",
  pitch: "full",
  slots: 6,
}

Switch sport, drag players between slots. The whole board is built from one config object.

03

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 TeamStanding table 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.

Try it — it's live

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.

04

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.

Fazea — set up a match in a few taps
English · LTR
فزعة — نظّم مباراة في بضع لمسات
العربية · RTL

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.

What this demonstrates

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.