Skip to content

docs(review-backend): flag guards that defend against developer errors#27

Merged
awrobel-gd merged 2 commits into
mainfrom
chore/review-backend-guard-classification
Jul 9, 2026
Merged

docs(review-backend): flag guards that defend against developer errors#27
awrobel-gd merged 2 commits into
mainfrom
chore/review-backend-guard-classification

Conversation

@mkonopelski-gd

Copy link
Copy Markdown
Contributor

What

Adds one new review item (#14) to .claude/commands/review-backend.md, under the 🟡 HIGH — CLAUDE.md coding pattern adherence section, right after item #13.

Why

Reviewers and coding assistants need a crisp rule for classifying every if/else and try/except by what it guards against:

  • Runtime/external conditions (user/API input, network, filesystem, DB availability, third-party payloads, parsing untrusted data, concurrency races) — the value can be wrong for reasons outside our code's control. Keep the guard and handle it gracefully.
  • Developer error / internal invariant (an internal constant, an Enum member, a dict key that exists by construction, a field our own code sets, a registry entry) — the value can only be wrong if some other part of OUR code is wrong. A defensive branch here adds dead branches to the hot path and hides the bug. Delete it, let the operation raise naturally (KeyError/AttributeError/TypeError), and cover the invariant with a unit test.

The item ships a one-line decision heuristic, a concrete example grounded in the SQLite table-name registry (table = _TABLE[name], not if name not in _TABLE: raise ...), and cites Python EAFP and PEP 20 ("Errors should never pass silently", "Explicit is better than implicit").

It is explicitly framed as the sibling of existing item #12 (asserts forbidden outside tests) so the two rules read as one philosophy: internal invariants belong in unit tests, not hot-path branches.

Scope

Documentation only — one file, no application code touched.

…s instead of runtime input

Add review item #14 teaching reviewers to classify every if/else and
try/except by what it guards against. A guard is legitimate only when the
value can be wrong for reasons outside our code's control (user/API input,
network, filesystem, DB availability, third-party payloads, concurrency).
A defensive branch on a trusted internal value — an internal constant, an
Enum member, a dict key that exists by construction, a field our own code
sets, a registry entry — guards against a developer error: it adds dead
branches to the hot path and hides the bug. Such invariant violations
belong in a unit test, not a runtime branch; let the operation raise
naturally (KeyError/AttributeError/TypeError) per Python EAFP, since a
swallowed or re-wrapped developer error passes silently past the suite
(PEP 20). This is the sibling of item #12 (asserts forbidden outside tests).
Turn item #14's single SQLite example into a "Real examples from this
repo" list with two more verified cases from the DB layer on this branch:

- db_adapter.py's repeated `and "_id" in r` re-check normalizing query
  rows — the IDatabase.query() contract guarantees `_id`, so the extra
  check defends against a backend breaking its own contract (developer
  error); prefer `r["id"] = r["_id"]` and unit-test the invariant.
- memory.py `_matches_filter` has no `case _:` default, so an unsupported
  operator silently matches nothing while firestore.py `_apply_filter`
  raises — operators are hardcoded literals in our own query code, so a
  bad one is a developer error the memory backend lets pass silently
  (PEP 20), diverging from Firestore. Add a raising default and test it.

@awrobel-gd awrobel-gd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving but still think this change is verbose. Maybe it can be compressed

@awrobel-gd awrobel-gd merged commit b824b3a into main Jul 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants