The right picture, delivered into the wrong rectangle
Resolution, crop and reserved space solve different image problems. Design all three around what the reader needs to see.
A reassuring summary cannot tell you whether a validator preserves old identifiers, rejects trailing text or understands the contract. Eight small inputs can.
The summary promises strict validation without breaking compatibility. Two tests pass. Reading the expression, though, shows that an old customer identifier has no way through it, while a valid-looking new one can bring extra text along. The example is authored for this review exercise, so we can inspect both disagreements directly. It is the gap between the very plausible explanation and the function’s actual answer that deserves the reviewer’s attention.
An assistant’s summary can be a useful introduction to a patch, just as a colleague’s can. In either case, it describes what the author thinks the change does. I would first read the requirement and the diff here, then return to the account of them. An old identifier gives that reading a concrete question to resolve before the summary supplies its own explanation of success.
The initial question is small enough to answer from examples: which complete customer identifier strings does this interface promise to accept? Keep the two accepted formats in view while reading, rather than trying to judge whether the regular expression looks sufficiently strict. Once those examples have an independent place in the review, the later explanation can help account for the code instead of supplying the only definition of success.
The example contract accepts two complete formats: a legacy identifier such as AB-1234, and a current identifier such as CUST-2025-0007. Prefix letters are uppercase ASCII and the digit positions contain ASCII digits. Leading or trailing material is invalid. Empty input is invalid. These choices belong to this invented interface; another system might correctly accept a wider character set or normalise whitespace before validation.
That last distinction matters. Normalising an identifier and validating an identifier are different operations. If surrounding whitespace is permitted at an input boundary, decide where it is removed and whether the stored identifier changes. Quietly trimming it inside one validator can make two services disagree about the same submitted string. Equally, a blanket ban on Unicode would be an absurd general rule for customer names. Here the narrow alphabet belongs to a specific machine identifier contract.
Before reading the proposed tests, set out examples for each obligation: an old identifier, a new identifier, extra text after a valid-looking prefix, a different digit family, an empty string, wrong letter case, a leading character and a trailing newline. Each probes a separate interpretation of the contract. The cases are useful because their expected outcomes come from that agreement, independently of which examples the implementation already handles.
The proposed function first checks that the value is a string, then calls re.match with the expression ^CUST-\d{4}-\d{4}. It returns true whenever a match exists. The current example passes, and empty input fails. Those two outcomes can make an accompanying test file appear reassuring without touching the compatibility promise at all. AB-1234 never has a route through the expression: its rejection follows directly from the required CUST prefix.
The next problem is where matching stops. Python documents re.match as matching at the beginning of the string; it does not require the pattern to consume the whole string. [1] A valid-looking identifier followed by extra text therefore succeeds. So does the same prefix followed by a newline. The code has checked for a recognisable beginning while its explanation promises a valid complete value. That is a behavioural difference, rather than a disagreement about preferred regular-expression style.
There is another small but consequential choice in \d. For Unicode string patterns, Python’s documentation describes it as matching Unicode decimal digits, not just the characters zero through nine. [1] The Arabic-Indic example in the fixture therefore passes the proposed expression. A reviewer cannot settle whether that is a bug from the expression alone; the interface contract settles it here. Replacing \d with [0-9] is appropriate because this particular contract requires ASCII digits, not because wider character support is inherently suspect.
Finally, appending a dollar sign is not a complete repair. Python documents its behaviour before a final newline, and it would still leave the legacy-format omission and the digit-class decision unresolved. The corrected teaching function uses re.fullmatch with an explicit alternative for both formats. Full-string matching answers the boundary question; the alternatives answer compatibility; the character classes answer the alphabet. Three promises require three separate decisions.
An original proposed validator passes its two narrow self-tests but matches only four of the eight independently specified outcomes. The corrected expression is checked against the same inputs.
Scroll sideways to see all columns.
| Input case | Contract | Proposed | Corrected |
|---|---|---|---|
AB-1234 · legacy | Accept | Reject | Accept |
CUST-2025-0007 · current | Accept | Accept | Accept |
| Current ID + extra | Reject | Accept | Reject |
| Arabic-Indic digits | Reject | Accept | Reject |
| Empty string | Reject | Reject | Reject |
| Lowercase prefix | Reject | Reject | Reject |
| Leading text | Reject | Reject | Reject |
| Trailing newline | Reject | Accept | Reject |
The downloadable program contains both authored implementations. Its narrow self-test set checks only the current identifier and empty input: the proposed implementation passes both. The independent eight-case set gives it four correct outcomes, while the corrected expression matches all eight expected outcomes. Keep the failing strings available alongside those counts. A four-of-eight result names a disagreement, but the legacy value, trailing material and digit family tell the reviewer which promises the implementation omitted.
Four failed cases need not mean four independent repairs. Trailing text and a final newline both expose prefix-only matching; rejecting the legacy format and accepting another digit family concern different promises. Group the failures by the rule each exercises before changing the code. That gives the reviewer a reason for each part of the corrected expression, rather than eight isolated answers to memorise. These are contract examples, not a sample from which to estimate how often AI-written code fails.
The downstream use helps decide how much the mistakes matter. If an import still contains AB-1234 records, rejecting them can interrupt a migration that was supposed to remain compatible. If search accepts trailing text but storage rejects it, the same submitted identifier appears valid at one step and fails at the next. Those are better reasons to change the expression than a preference for shorter or cleverer regex. They also give the author a way to understand why a small-looking validation comment matters to somebody trying to complete ordinary work. The review can stay focused on compatibility and complete-input behaviour rather than become a contest over pattern style.
These eight inputs are strings. If callers can also supply numbers or null, decide whether to reject them or perform an explicit conversion before pattern matching. Quietly converting an account number to text can change which inputs the interface accepts, just as silently trimming whitespace can. Put those outcomes beside the existing boundary cases so the review preserves the intended input contract.
The identifier exercise keeps compatibility, input boundary and alphabet decisions independent.
Scroll sideways to see all columns.
| Promise | Insufficient repair | Required decision in the fixture |
|---|---|---|
Legacy AB-1234 remains accepted | Add an end anchor to a CUST-only expression | Include both accepted formats |
| Trailing text is rejected | re.match checks only a beginning | Use complete-string matching |
| Digit positions are ASCII | Unicode \d admits other decimal digits | Use the contract’s explicit character class |
| Final newline is rejected | A dollar anchor has documented newline behaviour | Check complete input with fullmatch |
A useful review comment would name the observed behaviour: “AB-1234 is part of the legacy contract, but this expression rejects it; CUST-2025-0007extra is outside the contract, but it passes.” It can then ask for full-string matching, explicit format alternatives and tests for the exact boundary cases. This is more actionable than “make validation more robust”, which leaves the author to guess which promise the reviewer thinks is missing.
Keep the eventual repair proportionate. This change does not require an identifier framework, a new dependency or a wholesale rewrite of the surrounding customer flow. It does require agreement on how existing records enter the system and where normalisation occurs. If the real interface specification is unclear, record that uncertainty rather than allowing whichever regex arrived first to become the accidental business rule.
After revision, follow the expected answers as well as the expression. GitHub’s review guidance makes the changed lines and their discussion available together. [2] AB-1234 should still expect acceptance; CUST-2025-0007extra should still expect rejection. If the patch instead changes those expectations to match the faulty function, its green run records a withdrawn compatibility promise and an expanded input boundary. Compare each new expectation with the original interface before approving it.
Now return to the summary. It should say that both formats remain accepted, that matching covers the complete input and that the digit alphabet is explicit. The next reviewer can follow each claim into one of the eight expected outcomes without reconstructing the prompting conversation. That is a useful job for an explanation: preserve the reasoning behind the change after the behaviour has been checked, so the next tidy-up does not accidentally reopen the same agreement.
Working on a workflow like this? Hamsa helps finance and operations teams connect their tools and handle the exceptions, starting with one workflow.
See the Payment Operations PilotPrimary sources behind this article. Living documentation may change.

Practical engineering. A human perspective.
More about Hamsa Software and the person behind it. Editorial notes.