← All work

Beta · iOS & Android · TestFlight and Play internal

Split Decision Beta

A daily phrase puzzle with two ways to play. There's solo, which works like any daily. And there's co-op, where each of you owns a different hidden word in the same phrase, so neither one of you can finish it alone.

Split Decision solved screen showing the phrase TIME IS MONEY, with moves and streak stats and a prompt to start a co-op game Idioms puzzle mid-game: TIME solved, and HONEY submitted as a guess for the five-letter second target, with O, N, E, and Y confirmed correct and H marked wrong, matching letter feedback highlighted on the keyboard Co-op puzzle screen showing a teammate's row marked 'hasn't started yet' with a Nudge button

What it is

The category is crowded with word puzzles, so it isn't really competing on puzzles. The job it does is giving a partner or a sibling or a long-distance friend a lightweight reason to talk every day, which is why it's built for persistent groups of two or three rather than for a social network.

It's a TypeScript monorepo with Node and Prisma on Postgres, a React web app, and native SwiftUI and Kotlin clients, all deployed on Fly, with more than 1,100 tests across web and native.

The decision

One global daily phrase was serving two incompatible jobs.

The single daily phrase was the identity of the solo game, where there's exactly one puzzle a day and everybody gets the same one. It was also where team puzzles came from. That worked right up until somebody joined a second team, because the moment they've seen the phrase once, the information asymmetry the whole co-op mechanic runs on is gone: they already know the answer their teammate is working toward. So I decoupled team puzzles from the solo daily.

On metrics, the more useful part is what pressure-testing changed. My North Star was Recurring Co-op Duo-Days, which counted every team-day where both members played their assigned word. On paper it's the right metric, because it only moves when co-op actually delivers.

The problem is that computing it means recognizing the same two people tomorrow, and identity in the app is per-device with no recovery. Somebody clears their data or picks up a second phone or reinstalls, and they come back as a new player, which quietly splits a returning duo into two identities. The bias runs downward, it runs hardest on the engaged pairs the metric exists to reward, and it's invisible, because you can't tell "this duo churned" from "this person got a new phone." A number that's most wrong exactly when you're most successful is worse than no number, so I declared it uncomputable until identity recovery ships.

What I put in its place for launch were two things I could measure inside a single game: how often a player spends a Signal on their teammate's word, and how often a Rescue gets claimed when someone is stuck. Both of those need both people present and paying attention in the same session, which is exactly why they survive the identity churn that broke Duo-Days.

Two smaller ones. I derived the teams-per-user cap from an actual comprehension test instead of asserting it, because I wasn't willing to ship a possibly-confusing product on arithmetic nobody had run. And when I caught myself describing a change as "only two lines of code," I split the claim in two, because the genuinely risky part, which was cross-surface dedupe, had been getting laundered by the cheap headline.

What it cost

Bundling web assets into the native shells means giving up instant web-to-native updates. I took that trade knowingly, to avoid a rejection under App Store guideline 4.2, which bars apps that are thin wrappers around a website, and I logged live-update infrastructure as deliberate debt rather than pretending it wasn't debt.

Losing the single global phrase also cost the solo game some of its identity, and that was the price of the co-op mechanic working at all.

Status & next

iOS build 112 is in TestFlight external review and the Android bundle is on Play's internal track, so public launch is gated on store assets and listing copy rather than on engineering.

Account deletion hard-deletes personal data, but it pseudonymizes shared co-op records to "Former player," so one person leaving can't corrupt their teammate's history.