Skip to main content
ENGINEERING WALKTHROUGH

How FullCourt was built

A public NBA research application by Michael Ju. Follow one finding from source data to a working interface.

The problem

“They looked tired” is easy to say after a loss. Checking that idea requires schedules, results and a fair comparison group.

FullCourt brings those records together so a fan can inspect the conditions and see how much the historical pattern actually supports.

ScopeThis is a research application. Its comparisons do not isolate what rest caused, and its fatigue score is not a complete forecast of a winner.

How a record reaches the screen

  1. Collect

    NBA and other documented sources feed Python and TypeScript pipelines.

  2. Calculate and validate

    PostgreSQL records, offline models and generated research artifacts.

  3. Inspect

    Next.js pages with filters, comparisons, source records and shareable views.

Sources and coverage →

Three implementation decisions

Calculate schedule load before a visitor opens a page

Problem
A single game depends on both teams’ recent games, travel and rest. Repeating that work for every visitor would tie page delivery to historical calculations.
Built
The pipeline computes fatigue scores on the write path. Game pages read stored scores and attach the schedule facts needed to explain them.
Tradeoff
Reads stay simpler. A change to the calculation requires a deliberate data rebuild, and missing stored scores must remain visibly unavailable.
Read the fatigue engine src/lib/fatigue.ts ↗

Choose delivery around how the data changes

Problem
A game slate, a historical player table and an NBA report do not need the same loading strategy.
Built
Database-backed views serve games and season comparisons. Shooting and other research views use generated artifacts. Officiating fetches each report’s detail when it is opened.
Tradeoff
Published research remains reproducible, with smaller initial report payloads. Refreshing it requires validation and deployment rather than an invisible live replacement.
Read report loading src/components/officiating-report.tsx ↗

Make the evidence travel with the link

Problem
A shared finding is hard to inspect if its season, filters and selected game disappear when someone opens it.
Built
Selected views use URL state. Reports retain NBA source links and verdict text. Method articles preserve section anchors and open linked evidence.
Tradeoff
URLs require validation and reliable browser-history behavior. The payoff is a view that another person can inspect and revisit.
Read sharing and recovery tests e2e/officiating.spec.ts ↗

What gets checked

  • Calculations and artifacts

    Unit and Python contract tests check calculations and published artifacts.

  • Browser behavior

    Browser tests exercise filters, keyboard behavior, deep links and failure recovery. The suite runs separately against a populated environment.

  • Every change

    CI runs lint, types, tests, the production build and a dependency audit.

Not enough alonePassing automated tests is only part of verification: visible claims still need to match the source records.

Verification workflow ↗

A lesson from reviewing the product

A usability review found three problems.

  • A schedule-density approximationIt was presented as an exact game count. The label now distinguishes the underlying facts.
  • Altitude carryoverIt was described as a current location. The label now distinguishes the underlying facts.
  • Mobile month controlsThe controls worked, but the selected month was outside the visible area.

A passing calculation test or accessibility scan cannot establish that a sentence is accurate or a control is easy to use. Check the complete path from record to interpretation.

What remains uncertain

  • Historical comparisonsThey include differences in team strength, venue and other conditions.
  • Player rest splitsThey can be noisy.
  • NBA officiating reportsThey cover selected late-game situations.

FullCourt keeps those limits beside the findings and preserves tests that did not support a clear effect.

Next checkObservation: can a first-time visitor find a game, interpret a comparison and locate its source without help?

Read the evidence and limits →