Skip to main content
Case Study

MyRigCheck Case Study

A production game-compatibility platform that checks 367,825 titles against a detected rig. The build pairs a 13-phase IGDB + Steam read layer and a journaled blue-green swap with a Next.js runtime that reads precomputed compatibility by game, hardware hash, and resolution - then degrades gracefully when the self-hosted origin goes offline.

Role: Founder & Full Stack Engineer2026

Problem

What had to be solved before the UI mattered

PC game requirements are scattered across stores, inconsistent in format, and rarely map cleanly onto real hardware names. Answering 'can my PC run this?' for hundreds of thousands of titles needs normalized CPU/GPU catalogs, a repeatable way to derive compatibility tiers, and a runtime that can serve the answer without querying raw ingestion data or recalculating the same comparison on every request.

Timeline

How the project moved from scope to delivery

Discovery

2026-Q1

Framed the compatibility question and the data sources

Started with the actual user question - can this machine run this game? - and mapped which IGDB and Steam fields could answer it, where the data disagreed, and which comparisons needed to be precomputed.

Evidence / Result

The repository separates IGDB and Steam ingestion from a runtime read layer rather than treating them as one catalog.

Architecture

Read-layer design

Chose a namespaced read layer and a deterministic phase pipeline

Defined the igdb.*, steam.*, staging.*, read.*, and retired.* boundaries, then made the build a phase-id pipeline with validation gates, a journaled swap, and post-swap canaries.

Evidence / Result

docs/architecture defines the namespace model, canonical phase order, swap semantics, and determinism rules.

Implementation

Runtime and rig UX

Built the Next.js rig experience and compatibility surfaces

Implemented hardware detection and a saved-rig flow, then surfaced compatibility tiers, FPS estimates, bottleneck analysis, and the Rig Power Score on top of the read layer.

Evidence / Result

The app resolves the saved rig server-side and reads hardware_reference, games_requirements, and compatibility_cache.

Optimization

Search, ranking, SEO

Made 367,825 titles searchable, rankable, and crawlable

Built search_entities, discovery pages, ranking signals, and stable slugs so the catalog could be browsed and indexed without request-time source queries.

Evidence / Result

The phase contract includes slugs, discovery, feature engineering, ranking, pages, and search as separate materialized outputs.

Launch

Public release

Added trust surfaces, a public dataset, and degraded-mode operations

Shipped methodology, accuracy, data-source, and corrections pages; a versioned dataset export; llms.txt files; structured data; and a cache rule plus Worker fallback for the self-hosted origin.

Evidence / Result

CHANGES.md records the 1.0.0 dataset release, public trust pages, schema graph, and AI crawler allowances.

Key Decisions

Tradeoffs that shaped the build

Build a read layer instead of querying ingestion collections at runtime

IGDB and Steam data is large, normalized differently, and not shaped for page rendering. Serving it directly would couple request latency to ingestion state and make every schema change a runtime risk.

Query igdb.* and steam.* directly from the appCache individual API responsesMaterialize a dedicated read layer
Decision

I built a deterministic phase pipeline that materializes read.* collections and promotes them as one versioned unit.

Tradeoff: The pipeline is more infrastructure than a conventional CMS-backed app.

Impact: Runtime queries stay stable and fast, and a bad build can be rolled back as a whole.

Split read and build credentials behind separate gateways

The public app only needs to read the catalog, while ingestion and the read-layer build need write access to staging and metadata collections.

Give the runtime a broad database credentialUse one gateway with role checksSplit read and build executors with separate keys
Decision

I routed runtime reads through a read-only executor and kept writes behind a separate build gateway and key.

Tradeoff: There are two endpoints and two secrets to operate.

Impact: A compromised runtime credential cannot mutate source or staging data.

Precompute compatibility by game, hardware hash, and resolution

Compatibility is the core product surface, and recalculating it for every card and page would dominate request time.

Compute compatibility on every requestCache only the hottest gamesPrecompute compatibility for the catalog during the build
Decision

Phase07 builds the hardware reference and compatibility cache, and the runtime reads the precomputed result with resolver provenance.

Tradeoff: The build has to keep hardware aliases and benchmark scores fresh.

Impact: Cards and pages can show FPS and tier signals without a per-request comparison.

Design the self-hosted origin for failure

The production origin runs on a laptop behind a Cloudflare tunnel, so power or network loss would otherwise surface as a raw Error 1033 page.

Accept the default Cloudflare errorMove to a managed hostAdd a cache rule and a Worker fallback
Decision

I added a serve-stale cache rule, a Worker-based degraded page, and a heartbeat to keep the failure mode intentional.

Tradeoff: The fallback adds Cloudflare configuration and a heartbeat process.

Impact: Visitors see the last good catalog or a designed upgrading page instead of an infrastructure error.

Architecture

The system decisions behind the product surface

The application is a Next.js 16 / React 19 frontend backed by a MongoDB read layer. IGDB and Steam ingestion write to igdb.* and steam.*; a phase-id pipeline (phase01-init through phase12-finalize) materializes staging.* collections, validates the result, and promotes it into read.* through a journaled blue-green swap that can roll back in reverse order.

The runtime never touches the source namespaces. It talks to a read-only database executor, resolves the visitor's saved rig server-side, and reads precomputed games_requirements, hardware_reference, and compatibility_cache collections keyed by game_id, hardware_hash, and resolution. A separate build executor holds the write key, so the public runtime credential cannot mutate the catalog.

The read layer also carries search_entities, discovery_pages, games_pages, and dashboard collections so page rendering, ranking, and search are consistent with the same build. Determinism is enforced by a single build_reference_unix shared across phases rather than per-phase wall-clock reads.

Results

The public catalog exposes 367,825 titles and 102,587 Steam apps through compatibility tiers, FPS estimates, bottleneck analysis, and a Rig Power Score. The read layer is promoted through a validated, journaled blue-green swap; the runtime reads only read.* through a separate gateway; and the site ships a versioned dataset export, llms.txt and llms-full.txt, structured data, and a degraded-mode fallback for the self-hosted origin.

Takeaways and Next Moves

MyRigCheck is the clearest example of me owning a product end to end: data ingestion, a deterministic build pipeline, a read-only runtime boundary, compatibility logic, SEO at catalog scale, and operational resilience for a self-hosted origin.

Lessons Learned

  • Data-heavy products live or die on the pipeline behind them. Determinism, explicit phase contracts, and rollback are not infrastructure polish; they are what makes a large catalog safe to change. The second lesson is that hosting constraints should be part of the architecture from day one: designing the degraded mode alongside the product turned a laptop-hosted origin into an explicit failure model instead of a hidden liability.

Next Improvements

  • Expand hardware coverage and benchmark sources so more rigs resolve at the exact tier. Tighten the freshness loop between ingestion and the read layer. Grow the public methodology and corrections workflow as the dataset becomes a reference surface.
Ankur Halder

Loading portfolio…