Reconstruct the contract before correcting
An incorrect result can originate long before the line displaying it. Follow textual input, parsing, validation, transformations, retained state and publication. For each stage, write what has been established and what still needs checking. Valid JSON establishes syntax; it does not establish ID uniqueness, quantity bounds or fidelity of a historical copy.
Also distinguish value from identity. ID "07" may need to retain its leading zero, while units "2" may need rejection because it is not a Number. Applying a conversion indiscriminately to both fields changes the contract. Review aims to locate the first boundary losing information or accepting a wrong premise, then correct that decision without inventing data for later steps.
Keys and relationships across records
A Map retains one current value per key: a second set replaces that value without automatically creating history. If the contract requires rejecting duplicates, building a Map and observing only its final result can hide two rows having the same ID. Check the rule during preparation and retain an explicit rejection classification.
Key equality must also be intentional. For exact textual IDs, do not convert "01" into 1. For names grouped by Unicode canonical equivalence, a normalize("NFC") key can be appropriate while retaining original text for display. That policy is not automatically correct for every identifier. Uniqueness and normalization are domain requirements, not effects to accept merely because a data structure made them convenient.
Approved state and current state
An approval needs to retain the values that were accepted. Spreading the outer object does not isolate a nested rules array or the objects inside that array. Freezing the outer object does not recursively freeze those levels either. If the schema is known and contains simple records, project approved fields into independent objects and copy every required mutable level.
The same reasoning applies to functions and read timing. Bind fixes a method receiver; it does not freeze properties that method will read. A local variable copying a Number before await retains that value, while reading the shared binding after await can observe an update. To reproduce a decision, retain the inputs and policy version that actually governed it rather than reconstructing them later from current state.
Temporal order and operation ownership
Two strings with different offsets can sort differently from the instants they represent. 09:20+01:00 corresponds to 08:20Z, preceding 08:30Z. A temporal report should validate its expected format, interpret the offset and compare instants. String length, arrival order or local clock time alone do not provide that relationship.
In asynchronous coordination, an old operation can also finish after a new one. If active already belongs to B, The finally block for A must not clear that reference. Check identity or generation before changing shared state. Losing the B controller reference does not cancel B; it only weakens the control the application retained. Apply the same ownership rule to success, error and cleanup without relying on fixed delays.
Explicit failure and outcomes by identity
catch(() => []) turns a rejection into a successful empty list. That may meet an explicit fallback contract, but it is misleading when zero means a valid search found no records. In that situation, retain a separate failure state and display a count only when accepted data exists. The visible message can be short, but internal classification should distinguish failure origin.
For a report needing every read outcome, allSettled retains one record per input position. Link each position to its corresponding ID and preserve status, value or reason. Do not assign the first result to every ID or infer from all rejection that every input failed. Useful aggregation preserves relationships among requests as well as differences among their outcomes.
Guided example: accept an entire batch
prepareBatch separates syntax failure, batch shape, row contract and duplicate ID. The candidate is local and contains only id and units. For [{"id":"01","units":2},{"id":"1","units":3}], IDs are distinct and the total is 5. A second row with id "01" produces duplicate; a string quantity "3" produces row. Neither rejection returns a partial candidate.
The Set uses strings exactly as received. The function neither trims spaces nor normalizes Unicode because that is not the stated policy. Up to 1000 quantities from 0 to 50 yield a maximum total of 50000, within the exact integer range. An empty batch is accepted with total zero. An application may then replace a local variable with the accepted candidate; this preparation establishes neither atomicity of multiple remote writes nor a transaction system.
Justify a correction and its limits
A sound answer identifies the decisive fact, the applied rule and the conclusion limit. In configuration history, the fact is shared mutable levels; the correction is a schema-appropriate copy. In an asynchronous report, the fact is distinct outcomes per ID; the correction preserves that association. In module loading, an HTML body establishes a delivery failure before discussing the internal logic of the intended file.
Also examine alternatives: a solution that only renames values, adds a delay or turns failure into apparent success can hide the symptom without meeting the contract. Final questions combine fundamentals, application and diagnosis with single or multiple choice. Assessment requires reasoning about supplied facts; it does not depend on running code, a specific framework or reproducing a production environment.
function prepareBatch(text) {
let rows;
try {
rows = JSON.parse(text);
} catch (error) {
if (error instanceof SyntaxError) return { ok: false, reason: "syntax" };
throw error;
}
if (!Array.isArray(rows) || rows.length > 1000) {
return { ok: false, reason: "batch" };
}
const seen = new Set();
const candidate = [];
let total = 0;
for (const row of rows) {
if (row === null || typeof row !== "object" || Array.isArray(row) ||
typeof row.id !== "string" || row.id.length === 0 ||
!Number.isInteger(row.units) || row.units < 0 || row.units > 50) {
return { ok: false, reason: "row" };
}
if (seen.has(row.id)) return { ok: false, reason: "duplicate" };
seen.add(row.id);
candidate.push({ id: row.id, units: row.units });
total += row.units;
}
return { ok: true, rows: candidate, total };
}Assume text is a string, JSON.parse has no reviver, and standard methods are used. The contract accepts up to 1000 records, including an empty batch, nonempty string IDs with exact textual equality, and integer Number units from 0 to 50. Additional properties are ignored.
The example prepares a local candidate; it performs no remote operations.
Common pitfalls
Converting IDs to simplify arithmetic; retaining only a shallow copy of approved state; presenting failure as empty data; clearing newer operation state; inferring temporal order from text.
Related topics: Objects, identity and property boundaries · Errors, validation and failure contracts · JSON, text and time contracts · Promises, async and error propagation · Browser events, HTTP and cancellation · Modules and runtime boundaries
A complete correction preserves data meaning, operation identity and what the evidence supports at every boundary.
Reference: JSON.parse · ECMAScript 2026 (17th edition); browser APIs identified separately