Skip to content

Good Fights

A fight-tracking app for combat-sports fans. It's live on iOS, Android and Web, and is kept current by automated systems.

Role
Founder · sole developer
Platforms
iOS · Android · Web
Stack
React Native (Expo), Node/Fastify in TypeScript, PostgreSQL + Prisma, Next.js, Docker on Render, a Linux VPS for the daily automations and live trackers, Cloudflare R2
Good Fights home screen: events for Saturday September 12th, each card carrying its poster, start time and broadcaster, with the UFC Fight Night card expanded to show four hype-rated fights scored 7.5 to 9.2.An event page: the UFC Fight Night Silva vs Delgado poster, a written preview, an add-to-calendar action, Canadian broadcast listings for Sportsnet+ and Paramount+, and the main card with a hype score beside every fight.The rating sheet: How hyped are you for Silva vs Miguel Delgado, scored 8 out of 10 on a colour-graded flame scale, with a box for why and a link to the comments.

The problem

Film fans have Rotten Tomatoes. Fight fans had nothing - no place to rate an individual fight, see what the crowd thought, or see what's hyped in upcoming weeks.

Fighting Tomatoes, my earlier web version, proved people wanted this. Good Fights is the mobile-first successor, with advanced functionality and user reach.

What shipped

  • iOS and Android apps from one React Native codebase.
  • Migrated the legacy Fighting Tomatoes accounts onto the new platform. Good Fights users have submitted 83,957 fight ratings on 15,789 fights dating back to 1993.
  • A REST API in TypeScript on PostgreSQL, shared by the apps and the Next.js web version at goodfights.app.
  • Claude-based enrichment that writes fight, fighter and event detail, gated on a confidence score so nothing the model is unsure about is published - with unit tests that check its output, including one that verifies quoted material is real.
  • More than 30 scheduled jobs on the VPS, each with per-job locking and failure alerts, running the scrapers, Brave Search lookups, Claude API enrichment, database backups, deduplication and content-freshness checks.
  • A Remotion video pipeline that renders promo clips from live database data, with generated voice-over, plus an automated content system that writes an SEO-focused preview and results article for every numbered UFC card and refreshes a monthly fighter-rankings article from live data.
  • Over-the-air updates so fixes reach users without a new store submission.

How it fits together

Mobile app and web front-ends talk to a REST API backed by PostgreSQL; daily automations (scrapers, Brave Search and Claude API processing) feed the database.Mobile appReact Native · ExpoWebNext.js · goodfights.appREST APINode · Fastify · TSDatabasePostgreSQL · PrismaDaily automationsscrapers · Brave Search · Claude API

The big challenges

Live trackers that stay accurate

Good Fights promises to let users know exactly when the fight they want to watch is about to start. This means tracking events while they are live to determine exactly when each fight ends and the next one starts, and doing this consistently across 15+ organizations, week in, week out. So I built trackers that handle complex and changing data sources, degrade gracefully and alert me when there are any issues.

Clear the moment you open it

A first-time user has to grasp what they are looking at and what to do, without having to figure it out. This meant limiting feature creep and creating a UI optimized for immediate, intuitive understanding.

Developing functionality users actually want

Identifying valuable use cases for Good Fights and building clean solutions. Good Fights started as rating historic fights only, but has expanded to include hyping upcoming fights, comments, fighter walkout notifications and more. Each of these was built out in response to user feedback.

Outcome

81,208
fight ratings submitted by users
1,679
written reviews
16,265
fights catalogued across 1,670 events