Unit 01.04: Deciding the smallest API that does the job
Work down the list of capabilities and stop where the requirement stops.
Six stages, each adding something to operate
From a single endpoint to background workers, with what each stage buys.
The code lists them.
STAGES = [
("one endpoint, no auth, no database", "a prototype someone can call"),
("+ typed request and response models", "a contract that fails loudly"),
("+ tests against the contract", "changes you can make safely"),
("+ an API key and rate limits", "something you can expose"),
("+ a database and migrations", "state, and everything that implies"),
("+ background workers and a queue", "operations, monitoring, retries"),
]
print(f"{'stage':44} what it buys")
for stage, buys in STAGES:
print(f"{stage:44} {buys}")
print("""
Work down this list and stop where the requirement stops. Each row adds
capability and adds something to operate, and the last two add a great deal.
The commonest over-build is a database in week one for data that could have
lived in the request.
""")
The last two rows add substantially more than they appear to. A database brings migrations, backups, connection limits and a whole class of failure; a queue brings a broker, a worker deployment and a way to report status back.
The commonest over-build is the database in week one, for data that could have lived in the request and been returned immediately.
The mistake this prevents
The mistake is building the full stack because production will eventually need it. Each stage you add before the requirement arrives is something to operate, secure and debug while it is doing nothing.
Takeaway
Add capability one stage at a time, and only when a requirement forces it. State and queues are the two that add the most operational weight.
