Improvement Backlog
Prioritized improvements identified in May 2026. Ordered from quick wins to larger architectural changes. Redis is already planned — see Roadmap for the full implementation spec.
Medium Effort — High Value
5. Push notifications for game-ending events
Files: apps/server/src/game/game.gateway.ts
Currently push notifications only fire on "your turn." Adding notifications for gameAbandoned and turnTimeout is a small addition — same notificationsService.sendPushNotification() call with new message templates. Meaningful UX improvement for testers not watching the screen when their opponent leaves or times out.
Suggested messages:
| Event | Title | Body |
|---|---|---|
| Opponent timed out | "You win!" | "{opponentName} ran out of time" |
| Opponent abandoned | "You win!" | "{opponentName} abandoned the match" |
7. Socket connection hardening (playerId spoofing prevention)
Files: apps/server/src/matchmaking/matchmaking.gateway.ts, new auth utility
The identify event accepts any playerId string without verification that it matches the connected Clerk user. Before opening to testers, add a short-lived server-issued connection token (issued at REST login, validated on identify) so players cannot claim each other's IDs. More critical than it sounds once multiple accounts share a device or session.
Larger Changes
8. Rate limiting on socket events
Files: apps/server/src/game/game.gateway.ts, apps/server/src/matchmaking/matchmaking.gateway.ts
makeMove and joinQueue have no per-socket throttle. Before the 10–15 person test phase, add a sliding-window rate limiter in the gateway layer. NestJS @nestjs/throttler supports WebSocket guards. Prevents accidental double-submits, reconnect storms, and deliberate abuse.
bash
pnpm --filter @band-game/server add @nestjs/throttler9. Automated DB migrations in the deploy pipeline
Files: apps/server/railway.json
db:push is currently a manual step — the wiki notes it must be run before or after deploying breaking schema changes. A missed step leaves schema and code out of sync in production. Add drizzle-kit push as a pre-start command in railway.json so migrations apply automatically on every Railway deploy.
json
"deploy": {
"startCommand": "pnpm run db:push && pnpm run build && node dist/main"
}10. Client state management refactor (prerequisite for Redis)
Files: apps/client/services/GameService.ts, new apps/client/stores/
GameService currently has 38+ listener Sets as a pure singleton. Adding more real-time events makes state ownership harder to track and debug. Migrating client state to Zustand with typed slices (matchSlice, queueSlice, friendsSlice) would improve maintainability and is a natural prerequisite for the multi-instance Socket.IO changes the Redis rollout will require.
This is the largest item here — do it before Redis, not after.
Dropped / Superseded
In-memory Spotify band validation cache
Dropped in favour of a planned three-tier validation pipeline:
text
Submit band
→ check recent 100 played bands (fast DB query, hot bands)
→ miss → check full bands table (historical)
→ miss → hit SpotifyAn in-memory server cache would sit between layers 2 and 3 and add complexity without meaningful benefit — the DB lookup is already fast and survives restarts. Implement the "recent 100" layer instead when the time comes.
Done
Pre-commit hooks (Husky + lint-staged) + GitHub Actions CI
Shipped: May 2026 — commit d31702c
Files: root package.json, .husky/pre-commit, .husky/pre-push, .github/workflows/ci.yml
Husky + lint-staged runs ESLint + Prettier on staged apps/server/src/**/*.ts files before every commit. A pre-push hook runs the full Vitest suite and tsc --noEmit before anything reaches the remote. The GitHub Actions workflow (lint → typecheck → test) runs on every push to main/feat/** and on PRs — Railway can be configured to only deploy on workflow success, completing the CI gate.
Spotify API failure → retry / non-fatal path
Shipped: May 2026
Files: apps/server/src/spotify/spotify.service.ts, apps/server/src/game/game.service.ts
On API failure, validateBand now retries once (forcing a fresh token on retry). If the second attempt also fails, it returns apiError: true with the message "We couldn't verify this band, please try again". makeMove short-circuits on apiError — the move is rejected non-fatally so the player can resubmit. Only genuine "band not found" or "too few followers" results end the game.
Reorder validation: letter-chain check before Spotify lookup
Shipped: May 2026 — commit d012099
Files: apps/server/src/game/game.service.ts
Moved the letter-chain check before the Spotify lookup so obvious chain violations fail instantly with zero network calls. Also checks "The " + submittedName as a fallback so submitting e.g. "beatles" when the last band ends in T still passes through to Spotify (the canonical "The Beatles" satisfies the special-T rule). A post-Spotify re-check is retained for the rare case where the canonical name differs from the submitted name and changes the chain outcome.