One Codebase, Five Blockchain Environments
SupraScan runs against five separate blockchain environments today: production mainnet, production testnet, a MultiVM development network, and two independent microchains. All five run the exact same indexer codebase and are served by the exact same backend service. The interesting engineering question isn't "how do you support five environments," it's "how do you add a sixth without touching the code that already works for the first five," and the answer to that turned out to hold up under a genuinely different kind of test than the one it was designed for.
The indexer scales out, the backend scales in
The indexer side of this is deliberately simple: a single codebase, with a configuration variable selecting which blockchain it's pointed at, deployed as a separate running instance per environment. Each deployed instance polls its own RPC endpoint and writes to its own dedicated database. There's no cross-environment coordination at the indexer layer at all, by design; five environments means five independent processes running the same code against five different inputs.
The backend inverts that shape entirely. Instead of five separate backend deployments, one Apollo Federation service serves every environment at once, specifically so a single deployment can serve all of them simultaneously rather than multiplying the number of running backend processes by the number of environments. Every incoming GraphQL request carries a field identifying which environment it's asking about, and a connection manager routes the query to the correct database pool based on that field. The routing isn't a simple two-way split, though, and it's worth being precise about which environments actually share infrastructure today rather than assuming the obvious grouping: mainnet gets its own dedicated pool, one specific microchain has since been given its own dedicated pool as well, and testnet, the MultiVM devnet, and the other microchain share one pool. That's a narrower, more deliberately tiered split than "mainnet alone, everyone else together," and which environments get pulled out of the shared pool has evidently changed at least once since this system was first documented, this microchain's dedicated pool is a more recent addition than the routing scheme's original design.
That drift is worth naming directly: the system's own written documentation still describes the older, simpler grouping, mainnet on one side and testnet, multivm, and every microchain sharing the other, while the actual routing code has since moved one specific environment out of the shared pool without the documentation catching up to reflect it. It's a small, ordinary kind of staleness, the code evolved and the README didn't, but it's a genuine example of why checking live behavior matters even when a design doc already exists and looks authoritative.
There's still a real tension worth sitting with in the environments that do share a pool. A slow query or a resource spike on one environment's traffic runs through the exact same backend process serving every other environment sharing that pool. That's the opposite tradeoff from the one made on the Redis side of this same system, where the two networks judged to need isolation, mainnet and that same promoted microchain, got their own dedicated Redis instances specifically to stop one environment's cache behavior from contaminating another's, while the rest still share a default instance. The database-pool split and the Redis-instance split aren't drawn along quite the same lines, but they're answering the same underlying question, which environments earn dedicated infrastructure and which don't, and the fact that both keep getting revisited as specific environments grow suggests the current split, in either layer, isn't meant to be permanent.
Adding a genuinely new environment to this system is a documented, five-step procedure: register the new environment in a reference table, add its connection configuration to the connection manager in both the indexer and backend codebases, add its RPC endpoint configuration, add a Redis key namespace for it, and deploy a new indexer instance pointed at it. None of those five steps require touching the core indexing or query logic. That's the actual measure of whether an abstraction like this is real: can you add a sixth environment without anyone needing to reason about the first five.
The test this abstraction didn't expect: a different kind of infrastructure entirely
Most of the time, "add a new environment" means the same kind of managed cloud database on a new connection string. A more interesting test came from a different direction: two of the microchain-tier databases were migrated onto plain, self-hosted PostgreSQL running directly on virtual machines, not a managed database service, and not a Kubernetes-orchestrated cluster like some of the other environments use. That's a genuinely different infrastructure category from the managed cloud database clusters the rest of the system runs on, and it's exactly the kind of change that exposes an abstraction that only looked clean on paper.
It held. A direct schema comparison against the equivalent managed database cluster, run live, confirmed 290 tables on the self-hosted box, including the full set of already-created daily partitions, matching the production structure exactly, and a byte-for-byte diff of table and index names between the self-hosted box and its managed counterpart came back identical across all 819 index names. The same application code connected to either backing database correctly, with the only difference being a connection string. Both self-hosted databases also came up with zero database triggers, consistent with the trigger-removal work described elsewhere in this series: any new environment stood up after that work inherits application-level counting logic from day one, with nothing left to migrate. Every environment checked directly, production and QA, mainnet and testnet alike, showed the same result for the two trigger-removal passes confirmed shipped; one further trigger removal from that same series was still awaiting independent confirmation of its production rollout as of this writing, so 'no triggers anywhere' holds for what's been re-verified live, not yet as a fully closed book.
What this demonstrates
A multi-environment architecture is easy to design well when every environment looks the same underneath. This one got tested by an environment that didn't look the same underneath at all, a different hosting model entirely, and the abstraction held without a single code change. That's a more honest test of the design than adding another environment that looks just like the last one, and it's the kind of validation that only shows up when infrastructure decisions made for unrelated reasons, in this case a self-hosting migration on cost or operational grounds, happen to stress-test an architectural boundary nobody built specifically to be tested that way.