Logo

Building Against a Moving Spec: Deferring Integrations and Knowing When to Commit to the DB

7 min read
ArchitecturePlanningAI

Table of Contents

Intro

By the time the app was deployed behind SSO, the requirements had moved. A new design draft landed with features the first one didn't have, and a separate requirements list assigned each one a priority. That's normal for an internal tool — the spec firms up as people see a running version.

The interesting part isn't the features. It's how to absorb a moving spec without either thrashing (rebuilding on every new draft) or freezing (refusing to build until everything's final). This article is the triage discipline that kept the build moving, and the call on when to finally commit to a database.

Read the priorities, not just the wishlist

The new draft added several things: extra fields, a redesigned scheduling view split into two modes, and a bulk-import flow. My first instinct was to diff the two drafts and build everything new. That instinct was wrong, and the requirements list said why.

Each requirement carried a temperature: must-have, nice-to-have, or phase 2. Cross-referencing the shiny new features against that list flipped the plan:

Building the headline features would have meant investing most in the work most likely to change (it depends on an external API spec that doesn't exist yet). Priority labels are a filter on enthusiasm. The right move was to skip the exciting-but-deferred work and ship the boring-but-ready piece.

Defer integrations, not the data they imply

There's a subtlety in deferring integration-heavy features: separate the integration from the data shape it implies.

A couple of fields from the deferred integration turned out to be reclassified, on a closer read of the requirements, as fields the team already tracks manually (must-have) rather than parts of the integration (phase 2). The integration — syncing with the external system — stays deferred. But the columns are cheap to add now and are genuinely required. So they go into the schema; the sync logic doesn't.

The reverse also happened: a feature marked "done" in the new draft was, on inspection, still under active review for its exact shape. That one I modeled as a future additive table — noted in the plan, not built — so it wouldn't block the core schema and wouldn't get built twice.

The general rule: defer the behavior, but let settled data shapes land early if they're cheap and additive. Columns are easy to add now and easy to add later; the cost of guessing wrong is low. Integrations are expensive and depend on external unknowns; defer those hard.

The plan doc absorbs the churn

When the spec moves, something has to be the record of "what we decided and why," or the next session (human or agent) re-litigates it. For this project that's the plan doc — read on demand, not auto-loaded.

Every triage decision went in: the requirements table with each item's priority and disposition (done / deferred / phase 2), the rationale for deferring the integration work, the reclassified fields, the future-additive-table note. So when an agent picks the project back up, "why isn't the fancy scheduler built?" has a one-line answer in the doc instead of a re-derivation from the drafts.

This is the same division of labor from earlier in the series: the always-loaded conventions file says how to write code; the plan doc says what we're building and why, including the decisions that aren't obvious from the code. A moving spec is exactly when that second doc earns its keep.

When is the schema "stable enough" for the DB?

The whole point of building UI-first on mocks was to defer the database until the schema stopped moving. So: had it stopped?

The honest test isn't "is the spec final" — it never is. It's "are the things still in flux the kind that would force a painful migration, or the kind that's cheap to add later?"

Going down the open items:

Everything still moving was additive (new columns, new tables) rather than structural (reshaping or relating existing tables). And — the part that makes this easy — there's no real data yet. Pre-launch, with migrations generated by the ORM, adding a column or table is a one-liner and a reset; the scary part of schema change (migrating and back-filling live data) doesn't exist.

So: stable enough. Not because the spec is frozen, but because the remaining unknowns are the cheap-to-change kind, and the expensive-to-change parts have settled. That's the bar — not finality, just "the risky stuff is settled and the unsettled stuff is cheap."

Track phases as issues

One small process habit that paid off as the plan grew: each phase gets a tracking issue, opened as a sub-issue of the project epic, with the phase scope as a checklist. It sounds bureaucratic for a solo-ish build, but it does two things. It gives the agent an explicit, linkable unit of work to check off as steps land. And it makes the project legible to anyone glancing at the issue tracker — done phases closed, the current phase open with its checklist visible. I even backfilled issues for the already-finished phases, so the epic reads as a clean progression rather than starting mid-stream. I added an instruction to the plan doc to open one at the start of every new phase, so it doesn't depend on me remembering.

Wrapping Up

A moving spec is the default condition for an internal tool, not an exception to manage around. The build stayed healthy because the mock-first foundation made schema churn cheap, and because every new draft got triaged against priority instead of built wholesale.

Key takeaways:

  1. Triage new requirements against their stated priority before building. The flashiest additions are often the lowest priority — and the ones most likely to change.
  2. Separate the integration from the data shape it implies. Defer the external-system behavior; let settled, cheap, additive fields land now.
  3. Let the plan doc absorb the churn. Record each triage decision and its rationale so the next session doesn't re-litigate it.
  4. "Stable enough for the DB" ≠ "spec is final." It's "the expensive-to-change parts have settled, and what's left is cheap and additive." Pre-launch with ORM migrations, that bar is low.
  5. Track phases as issues. A checklist per phase, backfilled for history, makes the work legible and gives an agent a unit to close.

That's the series so far: stack and infra planning, the AI workflow, the mock-first UI build, the Next.js implementation, the gated deployment, and now absorbing a moving spec. Next in reality is the database itself — turning the mock store into Postgres on Cloud SQL, which is finally derisked enough to be worth doing.

Project Navigation

  1. 1.Picking the Stack for an Internal Workflow Tool
  2. 2.Setting Up the AI-Assisted Workflow Before Writing Code
  3. 3.Building the UI First, Against a Mock Data Layer
  4. 4.Implementing a Multi-Step Workflow UI on Next.js 16
  5. 5.Shipping to Cloud Run: Slack SSO, an IP Allowlist, and Keyless CI/CD
  6. 6.Building Against a Moving Spec: Deferring Integrations and Knowing When to Commit to the DB