← All posts

Why multi-environment consoles converge on a grid

A feature flag that’s enabled in staging and disabled in production is a rollout working as intended. A config value that differs by environment is the whole point of environments. But presenting per-environment state clearly, for an account with five or six environments, is a real UI problem, and it has a shape worth understanding before you build the screen.

The problem is two-dimensional; most UIs aren’t

Ten feature flags across four environments (development, staging, production-eu, production-us) is forty data points. A list view shows one environment at a time: one row per flag, and either a dropdown or four page loads to assemble the full picture in your head.

The questions an operator actually brings to this screen are cross-environment by nature. Is the flag I enabled in staging still off in production? What’s different between production-eu and production-us? Which loggers am I managing in development but not in production? Every one of those is a comparison across the dimension the list view hides - which is why a list makes the operator do the joining, one page load at a time.

This, we think, is why every multi-environment tool eventually converges on the same answer: when the data is resources × environments, the UI ends up as a grid, because anything else turns comparison into navigation. Ours has resources as rows and environments as columns; each cell is the state of that resource in that environment. Ten rows, four columns, all forty data points on one screen.

Column choice is part of the design

An account can have fifteen environments (including an AD_HOC one for every developer’s local machine), and fifteen columns is not a grid anyone can read. So which environments appear, and in what order, is a first-class control: a multi-select picklist above the grid. The default selection follows the account-level environment order (typically production, staging, development), and the selection is persisted per user, per product. The operator who always compares production-eu against production-us sees those columns by default, every visit, without re-selecting.

Discovered environments announce themselves

AD_HOC environments (created automatically when an SDK connected with an unrecognized environment name) show up in the grid wearing a discovery badge, with a banner above the columns: “This environment was discovered from SDK connections. [Promote to Standard] [Remove].” Promote it and it joins the account’s main environment list; remove it and the environment and its per-environment data are deleted.

The banner only appears for environments with recent activity - SDK connections within the last 30 days. Stale ones get quieter treatment: a note in the environment settings page (“last seen 45 days ago”) and a suggestion to archive.

One grid, three products

The same grid runs in Flags, Config, and Logging; only the cell contents change. A flags cell shows enabled or disabled plus a summary of the targeting rule, and clicking opens the flag editor scoped to that environment. A config cell shows the value (or the value inherited from a parent config, marked as inherited), and clicking opens the value editor for that key and environment. A logging cell shows the logger’s level, or “not managed,” and clicking sets the level right there.

The visual language is shared across all three: inherited values in a muted color, overridden values in the primary text color, managed values with a solid background, unmanaged cells empty or striped - the same in all three products.

Small screens get one column

The grid doesn’t work on a phone (five columns at 375px is unreadable), so on small screens it collapses to a single-environment view with an environment selector. Same interactions, one column at a time. This is pragmatic rather than elegant: most console users are on desktop, and mobile access to configuration management is rare enough that a fully responsive grid hasn’t been worth the investment.

Keyboard navigation

Power users work the grid hard, so it has keyboard navigation: arrow keys move between cells, Enter opens the editor for the focused cell, Escape closes it, Tab moves in reading order. Bulk edits become mechanical (e.g. change a value in production, tab, change, tab) without the mouse ever getting involved.

What we’d revisit

Batch operations. “Set this flag in all environments at once” currently means clicking each cell. A per-row “set everywhere” control would save real clicks.

Diffing. “Show me only the rows that differ between production and staging” is a filter we haven’t built; the grid has all the data to highlight rows whose cells disagree.

Snapshot comparisons. “Diff today’s grid against last week’s” would combine this UI with version history from the audit service - a bigger feature, since it needs historical snapshot data, but the natural end state for a screen whose whole job is comparison.