Building StackOps: A Multiplayer SRE Card Game
A deep dive into building a real-time multiplayer card game that pokes fun at DevOps culture, featuring TypeScript, Angular, NgRx, and Socket.io.
Keywords
StackOps is a turn-based multiplayer card game that pokes fun at DevOps and SRE culture. The goal is simple: complete deployment tasks to earn Reliability Points while surviving chaos events like outages, latency spikes, and “Friday deploys.” Under the hood, it’s a full-stack TypeScript project with a real-time game loop, a type-safe card system, and a state-driven Angular UI.
This post walks through what’s built so far and how the architecture hangs together.
The High-Level Shape
The project is split into a frontend Angular app and a backend Node/Express server with Socket.io. The server is authoritative, and the client renders state and sends intents. The ADRs capture the major architectural decisions and explain why the stack leans on TypeScript, NgRx, and Socket.io for predictable gameplay and multiplayer support.
What the MVP includes
- Multiplayer lobby and room flow (2–4 players)
- Turn-based phases: task-check → draw → action → penalty
- Task, action, chaos, interaction, and upgrade cards
- Server-side rule enforcement and validation
- Real-time state updates over websockets
- Basic UX polish: game log, toasts, timers, and sound toggles
Backend: The Authoritative Game Engine
The backend is a layered Node/Express app that owns the game state.
HTTP + WebSocket split
- REST endpoints handle game creation and lobby info.
- Socket.io handles the live game loop: join, start, draw, play, complete, end turn.
The server exposes /api/games to create a game and returns a temporary host secret. That secret is later used when the real socket connection joins so the host role is correctly bound to the live socket ID.
In-memory game state with strong typing
Game state is modeled with explicit types:
GameStatetracks players, decks, turn/phase, and pending interactions.PlayerStatetracks hand, active task, chaos cards, and upgrades.PublicGameStateis the client-safe view (other players’ hands are hidden).
This lets the server safely validate actions and broadcast tailored state updates.
The card system
Cards are defined as discriminated unions:
- Task cards require action sets and award RP.
- Action cards can resolve chaos and complete tasks.
- Chaos cards attach to tasks and apply penalties.
- Interaction cards target other players with counterplay.
- Upgrade cards provide passive or one-time buffs.
Decks are created from configuration, then shuffled and drawn by the DeckService. Chaos cards never enter a hand; they auto-attach to the active task or get discarded if no task exists.
Rules and validation
The rules engine enforces:
- Turn ownership
- Phase eligibility
- Action limits (plus bonuses)
- Chaos transfer rules
- Task completion constraints
- Deck depletion endgame
When a deck runs out, the server immediately ends the game and declares the RP leader, avoiding stalled endgame turns.
Multiplayer safety
Socket connections are rate-limited per IP. Join/leave flows are tracked by a room manager, and host reassignment is handled cleanly if someone leaves.
Frontend: Angular + NgRx for State-Driven UI
The frontend is built as a standalone Angular app with NgRx for predictable state updates. It’s designed to react to the server, not to invent state.
Store-driven architecture
The game slice tracks:
- connection state
- lobby metadata (room code, host secret)
- public game state
- UI state (errors, log, deck activity)
- UX settings (sound, reduced motion)
Actions are wired to Socket.io events through effects. The effects layer also persists UX settings and triggers sound events when cards are drawn or played.
UI mechanics
The main game board is a stateful container that combines:
- turn and phase header
- RP and action tracking
- active task and attached chaos
- hand rendering with interaction flow
- modal overlays for targeting and blocking
- event toasts for chaos, upgrades, and interactions
The app tracks per-turn timeouts and interaction windows, with short, visible countdowns to keep gameplay moving.
Client–server contract
The client sends intent actions (draw-card, play-card, complete-task, end-turn). The server replies with authoritative state updates and event signals (card-drawn, task-completed, chaos-attached, game-over). This keeps the UI honest and simplifies validation.
What’s Next
the next logical steps are already mapped out:
- Expand card content and interactions
- Add animations, sound design, and mobile optimization
- Add Redis for scaling and persistent game history
- Introduce authentication and player profiles
Closing
StackOps already plays like a real multiplayer game: typed cards, reliable state, and a front-to-back event flow. The backend enforces the rules, the client renders the truth, and the architecture is set up to scale into richer gameplay.