Skip to content

Tighten the release process: two identical faults reached dev and were found by hand #40

Description

@Devski

What happened

On 05.09.2026 the same root cause shipped to dev twice, and both times the person who found it was Dawid, on his phone, using the product.

  1. Changing a profile address failed with Saving failed. Try again.
  2. Uploading a photo failed with Something went wrong. Try again.

Both were one mistake: a new CHECK constraint plus an upsert that spelled a now-empty column into its INSERT values. PostgreSQL evaluates constraints on the tuple being inserted before ON CONFLICT turns it into an update, so the constraint fired on writes that never touched the constrained column.

The gate was green for both. The deployment reported healthy for both.

Why nothing caught them

The full journey does not run on a pull request, or on a push to main. e2e-full — register → verify → name → address → photo → public page — runs only on workflow_dispatch and on version tags. Both faults sit squarely on that path. This was raised on 05.09.2026 and deliberately deferred; the deferral is what let both through. Measured cost of running it: 227 seconds.

The unit fixtures describe a world that no longer exists. Every account in profile.test.ts and profile-handle.test.ts is created with a non-empty users.name. Registration stopped filling that field in #36 — so the tests kept proving the old reality, and the new one was untested by construction. A change to a shared assumption made every existing test simultaneously less true and still green.

The deploy proves liveness, not function. remote-deploy.sh waits for the container's HEALTHCHECK, which asks whether the process answers. Neither fault stopped it answering.

One fix, one instance. The first was fixed in setHandle while the comment beside it read "setAvatar does the same". Nothing made the second location visible; nothing checked whether the class was closed rather than the case.

Acceptance criteria

  • The full journey runs on every pull request and every push to main, not only on demand
  • At least one fixture account per database-backed suite is created the way registration actually creates one, so a change to that path breaks tests instead of hiding in them
  • The deploy verifies one real write end to end after the container is healthy — not merely that it responds
  • Adding a database constraint forces the paths that write that table to be exercised: an inventory test, a checklist in the migration template, or an equivalent that fails when it is skipped
  • A written rule for fixing a class rather than a case: when a fix touches a shared pattern, the other occurrences are enumerated in the change

Verification

  • Re-introduce either fault on a branch and confirm CI is red before a human sees it
  • Confirm the added time per run against the measured baseline (a normal run bills about 7.6 job-minutes today)

Why this matters more than the two bugs

Both were cheap to fix and neither destroyed data. What they cost was confidence: two stages of a pipeline passed something a person found in under a minute of ordinary use. A release process that needs the product owner as its last line of defence is not a release process.

Spec: SPEC.md §6 · Size: M · Label: enhancement

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    deploymentHow code reaches an environment: pipeline, release procedure, infrastructure

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions