2. Configuration Is Code

A reflection on the hidden costs of treating configuration as static data rather than executable software.

Reflections
July 19, 2026
Configuration Software Architecture Internal Tools

In our first iteration, almost the entire behavior of our system lived inside Google Sheets.

Routing rules, script parameters, workflow definitions, and operational settings were all crammed into cells. At first glance, the obvious engineering fix seemed simple: just migrate everything to a relational database.

But simply changing where you store configuration doesn't change what configuration is.

Our problem wasn’t actually Google Sheets. Our problem was that our configuration had quietly matured into software—without ever being treated like software.

Configuration Has a Lifecycle

As developers, we treat application code with immense respect. We understand that code is a living, evolving entity. It has structure, dependencies, validation, pull requests, peer reviews, version history, refactoring cycles, and documentation.

Configuration, however, is routinely treated as an afterthought. We dismiss it as static data—just a collection of passive strings that happen to nudge application behavior.

In reality, configuration evolves just as quickly as code—often faster.

  • Every new workflow introduces a fresh set of settings.
  • Every new third-party integration adds a dozen parameters.
  • Every operational edge case spawns a new feature flag.

Eventually, configuration ceases to be "settings" and becomes the very logic that describes how your system behaves. At that exact threshold, your configuration is code. Ignoring this reality doesn't make the complexity disappear; it just banishes it to a place where it is incredibly difficult to manage.

The True Cost of "Free Text"

The primary flaw of spreadsheet-based configuration wasn't the storage medium. It was the absolute, chaotic freedom it allowed.

Every cell accepted arbitrary text. A routing rule could effortlessly reference a workflow that didn't exist. A parameter could contain a subtle, unescaped character. A typo could silently sever an entire execution path.

Because our Python runtime had no schema to validate against, it simply blindly executed whatever string it ingested.

[Free-Text Spreadsheet] ──(No Validation)──> [Python Runtime] ──> Silent Runtime Crash

We spend an enormous amount of engineering effort validating user input at the boundaries of our applications, yet we blindly assume administrative configuration will always be perfect. In practice, configuration requires even stricter validation than user input. A bad user input breaks a single request; a bad configuration breaks the entire system.

Reuse Requires Rigid Structure

Our original architectural goal was the holy grail of automation: write a script once, reuse it everywhere.

But reality had other plans. Different workflows demanded different parameters. Those parameters had to be documented somewhere so operations knew which values were required, and developers knew which types were supported.

Without a central source of truth, parameter definitions quickly became scattered across random spreadsheets, stale comments, and deep implementation details.

Eventually, duplicating an entire Python script and modifying it became faster and less risky than trying to decipher how to safely configure the existing one. The scripts themselves weren't the issue. The issue was that our system lacked a formal, machine-readable contract describing what a script expected.

Without a schema, reuse is just a theory.

Configuration Should Describe Itself

As the chaos mounted, one core question began to eclipse all other implementation details:

What if our application could describe its own configuration?

What if, instead of documenting parameters manually in a wiki, the code itself exposed what it needed?

  • Instead of building custom admin forms, those forms were auto-generated from schema models.
  • Instead of maintaining manual dropdown lists, the options were populated dynamically from the system's state.
  • Instead of keeping documentation in sync with code, both originated from the exact same metadata.

The goal shifted from storing configuration to making configuration self-describing. Once your configuration carries its own schema, metadata, and constraints, the surrounding operational friction melts away. Documentation becomes a compiled artifact. Administrative interfaces become generic, plug-and-play UIs. Validation rules become reusable.

Adding a new feature no longer means building a custom settings page; it simply means declaring a new metadata schema.

Metadata Over Assumptions

This conceptual shift completely re-engineered our platform. We stopped focusing on individual workflows and began focusing on metadata modeling.

Every configurable component became a self-documenting asset:

  • Scripts declared their inputs, outputs, and expected data types.
  • Parameters contained explicit validation rules (e.g., regex constraints, value bounds).
  • Routing rules became structured relational entities rather than free-text spreadsheet rows.
  • Workflows, tasks, and execution stages were explicitly linked, rather than inferred from naming conventions.

The application stopped relying on the developer’s memory of how a script was supposed to run. Instead, the system itself became the authority on its own capabilities.

As we enriched this metadata layer, the platform grew increasingly generic—and incredibly powerful. Introducing a new script no longer required manual UI updates or configuration documentation. The same metadata powered our validation engine, generated our administrative dashboard, and compiled our system docs simultaneously.

The Shift

Looking back, our migration was never really about replacing Google Sheets with a database. It was about fundamentally changing the hierarchy of our software.

Originally, our configuration existed to support our code.

Eventually, our code existed solely to execute our configuration.

This distinction completely transformed how we design new features. Instead of asking, "Where should we store this new value?" we now ask, "How should the system understand this concept?"

Once you can express a business concept as structured metadata, database storage becomes the easiest and least interesting part of the architecture. The real challenge—and the focus of my next post—is turning that metadata into an executable engine: building a runtime capable of orchestrating complex, parallel business processes without having to deploy new code for every new workflow.