Code Review and Working with Existing Codebases Questions
Reviewing others' code and navigating unfamiliar systems: giving and receiving actionable review feedback, spotting correctness and design issues, and reading and understanding large or legacy codebases before changing them. Covers collaborative coding norms, incremental change in shared repositories, and verifying changes against existing behavior. The team-facing side of day-to-day engineering.
You are a senior engineer faced with many teams disagreeing about a shared code style standard for new language adoption. Describe your leadership approach to reach a decision: how to gather input, weigh technical trade-offs, pilot the standard, communicate the change, and measure adoption while minimizing disruption.
Sample Answer
Direct answer
I treat this as a decision-making process problem, not a technical one: gather input broadly but make the actual call with a small accountable group, pilot the standard on a real team before mandating it org-wide, communicate the reasoning and not just the rule, and measure adoption with an honest, mechanically-enforced signal rather than assuming a written standard changes behavior on its own.
Structured elaboration
Gather input
Survey affected teams for their current conventions and actual friction, not just preferences, so the decision is grounded in real problems (for example, "our formatter conflicts with theirs when we share a monorepo") rather than taste. Reduce the debate to 2-3 genuinely competing proposals rather than open-ended bikeshedding; most style disagreements collapse to a small number of real axes, like import ordering or naming convention.
Weigh technical trade-offs
Favor whichever option has the strongest tooling support (an existing auto-formatter, mature editor integration, linter support) over one that's marginally nicer but manual to enforce, since manual enforcement is where standards quietly die. Weigh switching cost too: a team with a large existing codebase in the new language has more sunk cost in its current convention than a team just starting out.
Pilot before mandating
Pick one or two willing teams to adopt the standard for a real sprint or two, not a toy example, and explicitly ask what broke, what felt like friction, and what they'd change before finalizing anything.
Communicate the decision
Publish the reasoning, not just the rule: why this option over the alternatives, and what trade-offs were accepted. Give a clear timeline, a grace period, and name who to raise disagreement with.
Measure adoption and minimize disruption
Track adoption through the automated formatter or linter's own pass/fail rate in CI (continuous integration, the automated build/test pipeline) across repos, an honest signal since it's mechanically enforced rather than self-reported. Roll out with tools that fix code automatically instead of requiring manual compliance, the single biggest lever for minimizing disruption. Grandfather existing code with format-on-touch (only reformat files as they're naturally edited) rather than one disruptive mass reformat that breaks blame history and floods review queues.
Worked example
Three teams adopting a new backend language disagree on import-ordering and error-handling conventions. I run a two-week input-gathering round: two teams prefer style A, matching their existing microservices; one team already has 40,000 lines in style B, a shared library they don't want to rewrite. Rather than forcing a binary choice, I pick style A as the org standard, since it has better tooling support (an existing auto-formatter plugin), but scope the rollout as format-on-touch: new and touched files get reformatted automatically by CI, while untouched legacy files in the third team's library keep style B until they're naturally edited, with a linter configuration that doesn't flag the legacy files. I publish a short doc explaining the tooling reasoning and the grandfather policy, pilot on the first team for two weeks, adjust the auto-formatter's import-grouping rule after they report false-positive linter noise, then roll out to the other two teams. I track adoption as the percentage of touched files in each repo that pass the new formatter in CI, which climbs past 90% within a month without anyone manually reformatting anything.
Trade-offs and pitfalls
- Forcing a big-bang reformat of an entire existing codebase creates a disruptive diff that breaks
git blameand swamps review queues; format-on-touch avoids this at the cost of a longer period of inconsistency - Deciding by committee vote often produces a compromise nobody's tooling actually supports well; weigh tooling maturity heavily, not just preference counts
- Skipping the pilot and mandating org-wide immediately is the most common way this backfires, since the standard hasn't been tested against a real team's actual workflow
- Publishing a rule without publishing the reasoning breeds quiet non-compliance; people follow standards they understand the "why" of far more reliably
Your team's review turnaround time increased from 8 hours to 72 hours and post-release defects doubled in the same quarter. Perform a root-cause analysis: list hypotheses across people, process, and tooling, describe data you would collect to confirm each hypothesis, and propose a prioritized remediation plan with measurable goals and a 90-day roadmap.
Sample Answer
Direct answer
I'd treat the two symptoms, slower review and more defects, as possibly the same root cause or two different ones, and not assume slower review caused more defects without checking. I'd generate hypotheses across people, process, and tooling, collect data to confirm or rule out each before proposing fixes, then sequence remediation by evidence strength and effort over a 90-day plan with explicit target metrics.
Structured elaboration
Hypotheses by category
People: the team grew or turned over, meaning fewer experienced reviewers are available, data to collect: headcount and tenure over the quarter, reviewer roster size over time. Or reviewer burnout, a few people doing most reviews, now overloaded, data to collect: review-count distribution per person, week over week.
Process: pull request (PR, a proposed code change submitted for review) size grew, bigger diffs take longer to review and hide more defects, data to collect: median and 90th-percentile PR size trend over the quarter. Or review requirements changed, for example a new mandatory second-reviewer rule added friction without adding value, data to collect: a policy change log cross-referenced against the timeline. Or priorities shifted, a launch or incident pulled focus away from review discipline, data to collect: a calendar of major initiatives and incidents in the quarter.
Tooling: continuous integration (CI, the automated build/test pipeline) got slower or flakier, so PRs sit longer waiting for a green build before a human even looks, data to collect: CI run duration and retry rate over the quarter. Or a tooling change silently reduced visibility of pending reviews, for example a broken notification bot, data to collect: a tooling change log and notification-delivery logs.
Data to collect, overall
- PR metadata over the quarter: size, time-to-first-review, time-to-merge, reviewer identity, per week
- Defect data: which PRs the post-release defects trace back to, and whether those PRs had unusually fast or slow review, small or large diffs, few or many comments
- Team roster and calendar: headcount changes, major initiatives, incidents, holidays
- Tooling telemetry: CI duration, notification delivery, any process or tooling changes deployed during the quarter
Prioritized remediation plan
- Address whichever hypothesis the data most strongly supports first, not the most dramatic-sounding one
- Prefer high-confidence, low-effort fixes before big structural changes; if the data shows PR size doubled, a size-nudge bot is cheap and directly targets the mechanism, a full process overhaul isn't needed yet
- Set a measurable target for each fix tied back to the original two symptoms (time-to-first-review, defect-escape rate), not just whether the fix shipped
90-day roadmap (adjust once the data confirms the actual cause)
- Days 1-15: instrument and collect the data above; don't change process yet, since changing multiple things at once makes it impossible to tell what worked
- Days 15-30: analyze, identify the one or two hypotheses the data actually supports, present the findings to the team
- Days 30-60: implement the highest-confidence, lowest-effort fix (for example, a PR-size nudge or reviewer load rebalancing) and hold everything else constant to isolate its effect
- Days 60-90: measure against the target (for example, time-to-first-review back under 24 hours, defect-escape rate back to baseline); if the metric hasn't moved, that hypothesis is disconfirmed and the next-highest-confidence one gets tried next
Worked example
Data collection shows: median PR size grew from 180 to 460 lines over the quarter, coinciding with a migration project that added several large, unavoidably broad refactor PRs; reviewer headcount and roster stayed flat; CI duration was stable. Defect data shows the doubled defects cluster specifically in the migration-related large PRs, not evenly across all PRs. This rules out the "reviewer burnout" and "tooling" hypotheses, since neither metric moved, and supports the "PR size" hypothesis instead. The remediation is scoped narrowly: a required PR-size-and-scope check specifically for the migration project (a splitting rule enforced via a bot comment plus a team norm), with a 60-day target of median PR size back under 250 lines and time-to-first-review back under 24 hours. Reviewer roster and CI tooling are left unchanged since the data didn't implicate them, avoiding a broad reorg that wouldn't have addressed the actual cause.
Trade-offs and pitfalls
- The single biggest mistake here is assuming causation, that slower review caused more defects, without checking whether both are downstream of a third cause like PR size; always check whether the defects actually trace back to the slow-reviewed PRs specifically, not just correlate in time
- Changing multiple things at once (new process AND new tooling AND reviewer reassignment) in the same 90 days makes it impossible to attribute the outcome to any one fix
- A remediation plan built before the data is in is a guess dressed up as a plan; resist the pressure to "do something now" before the first 15-day data-collection window closes
- Metrics chosen for the roadmap need a pre-quarter baseline to compare against; without one, "back to normal" isn't measurable
A reviewer used harsh language in comments and the author felt publicly humiliated. As the engineering manager, describe a stepwise plan to de-escalate the situation, repair relationships, update code review guidelines, and prevent similar incidents, including any coaching, documentation changes, and follow-up measurements.
Sample Answer
Direct answer
As the engineering manager, act on two tracks at once: repair the specific relationship and any public harm quickly and privately, and separately fix the guidelines or process so this failure mode doesn't repeat. Treat a single conversation as the start of the fix, not the end of it, and check back later.
Structured elaboration
- Talk to the author first, 1:1. Acknowledge the harm directly without minimizing it, and ask what they need right now, whether the comment should be edited or removed, or whether they'd prefer a different reviewer on this PR (pull request).
- Talk to the reviewer separately, not in a group setting. Be direct that the language was out of line regardless of whether the underlying technical point was right, and get their perspective, rushed, frustrated, unaware of tone, without treating that as an excuse for the impact.
- Repair publicly if the harm was public. If the comment was visible to the team, a short, genuine acknowledgment of what happened is worth more than a vague "let's all be kind" message that erases the specifics; the goal is for the team to see it was actually addressed.
- Close the structural gap. Check whether the team's review guidelines say anything about tone and conduct at all. If they don't, that's a process gap, not solely the reviewer's individual failure, and it's the manager's job to close it, e.g. adding an explicit norm plus example phrasing (this is a nit, this is blocking, avoid absolute language like "this is terrible") to the team's review guide.
- Coach, don't just discipline, for a first occurrence. Work through concrete feedback practices together: phrasing comments about the code rather than the person, leading with a question instead of a command, labeling severity explicitly so a blocking issue doesn't read as optional, and pairing any criticism with a specific alternative rather than a bare complaint. Consider having the reviewer shadow reviews from someone whose feedback style already does this well. If this turns out to be a repeat pattern, it escalates beyond coaching.
- Follow up and actually measure it. Check in with the author privately again a couple of weeks later, not just once, to see if the relationship genuinely repaired rather than just went quiet. Watch later review threads for whether tone actually changed; a guideline that's written down but never checked tends to fade.
Worked example
A reviewer writes something like "this is embarrassing, did you even test this" on a junior engineer's PR, visible to the whole team channel. The manager messages both people privately within the day, has the reviewer edit the comment, and posts a short, honest acknowledgment in the channel rather than a vague platitude. The team's review guide gets an explicit tone section with example blocking-versus-nit phrasing. Two weeks later, the manager checks in privately with the junior engineer, not just assuming things are fine because nobody's raised it again.
Trade-offs and pitfalls
Over-correcting into a heavily policed review culture, where people are afraid to say anything critical, is its own failure mode; the goal is honest, direct feedback delivered respectfully, not conflict avoidance. Treating this as purely an individual coaching issue without fixing the guideline gap means the next person makes the same mistake. Treating it as purely a documentation fix without a real conversation with both people leaves the actual relationship unrepaired.
You are reviewing a data migration that renames a heavily used column and requires backfilling millions of rows. Design a rollback-safe migration strategy that can be reviewed and approved. Cover schema changes, dual-write/read strategies, backfills, verification, monitoring, and how code review should verify each migration step.
Sample Answer
Direct answer
A rollback-safe rename plus backfill never touches the old column or existing readers directly. It adds the new column alongside the old one, writes to both while backfilling the new one in batches, verifies the backfilled data matches, only then switches reads over behind a flag, and keeps the old column around for a retention window so any step can be reversed just by flipping the flag back, not by undoing a destructive change.
Structured elaboration
Each phase below names what code review should specifically confirm before approving it, as a "Review check," plus what to monitor once it ships.
1. Schema change. Add the new column as nullable, with no constraints yet:
ALTER TABLE events ADD COLUMN new_name text NULL;
Review check: confirm this specific statement is additive only and backward-compatible, meaning every existing reader and writer keeps working unmodified the moment this ships, with zero application changes required yet.
2. Dual-write. Deploy an application change, behind a feature flag, that writes both the old and new column on every write to a row. Review check: is the write to both columns transactional or otherwise guaranteed consistent (not "write old, then separately and non-atomically write new"), and is the flag off by default so this ships dormant before anything depends on it?
3. Backfill. A batched, idempotent job fills in the new column for existing rows, only where it's still NULL, ordered by primary key, with a checkpoint so it can resume after an interruption instead of restarting from row one:
UPDATE events
SET new_name = old_name
WHERE id BETWEEN :batch_start AND :batch_end
AND new_name IS NULL;
Review check: is progress persisted somewhere durable (not just in the running process's memory), and does re-running an already-completed batch do nothing (true idempotence), not create incorrect data? Monitoring: track batches completed, rows backfilled, replication lag, and write error rate on the table for the duration of the backfill, and pause automatically if replication lag crosses an agreed threshold.
4. Verification. Before trusting the backfill, sample a random set of rows and confirm new_name matches what old_name implies for each. Review check: is the sample size and comparison method actually specified in the PR, not just asserted as "we verified it"?
5. Read cutover. Only after verification passes, flip the flag so reads prefer the new column, falling back to the old one if the new one is somehow still empty for a given row. Review check: is the fallback logic actually tested, not just written? Monitoring: application error rate and query latency on this table specifically, right after the flag flips, since a regression here is the trigger for the rollback shown in the diagram below.
6. Cleanup. Once reads have run on the new column successfully for a defined retention window, make it NOT NULL, add any index it needs, and only then drop the old column, in a separate, later PR. Review check: is dropping the old column genuinely a separate step from everything above, so it can never accidentally ship bundled with a change that hasn't been verified yet?
flowchart LR
A[Step 1: add new_name column, nullable] --> B[Step 2: dual write old_name plus new_name]
B --> C[Step 3: backfill new_name in batches where NULL]
C --> D{Verification: sampled row values match}
D -- mismatch found --> C
D -- fully verified --> E[Step 4: flip reads to new_name behind a flag]
E --> F{Error rate normal after cutover}
F -- regression --> G[Rollback: flip flag back to old_name, dual write stays intact]
F -- healthy --> H[Step 5: make new_name NOT NULL, add index concurrently]
H --> I[Step 6: drop old_name after a retention window]
Worked example
Renaming user_email to primary_email on a table with 40 million rows, backfilled in batches of 5,000 rows: that's 40,000,000 / 5,000 = 8,000 batches total. With a short pause between batches to keep replication lag bounded, the job runs as a background process over however long it takes to work through all 8,000 batches, checkpointing its position after each one so a restart resumes from the last completed batch instead of row one. Verification samples 10,000 random rows after the backfill reports complete and confirms primary_email equals user_email for every one of them before the flag is ever flipped to prefer reads from the new column.
Trade-offs and pitfalls
Every step here is reversible specifically because the old column and old read path stay intact until the very last, separate cleanup step, which is exactly what makes this slower and more code than a single rename statement; that trade is worth it for a heavily-used column and wrong for a rarely-touched internal table, where a single migration with a maintenance window might be simpler and perfectly safe. The most dangerous version of this pattern to review is one that quietly combines two of these steps, most often shipping the read cutover and the old-column drop in the same change, which collapses the rollback safety the whole design exists to provide.
List five elements you always include in a pull request description to make reviews efficient for team members and new joiners. Explain why each element matters for maintainability, discovery, and onboarding.
Sample Answer
Direct answer
A good pull request (PR, a proposed code change submitted for review) description answers what changed, why, how it was tested, what the reviewer should focus on, and what's deliberately out of scope. Each element saves a reviewer from reconstructing context from the diff alone, and later saves an engineer doing archaeology on why the code looks the way it does.
Structured elaboration
Five elements and why each matters
- What changed (a one-to-two sentence summary): gives the reviewer a frame before reading the diff line by line; without it they infer intent from code, which is slower and more error-prone
- Why (the motivating problem, with a linked ticket or incident if there is one): this is the piece that survives long after the code changes again; an engineer later using
git blameor the commit log to understand "why is this here" finds the why in the description, not the diff - How it was tested: tells the reviewer what confidence already exists (unit tests added, manually verified in staging), so they know what's already covered versus what still needs scrutiny
- What to focus review on: the author usually knows which part of the diff is riskiest or most novel; naming it directs a time-constrained reviewer's attention where it matters most
- What's explicitly out of scope: prevents scope-creep comments ("why didn't you also fix X") and tells a future reader that a related-looking issue was a deliberate non-goal, not an oversight
Why this matters beyond the immediate review
- Maintainability: the description becomes permanent documentation attached to the commit history; months later it's often the only record of intent
- Discovery: someone tracing "why does this function do X" through the commit log finds the description as the answer, faster than re-deriving it from the diff
- Onboarding: a new engineer reading recent PR history to understand a codebase's evolution gets a narrated version of the system's history instead of bare diffs
Worked example
A PR titled "Fix race condition in session refresh" with the description "fixes bug, see diff" forces the reviewer to reverse-engineer what race condition, why this fix addresses it, and whether it was actually reproduced. Contrast: "What: adds a lock around session-token refresh. Why: intermittent authentication failures under concurrent requests, traced to two requests refreshing the same token at once and one overwriting the other's write. Tested: added a test that fires 50 concurrent refresh calls and asserts exactly one network call happens; manually reproduced the original bug on staging before the fix and confirmed it no longer reproduces after. Focus review on: the lock's scope, it needs to cover the full read-modify-write, not just the write. Out of scope: the underlying token storage isn't thread-safe either, that's a separate, larger fix tracked elsewhere." A reviewer can now scrutinize exactly the risky part (the lock's scope) instead of re-deriving the whole problem, and anyone reading this PR later understands both the bug and the deliberate boundary around the fix.
Trade-offs and pitfalls
- A template that's too long or bureaucratic gets filled in with boilerplate nobody reads; keep it to the elements that actually change reviewer behavior
- A "why" section that just restates the ticket title without the actual reasoning doesn't help; the value is in the reasoning, not a ticket number
- Descriptions get stale if the PR's scope changes mid-review and nobody updates them; treat the description as something to revisit before merge, not only before opening the PR
Unlock Full Question Bank
Get access to all Code Review and Working with Existing Codebases interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.