Skinrave.gg
A real-time iGaming platform on event-driven microservices, now generating over $3M in annual revenue.

TL;DR
- Outcome
- Over $3M in annual revenue
- Role
- Full-stack engineer, mostly backend; team lead
- Context
- iGaming product built at Milies
Context
Skinrave.gg is a real-time iGaming platform for in-game items: live game rounds, a marketplace with listings, auctions and peer-to-peer trades, and a wallet. It was one of two products my team at Milies worked on. The hard part was the backend. Many players act on the same state at once, and every balance has to stay correct while they do, with no lost updates and no double-spends. Results have to reach players live, and calls to the external item platform have to stay under its rate limits.
Architecture
The backend is a set of TypeScript services on NestJS.
- API gateway. The only public HTTP surface. It handles auth and calls the services request/reply over RabbitMQ.
- Domain events. Every state change is published to one topic exchange. A WebSocket gateway subscribes and pushes the events to users and rooms over Socket.IO, so clients never poll.
- Wallet and payments. The only service that changes a balance. Payment webhooks land here.
- Data stores. PostgreSQL for money and users, MongoDB for settings and snapshots, Redis for hot game state and counters.
- Scheduler. The single home for cron jobs and deadlines. Deadlines are persisted, so in-flight timers survive a server restart.
Decisions
One ledger service for every balance change. Other services send effects (add, spend, freeze, settle) instead of writing balances, and each effect runs in a transaction with a lock on the wallet row, exact decimal maths and an audit row. A wager freezes funds and the round settles later, so the available balance is right mid-round. I ruled out letting each service update balances itself, because that is where lost updates and double-spends come from.
Atomic increments, not read-modify-write. Reading a value, changing it and writing it back loses updates once several copies of a service work on the same value. I replaced read-modify-write with atomic increments across the scaled services, which eliminated the lost updates.
Idempotent webhooks, one pipeline. After the signature check, a conditional update marks a payment completed only if it isn’t already, and only the delivery that changed the row credits the wallet. Providers retry and send duplicates, so crediting on every delivery would pay twice; with this check, repeats do nothing.
Reject rather than risk the upstream. Calls to the external item platform are spread across a pool of workers, each under a rate cap, and a request past the cap is rejected. Pushing past the limit would risk the platform banning us.
Result
Skinrave.gg now generates over $3M in annual revenue, which is the whole team’s result. My own part included rebuilding leaderboard ranking from seconds to milliseconds and cutting roulette writes 95-99% at 2,000+ concurrent users.
My role
I was a full-stack engineer, mostly on the backend, and the team lead, leading a team of eight, engineers and designers, across two products. I architected the backend as event-driven microservices, built the persistent scheduling with full timer recovery, and integrated the crypto and fiat payment providers behind one idempotent webhook pipeline.