QUBU GAMES
QUBU JOURNAL

Engineering Taht ve Kan: Ten Production Lessons from an Asynchronous Strategy Game

July 21, 2026
← Back to Journal3 min readUpdated: September 3, 2026

This engineering journal documents the real architecture decisions, production failures and durable rules learned while building Taht ve Kan.

1. Architecture: the client requests, the server decides

React client → Server Actions → pure game logic → Supabase/PostgreSQL

The client is never trusted. It can request an attack, but turn ownership, action points, adjacency and authorization are verified on the server. Combat, economy, Elo and league thresholds live as database-independent pure functions. RLS is enabled on every table and anonymous access cannot read rows.

2. Identity: guest and account players share one resolver

Guests use an httpOnly per-game token; signed-in players use a 30-day session backed by a scrypt hash. resolvePlayer() accepts both. The secret game token is never added to a broad select list, and fields returned to the client are explicitly enumerated.

3. The real performance bottleneck was geography

The database ran in Frankfurt while server functions initially ran in a distant default region. Dozens of sequential calls repeatedly paid roughly 90 ms of network latency. Pinning functions to fra1 and consolidating account loading from seven round trips to two fixed the actual source of the perceived slowness.

Taht ve Kan region management panel and strategy map
Real product screenshot from Taht ve Kan.

4. Migration resilience

Schema changes and code deployments are not guaranteed to happen together. A query reading a new column is isolated and guarded; when the column does not exist, the default value is used. Breaking this rule with daily_streak caused the entire main select to fail and made cosmetics and the admin flag appear to reset. The permanent rule is simple: never put a guarded column in the main select.

5. Three defenses against stalled matches

  1. A turn deadline advances play automatically.
  2. Players, timeouts and AI all pass through the same advanceTurnCore() function.
  3. Long-abandoned matches can be taken over by AI.

Unused action points also pay a small gold and food bonus, creating an economic nudge to finish a turn instead of holding it.

6. Three generations of the map

The first version used procedural SVG hexes: clean engineering, generic presentation. The second used a painted tile atlas but still looked like a board. The current map is one painted world divided into 22 asymmetric regions by terrain-aware Voronoi segmentation. Artificial white borders were removed; ownership is communicated through soft color influence. Legacy 40-region matches still open with their original renderer so old games remain valid.

Hand-painted 22-region world map in Taht ve Kan
Real product screenshot from Taht ve Kan.

7. Android is not a WebView

The Android client began as a TWA wrapper and was rebuilt as a native Kotlin and Jetpack Compose application. The game engine was rewritten in Kotlin, supports offline play and uses the same backend through dedicated mobile endpoints when online. Defaults for new fields and tolerance for unknown fields protect older local saves and APK versions.

8. Decoupling server cost from registered users

  • Completed matches skip battle, espionage and event logs.
  • Polling stops while document.hidden is true.
  • Chat polls every eight seconds and turn refresh every eighteen seconds.
  • The expensive map is memoized and the one-second timer lives in a separate component.

As a result, cost tracks active turns more closely than total accounts.

9. Platform-specific traps

  • "use server" modules may export only async functions.
  • Server-only modules cannot be imported into client components.
  • Windows case-insensitivity created a real collision between recentGames.ts and RecentGames.tsx.
  • Path invalidation cannot be called from a server action during page rendering, so the email verification flow had to be reorganized.

10. Current state

The game is in closed beta with registration, approval, sign-in, email verification, invitations and feedback collection active. Thirty database migrations have been applied. The native Android client is at version 1.8.1. Real payment integration remains intentionally deferred while debugging, balance and retention take priority.

Taht ve Kan three-branch technology tree
Real product screenshot from Taht ve Kan.