← all writing

Matching 112 students to 112 families without a spreadsheet relay

Every August, my school’s international program matches incoming students with host families. It used to be a spreadsheet relay: columns of notes, tribal knowledge, weeks of email. This is the story of the app that replaced it — and the design decisions that mattered more than the code.

Decision one: recommend, don’t decide

The pairing recommender scores every unpaired student against every eligible family on a 100-point model: age proximity to kids in the house (20), shared interests (30), cultural proficiency (25), prior hosting experience (15), capacity readiness (10). Hard constraints — gender mismatches, families at capacity, pairings that failed in prior years — are excluded automatically.

But it never auto-pairs. The coordinator (me) makes every final match; the system’s job is to make each option explainable — here’s the score, here’s the breakdown, here’s why these three rose to the top. A tool that decides is a black box nobody trusts; a tool that explains is a colleague.

The recommender in action: each unpaired student gets ranked family suggestions with scores and the reasoning behind them — age fit, interests, cultural fit, capacity. (Mock data shown.)

Decision two: drag-and-drop IS the interface

The pairing board is two panels — students left, families right — and you drag one onto the other. That’s not decoration; dragging is the data entry. No form mode, no confirm dialog. The board always shows the whole roster state: who’s placed, who’s waiting, which family has room.

The pairing board: students on the left, families on the right, with the whole roster’s state — paired, waiting, capacity — visible at a glance. (Mock data shown.)

Decision three: history is sacred

Pairings are stored per school year. Rollover creates new rows for continuing matches and leaves dissolved ones in their year as record. Nothing is ever silently overwritten — because in August, “why did we move this student last year?” is a question you need answered from the data, not from memory.

The result

Matching went from weeks to days across 112 families — turnaround cut by more than half — and every decision has reasons attached. Flask + SQLite, running on my homelab behind Cloudflare Access. Boring stack, deliberately: the innovation budget all went into the workflow.