REFACTORING OBJECTIVE
Modernize this callback-style JavaScript module to current syntax, behavior intact.
Refactoring goal: modernization — move the code to current language features and supported APIs, behavior intact.
Change how the code is built — never what the code does. If a transformation cannot be made safely with the available information, leave that code unchanged and say why.
EXISTING CODE CONTEXT
Code context: none specified — treat unknown consumers as possible and unstated requirements as existing.
Language: JavaScript — keep the refactored code idiomatic and consistent with the file's existing style.
Code to refactor:
```
var utils = require('./utils');
function loadUser(id, callback) {
db.query('SELECT * FROM users WHERE id = ?', [id], function (err, rows) {
if (err) { return callback(err); }
if (!rows || rows.length === 0) { return callback(null, null); }
var user = rows[0];
utils.attachAvatar(user, function (err2, withAvatar) {
if (err2) { return callback(err2); }
callback(null, withAvatar || user);
});
});
}
```
REFACTORING GOAL
Primary goal: Modernization.
Transformation priorities for this goal:
1. Replace deprecated and obsolete APIs with their supported successors.
2. Adopt current language features where they make the code simpler — not everywhere they merely fit.
3. Update outdated patterns — manual loops, callback pyramids, hand-rolled utilities — to their modern equivalents.
4. Modernize incrementally: each replacement should be independently verifiable.
RISK PROFILE
Risk level: Balanced.
- Apply reasonable improvements: moderate restructuring is allowed where the benefit is clear.
- Group related changes, but keep each group reviewable and separately verifiable.
- When a transformation is borderline-safe, apply the conservative version and note the more aggressive option.
SAFETY RULES
These rules apply to every transformation, at every risk level:
1. Identify behavior assumptions before changing code — list every assumption you rely on.
2. Flag uncertain behavior instead of deciding it: if you cannot tell what a branch does or why it exists, ask — do not guess.
3. Do not remove functionality. Code that looks redundant may be load-bearing.
4. Do not invent requirements. Refactor toward the stated goal, not toward an imagined better product.
5. Do not rewrite unrelated code. Touch only what the goal requires.
6. Explain why each change exists — every transformation traces to the stated goal or to a named problem in the code.
7. Preserve public contracts — signatures, return types, error types, serialized shapes — unless explicitly instructed otherwise.
TRANSFORMATION GUIDANCE
Known failure modes of this goal — watch for:
- Modern equivalents are rarely exact equivalents — check null handling, error behavior, lazy vs eager evaluation, and culture/locale defaults before swapping.
- Do not modernize for fashion: a working pattern with no maintenance cost may not be worth the regression risk of replacing it.
Modernization scope for JavaScript:
- Use const and let — eliminate var and the hoisting behavior it drags along.
- Replace callback pyramids with async/await; keep a promise chain only where it is genuinely clearer.
- Use optional chaining (?.) and nullish coalescing (??) instead of && and || guards — careful: || treats 0 and "" as missing, ?? does not.
- Use destructuring, template literals, and spread where they simplify — not as decoration.
- Prefer array methods (map, filter, reduce) over index loops where intent gets clearer.
BEHAVIOR PRESERVATION REQUIREMENTS
- Preserve observable behavior: the same inputs produce the same outputs, including error cases.
- Preserve business rules: every condition, threshold, and special case survives the refactor exactly.
- Preserve outputs: formats, ordering, precision, and encodings stay identical unless the goal explicitly targets them.
- Preserve side effects: writes, emitted events, log lines other systems consume — and their relative order.
- Preserve integrations: API shapes, serialized payloads, database access patterns, and external call sequences.
- These requirements hold unless the instructions explicitly say otherwise — and any knowingly behavior-affecting change must be flagged, never silent.
- If achieving the goal and preserving behavior conflict, behavior wins: stop and explain the conflict instead of compromising it.
VALIDATION STRATEGY
Goal-specific validation: verify each API replacement against its documented behavior differences — old and new must agree on edge cases, not just the happy path.
- Identify the key workflows this code serves and describe how to verify each still works after the refactor.
- List the major edge cases the original code handles and confirm each is still handled — name them individually.
- Recommend regression checks for the riskiest transformations made, riskiest first.
ASSUMPTIONS
- List every assumption made about the code's behavior, inputs, or environment.
- Mark each assumption VERIFIED (with its evidence) or UNVERIFIED (with the question that would resolve it).
- Any transformation that depends on an UNVERIFIED assumption must be flagged as conditional on it.
NON-GOALS
- Do not invent requirements.
- Do not redesign the entire system.
- Do not remove behavior without justification.
- Do not introduce new features.
- Separate assumptions clearly from facts.
- Flag uncertainty instead of resolving it silently.
- Explain the tradeoffs of any change that has them.
OUTPUT REQUIREMENTS
- Return the refactored code, complete — no elided sections, no "rest unchanged" placeholders inside changed units.
- Follow it with a change list: for each change — what changed, why it serves the goal, and its behavior impact (expected: none).
- Flag any change that could plausibly alter behavior, with the reason it might and the check that would confirm it does not.
- Where a worthwhile transformation was skipped for safety, list it under "Suggested but not applied" with what information would unlock it.