Dink+ started because the thing we were using was bad
Building in public ·We had a Sunday group, and we were using an app to organize it. The app was bad. Not broken exactly, but bad in the way that software you’re stuck with tends to be bad, where every individual annoyance is too small to justify leaving and the pile of them still manages to wreck the evening.
I didn’t write the first version of the replacement. A player in our group did.
Day one, November 5, 2025
One of the players in our group opened an editor and committed 23 times in a single day. I don’t have to guess at what was bothering him, because the first day’s commit log reads like a list of complaints about the old app.
Matchmaking with guest player support was in the very first commit, not added later. Anyone who runs a rec group knows why. Half of any Sunday is someone bringing a friend, a cousin who’s visiting, or a regular who bailed at 4pm and got replaced by a guy from the next court over. A tool that assumes a fixed roster has never met a Sunday group.
A leaderboard came next, followed immediately by a second commit fixing the leaderboard’s layout. He got it working, looked at it, and didn’t like it, all in the same afternoon.
Then chat, because the alternative is the group text, and the group text is where scheduling information goes to die.
Then editing a game’s time, twice. Once to fix a locale bug that mangled the time, and once to fix the edit function itself. Plans change at 9am on a Sunday, and that shouldn’t be hard.
Then approval-only registration, because a neighborhood group isn’t a public sign-up sheet.
None of that is visionary. It’s just the list of things that had been annoying all of us, in roughly the order they annoyed him most.
The breaking point was the round robin itself. The old app would put someone in a round and also show them as having a bye in that same round, so you would look at the board, see your name in two places, and have to go ask the organizer which one was real. And the groupings were always off. We would have 8 men and 8 women playing 8 rounds, which is the one configuration where everyone should get to play with everybody once, and I would come off the court having played with maybe half of them. Once you know a schedule like that exists and the app keeps not finding it, it is hard to keep using the app. (That configuration turns out to be the sweet spot for a reason. I fixed the same class of bug in our own version later and will write up the math in a follow-up.)
The commit messages
Day three brought iOS support through Capacitor. Day four went entirely to iOS refusing to cooperate, with safe areas, the keyboard double-offsetting, and landscape mode all taking their turn, and it ended with four commits in a row named chat rubber band fixed position through chat rubber band fixed position 4.
By the following week the commit messages had stopped pretending to be professional:
Massive groups add, various fixes, my bad for big push....
Stuff....
Another big push, gotta stop this. Algo refactor to remove constraints
refactored DB schema and probably broke more than i know.....
sometimes vibe coding is stupididly overcomplicated....
He was apologizing for the big push to nobody in particular, since he was the only person on the repo.
I’m quoting these because a polished version of this story would leave them out, and the polished version would be a lie. This is what the first few weeks of something real look like. You know you’re doing it wrong, you do it anyway because the group plays again in seven days, and you leave yourself a note about it.
And it worked. By late November the group was running on it every Sunday. Three months in, he had shipped 196 commits and built something people actually used.
February 2: I show up with a plan instead of a feature
My first commit was Add Azure replatform plan docs (Bicep, prod-only). It wasn’t a fix or a feature, just a document. Over the next five days I added Bicep scaffolding, a migration runner, a baseline schema, and an auth stub, while he kept shipping on the original stack.
The reason was pretty simple. The app was on Supabase, and Supabase was fine for what it had done, which was get something in front of people fast. But I’ve spent 25 years running infrastructure and security, and I was about to become the person who gets paged when it broke. I wanted every resource defined in code, an API I could put a rate limiter and real authentication in front of, and a database I could reach with psql at 11pm. I wanted to own it in the boring, operational sense of the word.
His last commit was February 7. On February 8 I merged the branch.
That one day’s log included an Express API replacing the direct Supabase calls, Entra for authentication, Twilio Verify for SMS, Bicep for Postgres, App Service, storage, and DNS, GitHub Actions deploying both halves, Static Web Apps for the frontend, a commit called Fix security vulnerabilities, and somewhere in the middle of the afternoon, Rebrand to Dink+.
Thirty commits on one Sunday, and the app came out the other side with a new name and a new backend. The group didn’t notice, which was the point.
What I kept
“Took over and rewrote it” is usually a story about ego, so I want to be clear about what actually happened.
I replaced the platform. I did not replace the product. Guest players, approval-only groups, chat sitting next to the schedule, the general shape of the matchmaking: all of those were right, and they were right because they came from the courts rather than from a spec. The three months where the group beat on his version were the most valuable design work in the whole project, and none of it was mine. I would have gotten most of those calls wrong from a whiteboard.
If you ever inherit a codebase, the rule I’d suggest is to replace what you can’t operate, keep what the users have already validated, and be very sure you know which is which.
Since then
Since February the codebase has roughly quadrupled. The API grew about tenfold, which is the tournament engine, inter-club leagues, ladders, and combines, all built from scratch. Tests grew about twentyfold, because I don’t trust myself with a bracket-seeding algorithm at midnight without them. There’s live scoring, DUPR score reporting, and iOS and Android builds going out through the same pipeline. It’s somewhere around 1,800 commits since that first planning doc.
I didn’t plan any of that on February 2. On February 2 I just wanted to be able to sleep while the app ran.
The loop hasn’t changed from that first week, though. Play on Sunday, notice what was annoying, fix it before next Sunday. The group is the roadmap. It’s slower than a strategy document, and it’s been right a lot more often.
If you’re at day zero
Build the thing that annoyed you this week, not the platform it could eventually turn into. The annoyance is real data from a real user, and most product ideas don’t have that going for them.
Ship it to people who will tell you the truth. Ours were people we’d see in person every Sunday, and you can’t ignore a bug report delivered at the kitchen line by a guy holding a paddle.
Let the ugly commits be ugly. Stuff.... shipped, it worked, and nobody has ever asked about it.
And if you take something over, respect the part that already works. The scrappy version got the hard part right by figuring out what people actually needed. The rest is plumbing.
The best reason to build something is still that you needed it and what existed was bad. That was the whole reason here.