Jet Callen

Shipping Fast: Lessons from Building a World Cup Predictor

·5 min read
buildingshippingnextjssupabaseworld-cup

The goal

With the 2026 World Cup coming to North America in 71 days, I wanted to build something fun that my friends and I could use to predict brackets and compete with our (terrible) football knowledge. Simple goal: make bracket predictions, share them, see who gets it right.

What started as a weekend side project turned into a case study in shipping fast and iterating in public.

The stack

I picked tools that would let me move quickly:

  • Frontend: Next.js 14 with App Router, TypeScript, Tailwind CSS
  • Backend: Python Flask + SQLAlchemy (familiar territory)
  • Database: PostgreSQL via Supabase (instant setup)
  • Auth: Supabase Auth (email/password + Google OAuth)
  • Hosting: Vercel for frontend, Railway for backend
  • Analytics: Built-in crowd prediction analytics

The stack matters less than the shipping velocity. I chose tools I could build fast with, not necessarily the "perfect" technical choice.

27 features in 3 weeks

Here's what made it to production:

Core bracket functionality:

  • Interactive tournament bracket builder
  • 8-team knockout format with automatic winner progression
  • Real team data from all 48 qualified nations
  • One-click randomize and reset

Social features:

  • Share brackets via unique URLs (/b/{slug})
  • Social media cards with dynamic OG images
  • Mobile-responsive design

Group stage predictions:

  • Pick winners for all 48 group stage matches
  • Live standings calculated from your picks
  • Share group predictions separately (/g/{slug})

Authentication & personalization:

  • Email/password signup + Google OAuth
  • Session claiming (anonymous brackets link to new accounts)
  • Personal bracket history

Analytics & competition:

  • Global leaderboards
  • Friend group competitions
  • Crowd prediction wisdom (aggregate picks from all users)
  • Real-time statistics on most/least popular picks

Polish & monitoring:

  • Countdown timer to tournament kickoff
  • Health monitoring dashboard
  • Automated QA workflows
  • SSR for shared bracket links (proper previews)

The development process

I tracked everything in GitHub issues — 27 total, all now closed. Here's what worked:

Build in small, shippable chunks

Instead of planning a massive v1, I shipped the MVP bracket builder first. Then added features incrementally:

  • Week 1: Basic bracket + sharing
  • Week 2: Group stage picks + auth
  • Week 3: Social features + analytics

Each feature was a separate PR, deployed immediately. No "big bang" releases.

Use AI agents for velocity

I paired with Claude Code for complex features like authentication integration and database migrations. The AI handled boilerplate, I focused on product decisions. This probably 3x'd my development speed.

Real data from day one

I didn't build with fake data. I scraped real World Cup 2026 team data, group assignments, and match schedules. Working with real constraints forces better design decisions.

Deploy early, deploy often

Both frontend and backend auto-deploy on every push. The live site was available from day 2. Friends could test features as I built them, giving real feedback instead of theoretical concerns.

Technical decisions that paid off

Supabase for instant backend services

Setting up auth, database, and real-time features took hours, not days. Supabase's client libraries handle all the complexity — I just write SQL and frontend code.

Flask for the prediction engine

While I used Supabase for data storage, the bracket logic and analytics run through a custom Flask API. Python was perfect for tournament simulation and statistical analysis.

Dynamic OG images

Every shared bracket generates a custom social media preview image showing the bracket picks. This turns shares into mini-advertisements for the app.

Things I'd do differently

Start with friend groups

The most-requested feature is private groups for friends to compete. I built global leaderboards first, but people want to compete with people they know. Social features should come before global features.

Better mobile experience

Tournament brackets are hard to display on mobile. I made it work, but the experience feels cramped. Next time I'd design mobile-first for this type of data visualization.

More aggressive caching

With 48 teams and hundreds of matches, I'm making too many database queries. A proper Redis cache layer would improve performance significantly.

The shipping mindset

Building this reminded me why I like alter egos for side projects. Under my real name, I'd be overthinking the tech choices, worrying about the code quality, second-guessing the feature set.

As Jett, I just built and shipped. Is it perfect? No. Does it work? Yes. Will friends use it? We'll find out in 71 days.

The best projects feel slightly embarrassing to ship. If you're not a little worried about showing your work, you probably waited too long.

What's next

With the core features done, I'm focused on content and growth:

  • SEO optimization for World Cup keywords
  • Email capture for tournament reminders
  • Social media content about predictions and tournament facts
  • Maybe a mobile app if web usage is strong

But the real test comes June 11th when the first match kicks off. Until then, it's live at wc2026predictor.com if you want to make your predictions.

71 days and counting.