DRY: Dev Utils Panels Beat Manual State Setup

James Phoenix
James Phoenix

Every repeated setup ritual is an undeclared API waiting to be formalised. Build the panel once, skip the ritual forever.

Author: James Phoenix | Date: April 2026


The Frustration That Started This

I kept doing the same thing. Create a user. Verify the email. Log in. Create a Stripe customer. Attach a subscription. Grant credits. Navigate to the page I actually wanted to test.

Six steps, every time. Sometimes ten if I needed a specific billing state or a workspace with seed data.

It takes maybe three minutes. That sounds trivial. But three minutes of mechanical context-switching before you can even look at the thing you care about is death by a thousand cuts. The real cost is not the clock time. It is the cognitive interruption. By the time I finish the setup ritual, I have already lost the thread of what I was trying to test.

Leanpub Book

Read The Meta-Engineer

A practical book on building autonomous AI systems with Claude Code, context engineering, verification loops, and production harnesses.

Continuously updated
Claude Code + agentic systems
View Book

My dad gave me the simplest possible framing: never do something twice. If you find yourself repeating a multi-step process, that process should become a button.

So I built a dev utils panel.


What TanStack Query DevTools Got Right

TanStack Query ships a small floating panel at the bottom of your app. Click it open, and you can inspect every query in your cache. Stale queries, fetching states, error states, cache keys. It is always there, always one click away, and it costs the library author almost nothing to maintain relative to the value it provides.

The insight is not the implementation. The insight is the pattern. A tiny, always-available UI surface that gives you direct visibility into system state. No browser extensions. No separate tools. No context switch. Just a floating button in the corner of your app that opens a panel.

That pattern is not limited to query caching. It applies to anything you repeatedly need to inspect or manipulate during development. TanStack used it for cache state. I used it for account state.


The Panel I Built

Here is what it looks like:

Custom dev utils panel for state seeding
Custom dev utils panel for state seeding

A small “Dev utils” button sits in the bottom corner of the app. Click it, and a panel opens with:

Configuration fields. Email prefix, username base, org name, workspace name. These default to sensible values so I can just hit “Launch” without thinking.

Scenario presets. Each preset is a named, composable state fixture. “Fresh free org” creates a user, org, and workspace with no billing. “Fresh Pro + $20 local credit” does the same but also activates a Pro subscription and seeds credits. One click, full state.

Latest seeded account. After running a preset, the panel shows the created email, org, and workspace. I can see exactly what was generated and the app automatically logs me into that account.

The entire component is maybe 200 lines of React. The backend endpoints behind it are straightforward: create user, create org, optionally attach billing state, return credentials. The panel calls them in sequence.

Building it took less time than I had spent on manual setup in the previous week alone.


Why Scenario Presets Matter More Than Raw Buttons

The first version I considered was a set of individual mutation buttons. “Create user.” “Add credits.” “Set subscription status.” That would have been a mistake.

Individual buttons force you to think about sequencing every time. You still carry the cognitive load of “what state do I need?” and “in what order do I build it?” You have replaced typing with clicking, but the mental overhead is unchanged.

Scenario presets are different. A preset encodes a complete, named state. “Fresh free org” is not a sequence of steps. It is a declaration: put the system in this state. The sequencing is handled once, in the preset definition, and never thought about again.

This mirrors how you actually think about testing. You do not think “I need a user, then an org, then credits.” You think “I need a pro user with credits.” The preset matches the mental model. The raw buttons do not.


The Architecture Under the Panel

The panel is a thin UI over three layers:

1. State primitives. Small backend functions that each do one thing: createUser, createOrg, createWorkspace, grantCredits, activateSubscription. These are idempotent where possible and always create fresh state with unique identifiers (timestamps in the email address handle collision avoidance).

2. Scenario composers. Functions that compose primitives into named states. freshFreeOrg calls createUser then createOrg then createWorkspace. freshProWithCredit does the same plus activateSubscription and grantCredits. Adding a new scenario is a five-line function that calls existing primitives in a new combination.

3. Auth shortcut. After seeding, the panel logs the browser into the new account directly. No email verification, no password entry. This is dev-only, gated by environment checks. The session is real, so from that point forward the app behaves exactly as it would for a real user.

The key design decision: billing state is separated from Stripe. The activateSubscription primitive writes directly to the database. It sets the subscription status, plan tier, and credit balance without touching Stripe at all. For integration tests that need real Stripe, I have a separate path that uses Stripe test keys. But for 90% of manual testing, the stubbed path is faster and more reliable.


Environment Gating

This panel must never exist in production. The gating is simple but non-negotiable.

The component only renders when NODE_ENV === "development". The backend endpoints only register in dev mode. There is no feature flag, no admin toggle, no “hidden but accessible” state. The code paths do not exist in production builds. They are excluded at the bundler level.

This is not paranoia. A dev utils panel that can create accounts and grant credits is a security vulnerability if it ships. Dead code elimination is the only acceptable guard.


The Compound Return

The obvious return is time saved. But the deeper return is willingness to test edge cases.

Before the panel, I would skip testing awkward states because setup was annoying. “What happens when credits hit zero?” I knew I should test it, but getting a user into that exact state meant a five-minute detour. So I would tell myself I would test it later. I rarely did.

Now testing a zero-credit state is one click. So I actually test it. The panel did not just save time. It raised the quality floor by removing the friction that made thorough testing feel expensive.

This is the same dynamic that makes TanStack Query DevTools valuable. It is not that you could not inspect query state without the panel. You could open React DevTools, dig through the component tree, find the query client. But you would not do it often enough. The floating panel makes inspection so cheap that you do it constantly.

Cheap access changes behaviour. That is the real leverage.


When to Build One

If you find yourself doing any of these more than twice a week, build a panel:

  • Creating test accounts manually
  • Navigating through onboarding to reach a specific page
  • Wiring up billing state to test credit flows
  • Seeding data to test dashboard views
  • Switching between user roles or permission levels

The implementation is small. A floating button, a panel component, a few API endpoints, a handful of scenario presets. It is less work than most UI features you ship to users.

The principle is simple. Manual setup is hidden technical debt. Every repeated ritual is an API you have not written yet. Write it once, call it forever.


Related

Topics
AutomationDev PanelsDeveloper ExperienceSoftware ToolsWorkflow Optimization

Newsletter

Become a better AI engineer

Weekly deep dives on production AI systems, context engineering, and the patterns that compound. No fluff, no tutorials. Just what works.

Join 306K+ developers. No spam. Unsubscribe anytime.


More Insights

Cover Image for How to Protect Your Coding Harness from Longer Integration Test Runs

How to Protect Your Coding Harness from Longer Integration Test Runs

Integration test suites get slower under LLM-assisted development. Not because the tests themselves are fundamentally slow, but because agents have every incentive to bump the timeout when they hit it

James Phoenix
James Phoenix
Cover Image for Skills Need Evals, Not Vibes

Skills Need Evals, Not Vibes

A skill you cannot measure against no skill is just a prompt you felt good about. The whole value of a skill is the delta it buys you over a bare model, and that delta only shows up when you run both sides of the comparison.

James Phoenix
James Phoenix