Software maturity

A project's maturity is declared, not inferred: task-plus.yml states the level the project has reached on each ladder, and task-plus enforces the practices that level requires and reports the gaps. The ladders are the contract; the enforcement details live in task-plus and are linked from here, so this page stays true when a command changes shape.

There are two ladders rather than one, because how code reaches main and how a release reaches a running system move independently: a project can deploy every release and sense-check it while still committing straight to main, and a trunk-based project can run blue-green. Each ladder is strict — a level includes everything below it.

Code flow

How a change reaches main.

Level Name Practice task-plus enforces
0 Trunk Commit on main; releases are cut from it nothing new
1 Branches One branch per story, merged to main by a pull request on the repo's own forge, fast-forward or rebase; main is protected against direct pushes and is always releasable tp release refuses to run off main; tp pr opens the pull request for the current branch; the release commit itself goes through a pull request
2 Gated CI runs the project's check task on every pull request; merge is blocked until it is green tp check confirms the forge has branch protection and a CI config for main

The release is a write to main like any other, so it goes through a pull request like any other. On a protected main, tp release puts the rolled changelog on a release/vX.Y.Z branch and opens the pull request; a person merges it, and a second tp release tags the merged commit and publishes. No account is whitelisted for pushes, so the gate holds for everyone and every commit on main has been through the same door. Learned on gobank's first protected release, which Forgejo refused and tp release rolled back; until task-plus ships this, a release on a protected main needs the rule relaxed for that one push.

Level 1 is not about review. A solo project with an agent gains little from a second pair of eyes on every merge; it gains a main that is deployable at any moment, which the deployment ladder from level 2 upward depends on. Review, when a project wants it, is a forge setting on top of level 2, not a level of its own.

Deployment

How a release reaches a running system, and what that system promises while it happens. Each level is a deployment strategy paired with the reliability metrics it commits to; the metrics are what make the level testable rather than aspirational.

Level Name Strategy Downtime per upgrade Data loss (RPO) Recovery (RTO) task-plus enforces
0 Manual Deployed by hand when someone remembers unmeasured unmeasured unmeasured nothing
1 Released Every story ships as a release that is deployed and sense-checked before the next story starts the whole run restarts; state is not kept everything since the run began a fresh run post_release deploy hook present; release not closed until the deploy reports
2 In place Stop, replace, start on the same host; schema migrations are versioned and expand/contract; rollback to the previous release is rehearsed the restart, measured and recorded per deploy (minutes) none: every committed fact survives restart time, or rollback time if the release is bad a migration version table exists; the deploy log records downtime per upgrade
3 Zero downtime Blue-green or rolling behind a load balancer; old and new versions serve together, so migrations must be compatible with both none: every request during the deploy is served none seconds (switch back) deploy log shows requests served across the switch
4 Multi-region Level 3 in more than one region; the loss of a region is survived none, including for a region outage none automatic failover —

Downtime is therefore not a separate concern bolted on to a deployment strategy: it is the metric that distinguishes the levels. A project at level 2 is allowed downtime, and says how much; moving to level 3 is the decision to stop allowing it, and that decision costs the ability to run a single process against the database.

Declaring a level

# task-plus.yml
maturity:
  code: 1
  deploy: 2

tp check reports each ladder's level and the practices still missing for the declared level, and warns when a project exceeds a level it has not declared (a project at deploy: 1 with branch protection on main is probably at code: 2 and should say so). Raising a level is a story in the project that adopts it, because each one changes the project's day-to-day flow; lowering one is an ADR.

Where this is implemented, and the exact checks, is task-plus's maturity page. Until that ships, the table above is the specification it is built to.

Example: gobank

Today Next Later
Code 0 1 — from the first ADR-0002 stage 2 story 2 — CI on the Bytestone Forgejo
Deploy 1 — every story deployed to Hetzner and sense-checked 2 — stage 2, the demo resumes from its database and each later story is an upgrade drill 3 — stage 8, several processes, blue-green; 4 — phase 4, CockroachDB across regions

Downtime is accepted at deployment level 2 and recorded per upgrade, so there is a number to drive to zero when level 3 arrives.