Personal project · 2024–present
laceup.club
Tennis club SaaS with 148 clubs and 40,000 match records live in production. Built by orchestrating 12 AI specialists, then making the product calls they couldn't.
I built laceup.club — a tennis club SaaS — by orchestrating a team of 12 AI specialists: a database architect, a frontend engineer, a brand designer, a product designer, a content strategist, a security lead. Each runs in an isolated context with a defined brief. I orchestrate the handoffs.
The hard part wasn't the code.
AI can write components, design schemas, draft copy, audit RLS policies. What it can't do is decide what the product should be. Every interesting choice in laceup came down to judgment that no context window could automate. Here are four of those calls.
Why seed 148 clubs before anyone signed up?
Every competitor in this space uses the same onboarding model: an admin signs up, a club gets created, a page appears. The platform starts empty.
I flipped it. Before a single admin signed up, 148 Irish tennis clubs already had public pages on laceup — each populated with real competitive data: league fixtures from the Dublin Lawn Tennis Council, tournament results from Tennis Ireland, facility details from club websites. A captain searching for their club finds it already there, with match results they recognise.
I call the conversion mechanism ghost prompts: interactive editors on unclaimed pages that invite visitors to fill in their club's profile before claiming ownership. Once you've written your club's description, you feel ownership before you've signed up. The IKEA effect, applied to SaaS onboarding.
The claim itself is a single modal. I rejected a two-step flow because the captain has already decided by the time they click — splitting it adds friction without adding value. Behind this sits a HubSpot CRM pipeline tracking each club from Unclaimed through Contacted to Claimed, so outreach is operationalised, not improvised.


Why give away the most valuable page for free?
A public club page with facilities, results, and a join link is valuable. Most competitors gate it behind a paid plan. I left it free.
Every unclaimed page is a search result waiting to convert a club captain. Every claimed page is a recruitment tool that brings members to the platform. Gating it behind Pro turns the best acquisition channel into a paywall.
The things I gate — automated reminders, CRM-style member notes, roster exports — are operational tools a captain values after they're committed. I gate the automation, not the presence. Blocking a match confirmation email because you're on the free plan drives a captain to WhatsApp. Offering scheduled reminders as a Pro feature gives them a reason to pay.
Where should the seam go?
Laceup has two competition formats: ladders (ongoing, rank-based) and box leagues (round-robin within groups, seasonal). They look the same to a club admin: same registration flow, same match cards, same status lifecycle.
Under the surface, they use fundamentally different matchmaking algorithms. Ladders use level-banded greedy pairing: a band coefficient scales with field size, candidates are filtered by rank proximity, a seeded-random pick avoids back-to-back rematches. Box leagues use a deterministic circle-method rotation within each box, with the full schedule pre-generated for the season.
I could have built a generic “competition engine” that abstracts over both. I didn't. The algorithms have different inputs, different constraints, different failure modes. Forcing them into one model would have made both worse. I shared only the thin layer where they're genuinely the same: a parent competitions table, a shared registration UI, common match display components.
Why not just put a Save button on it?
Settings pages usually have a Save button at the bottom. You change three fields, scroll down, hit Save, hope you didn't miss one.
I rejected this. Every field saves individually the moment you change it. A small checkmark confirms it. No save button, no bar, no toast.
Ambiguity about what's saved is worse than any amount of friction. If some fields save inline and others need a button, the admin has to remember which is which. A missed click means lost work with no feedback. Per-field auto-save is more work to implement — each field needs its own mutation and optimistic state — but it eliminates an entire category of user error.
How it was built
I built laceup by orchestrating a team of AI specialists through Claude Code. Each has a name, a defined expertise, and a brief: Tomas owns the database schema and RLS policies, Priya owns the frontend, Suki owns the design system, Nadia owns the brand, Finn owns pricing strategy, Theo runs security audits.
Each runs in an isolated context. I dispatch them with full briefs, review their output, and make the judgment calls that connect their work into a coherent product. Before any visual change ships, it goes through a design preview cycle: an HTML treatment reviewed in the browser before a line of production code gets written.
The AI team gives me the bandwidth of a small studio. The product decisions — what to build, what to reject, where the seams go — stay with me.

