Production systems fail. Networks drop, APIs return 500s, databases time out. What's the core retry-loop shape — stripped to its essentials?
Try the operation. If it fails, try again, up to a cap. Return success or give up after the last attempt. The count of attempts is useful telemetry — if something consistently needs three tries, that's worth knowing.
Exactly. We'll model it with a simple stand-in: given a list of booleans where True is success and False is failure, find how many attempts it took to hit the first True. enumerate gives you both the index and the value in one pass:
for i, success in enumerate(attempts):
if success:
return i + 1
return -1Why i + 1? enumerate starts at 0?
enumerate gives zero-based indices. To return a count — "how many attempts" — you add 1. Index 0 → 1 attempt. Index 2 → 3 attempts. Or use enumerate(attempts, start=1) and return i directly:
for i, success in enumerate(attempts, start=1):
if success:
return i
return -1And -1 for no success? That feels fragile — what if a caller treats it as an index?
Sentinel values work when the domain is unambiguous. -1 is never a valid attempt count, so the caller's if result == -1: check is clean. The alternative is raising an exception — that's more Pythonic but forces every caller to wrap the call in try/except. Choose based on how you want failure to propagate.
So this exact loop shape underlies every real retry system. Add time.sleep(2 ** i) for backoff and it's production-grade.
That's the whole insight. Strip a pattern to its simplest form, learn the shape, then add the production knobs — backoff, jitter, circuit breaker — on top of the same five-line skeleton.
enumerate + sentinel patternTL;DR: iterate with enumerate, return on first success, return sentinel on exhaustion.
enumerate(xs) — yields (index, value) from 0enumerate(xs, start=1) — start from 1 for 1-based counts-1 — "no success" without raising| Style | Caller ergonomics | Fit |
|---|---|---|
return -1 | if r == -1: ... | expected failure |
raise RuntimeError | try/except | unexpected failure |
Choose based on whether permanent failure is a valid outcome or a bug.
Production systems fail. Networks drop, APIs return 500s, databases time out. What's the core retry-loop shape — stripped to its essentials?
Try the operation. If it fails, try again, up to a cap. Return success or give up after the last attempt. The count of attempts is useful telemetry — if something consistently needs three tries, that's worth knowing.
Exactly. We'll model it with a simple stand-in: given a list of booleans where True is success and False is failure, find how many attempts it took to hit the first True. enumerate gives you both the index and the value in one pass:
for i, success in enumerate(attempts):
if success:
return i + 1
return -1Why i + 1? enumerate starts at 0?
enumerate gives zero-based indices. To return a count — "how many attempts" — you add 1. Index 0 → 1 attempt. Index 2 → 3 attempts. Or use enumerate(attempts, start=1) and return i directly:
for i, success in enumerate(attempts, start=1):
if success:
return i
return -1And -1 for no success? That feels fragile — what if a caller treats it as an index?
Sentinel values work when the domain is unambiguous. -1 is never a valid attempt count, so the caller's if result == -1: check is clean. The alternative is raising an exception — that's more Pythonic but forces every caller to wrap the call in try/except. Choose based on how you want failure to propagate.
So this exact loop shape underlies every real retry system. Add time.sleep(2 ** i) for backoff and it's production-grade.
That's the whole insight. Strip a pattern to its simplest form, learn the shape, then add the production knobs — backoff, jitter, circuit breaker — on top of the same five-line skeleton.
enumerate + sentinel patternTL;DR: iterate with enumerate, return on first success, return sentinel on exhaustion.
enumerate(xs) — yields (index, value) from 0enumerate(xs, start=1) — start from 1 for 1-based counts-1 — "no success" without raising| Style | Caller ergonomics | Fit |
|---|---|---|
return -1 | if r == -1: ... | expected failure |
raise RuntimeError | try/except | unexpected failure |
Choose based on whether permanent failure is a valid outcome or a bug.
Write `count_retries_needed(attempts)` that takes a list of booleans (`True` = success, `False` = failure) and returns the one-based index of the first `True`. Return `-1` when no attempt succeeded. Use `enumerate` and a sentinel.
Tap each step for scaffolded hints.
No blank-editor panic.