Skip to content
WorkExperienceAboutContact
Request my CVOpen to work

StillKoi pond

Layer some sounds while you read

Sounds from StillKoi →

Name More!

  1. +30
  2. ?
  3. +92
  4. ?
  5. +40

Name a sea creature. The rarer, the better.

Play the mini board →

To do

  • Build my own OS
  • Ship my own products
  • Find my next team
  • More mint tea

Name More!

Name More!

In closed testing on iOS and AndroidSole engineer, from game design to servers

Website ↗
OverviewThe game and what I builtPlayA small board you can play hereProductScreens, scoring, economyEngineeringServer, matching, realtimeDecisionsTrade-offs, tests, operationsNext projectAI Watermarkless →

Engineering decisions

Fairness, cost and invites

01

Edit distance over embeddings

Problem

A semantic fallback accepted unrelated short proper nouns on fandom boards, and most of the content is fandom boards.

Decision

Replace it with a bounded edit-distance layer guarded by length, an edit budget, a first-letter anchor and tie rejection.

Why

A false accept is the worst failure a recall game can have. Edit distance has no similarity cliff and needs no model in memory.

Tradeoff

Meaning-based near-synonyms that aren’t typos now depend on generated synonym sets.

02

Self-hosted Postgres over a managed BaaS

Problem

A game makes many small requests and serves heavy image traffic, which is exactly the usage that metered platforms bill for.

Decision

Run Postgres, Fastify and nginx in Docker Compose on one VPS, with Cloudflare caching images at the edge.

Why

The marginal cost per player stays close to zero, and Compose keeps the whole stack movable.

Tradeoff

I own backups, tuning and uptime, so automated dumps to object storage were part of the build from the start.

03

Room codes over a deep-link vendor

Problem

Firebase Dynamic Links shut down in 2025, and an invited friend who installs the app often lands on a blank screen.

Decision

Use native Universal and App Links on our own domain, plus six-character room codes.

Why

The links cover everyone who already has the app, and codes cover everyone else with no vendor on the growth path.

Tradeoff

There is no deferred deep link. A new player types a short code once.

04

Decaying claims

Problem

In a six-player room the fastest thumbs would take every obvious answer, and the person who knows more would still lose.

Decision

Each later finder of an answer earns a decaying share of its value.

Why

The race stays, but the best strategy shifts from typing fast to digging deep. The game rewards the skill it is about.

Tradeoff

There is more state per answer, because claim order has to be recorded, broadcast and restored on reconnect.

05

Ghost opponents

Problem

A 1v1 queue with no one online is an empty queue.

Decision

Replay real recorded runs as opponents, time-compressed to the match length and scheduled through the same claim path as humans.

Why

They play like people because they are people. The curated pool also lets new players get winnable early matches.

Tradeoff

The ghost pool needs curation, and the framing has to stay honest: you are playing the community’s answers.

Quality & operations

Benchmarks, query plans and CI

Performance claims come from benchmarks and query plans, and the release path is scripted end to end.

  • ↳

    Tests

    Unit tests on node:test for matching, scoring, rooms, ghosts, leagues and missions, plus a real-Postgres suite and API integration tests.

  • ↳

    Benchmarks

    A reproducible matcher benchmark with a board-size sweep from 46 to 10,000 answers.

  • ↳

    Query plans

    An EXPLAIN pass over every hot path. It found a missing index on the fastest-growing table, which I added in a migration before launch.

  • ↳

    Room tests

    Colyseus testing harness for private rooms, quick match, reconnects and the room server.

  • ↳

    Operations

    GitHub Actions for client and server CI and deploys, health checks, a tuned 2 GB Postgres and automated off-box backups.

  • ↳

    Store readiness

    In-app account deletion with a public deletion URL, a data-safety mapping, and the store listing copy and graphics drafted.