runtime
250 / 250
lesson 250 Production Readiness Capstone · advanced

Decide whether to release

The last review before a release gathers the evidence the whole course has taught you to produce: tests pass, dependencies are locked, settings load, the schema is current, and a smoke run on a fixture gives the known answer. A release script turns it into one go/no-go report, with a reason for every blocker.

You'll be able to
  • Run each release check and record its result and detail, treating missing evidence as a failure
  • Smoke-test the pipeline on a fixture: identical output on every run, matching a stored golden hash
  • Decide go/no-go: required checks must pass, optional ones become warnings, and every blocker has a reason
The idea

Releasing is a decision, and a good decision rests on evidence, not on "it worked on my machine". Each module of this stage produced some:

  • Tests pass (L02, the Application Delivery module's CI gate).
  • Dependencies are locked: every package pinned with a hash, no drift (see Application Delivery).
  • Settings load and validate for the target environment (see Application Delivery).
  • The schema is at the version the code needs (see Application Delivery).
  • A smoke run of the real pipeline on a small fixture gives exactly the known answer.

The smoke run uses a golden hash: the SHA-256 of the fixture's report, serialised the same way every time (json.dumps(..., sort_keys=True)), stored when the output was last reviewed by a person. If the hash changes, either the code changed behaviour on purpose (review the new output, update the hash) or by accident (don't ship). Running it twice also catches output that isn't deterministic.

A release report lists every check with its result and detail, separates blocking failures (required checks) from warnings (optional ones, such as a slower benchmark), and treats missing evidence as a failure: a check that didn't run hasn't passed.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · A release report
import io
import json
import unittest

def borough_revenue(trips):
    revenue = {}
    for borough, total in trips:
        revenue[borough] = round(revenue.get(borough, 0) + total, 2)
    return revenue

class CoreTest(unittest.TestCase):
    def test_adds_per_borough(self):
        self.assertEqual(borough_revenue([("Queens", 66.71), ("Queens", 70.0)]), {"Queens": 136.71})

def check_tests():
    result = unittest.TextTestRunner(stream=io.StringIO()).run(unittest.defaultTestLoader.loadTestsFromTestCase(CoreTest))
    return result.wasSuccessful(), f"{result.testsRun} tests, {len(result.failures) + len(result.errors)} failing"

def check_lock(lock_text):
    unpinned = [line for line in lock_text.splitlines() if line and not line.startswith("#") and "==" not in line]
    return not unpinned, "all pinned" if not unpinned else "unpinned: " + ", ".join(unpinned)

def check_settings(environ):
    missing = [name for name in ("APP_ENV", "DATABASE_URL") if not environ.get(name)]
    return not missing, "settings complete" if not missing else "missing " + ", ".join(missing)

report = {}
for name, check in [
    ("tests", check_tests),
    ("lock", lambda: check_lock("numpy==2.1.3\npandas>=2.2\n")),
    ("settings", lambda: check_settings({"APP_ENV": "prod", "DATABASE_URL": "sqlite:///market.db"})),
]:
    ok, detail = check()
    report[name] = {"ok": ok, "detail": detail}

blocking = sorted(name for name, result in report.items() if not result["ok"])
print(json.dumps(report, indent=2))
print("decision:", "go" if not blocking else "no-go", blocking)

A release script must also *act* on the decision: in CI it exits non-zero so the deploy step never runs (see Application Delivery). Here an assertion plays that part, with the blockers in its message:

example-2.py · The release script refuses to continue
report = {
    "tests": {"ok": True, "detail": "42 tests, 0 failing"},
    "schema": {"ok": False, "detail": "schema at 2, release needs 3"},
    "smoke": {"ok": False, "detail": "report hash changed"},
}
blocking = [f"{name}: {r['detail']}" for name, r in report.items() if not r["ok"]]
assert not blocking, "release blocked by " + "; ".join(blocking)
print("deploying")
example-3.py · A smoke test with a golden hash
import hashlib
import json

def run_report(trips):
    boroughs = {}
    for trip in trips:
        entry = boroughs.setdefault(trip["borough"], {"trips": 0, "revenue": 0.0})
        entry["trips"] += 1
        entry["revenue"] = round(entry["revenue"] + trip["total"], 2)
    return {"boroughs": boroughs, "rejected_lines": [3]}

def fingerprint(report):
    text = json.dumps(report, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(text.encode("utf-8")).hexdigest()

FIXTURE = [{"borough": "Manhattan", "total": 64.19}, {"borough": "Brooklyn", "total": 18.7}]
GOLDEN = "f3e2b2185f8c0794a51b2167db7f26dbfe31a639af1e9804a8a2df6a642fddbc"

first = fingerprint(run_report(FIXTURE))
second = fingerprint(run_report(FIXTURE))
print("deterministic:", first == second)
print("matches golden:", first == GOLDEN)

def run_report_changed(trips):
    report = run_report(trips)
    for entry in report["boroughs"].values():
        entry["revenue"] = round(entry["revenue"])
    return report

print("after a rounding change:", fingerprint(run_report_changed(FIXTURE)) == GOLDEN)

When to use it: for every release of anything people depend on, automated in the pipeline after the tests. Mistakes to avoid: a release decision with no reasons; treating a check that didn't run as passed; golden outputs serialised without sort_keys (they change for no reason); updating the golden hash without reviewing the new output; and warnings nobody reads, which are blockers waiting to happen.

Review
  • A release decision rests on evidence: tests, lock, settings, schema, and a smoke run with a golden hash.
  • Report every check with its detail; required failures and missing results block, optional failures warn.
  • Fingerprint canonical output (sort_keys=True), run it twice, and review any change before updating the golden value.
  • That completes the course: you can now read, write, test, secure, operate and ship real Python programs.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Decide whether to release — Production Readiness Capstone · Runtime