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.
Given this pandas snippet, walk through each line as you would to a junior data scientist and explain inputs, outputs, and intent:
df = df.drop_duplicates(subset=['user_id', 'event_date'])
df['event_date'] = pd.to_datetime(df['event_date'])
df = df[df['event_date'] >= '2023-01-01']
Explain potential pitfalls and one unit test to add.
Sample Answer
Direct answer
Each line does one job on the way to a clean, deduplicated, recent-events dataset: drop duplicate user-and-date rows, convert the date column from text to a real datetime type so it can be compared correctly, then filter down to events from 2023 onward. Walking a colleague through it means naming each line's input, output, and assumption, not just what the syntax does.
Structured elaboration
Line by line
df = df.drop_duplicates(subset=['user_id', 'event_date']). Input: the full dataframe (pandas' table-like data structure). Output: a dataframe with one row per unique(user_id, event_date)pair, keeping the first occurrence by default. Intent: treat repeated rows for the same user on the same date as duplicates, for example from a retried API call. Worth naming to a junior colleague: "first occurrence" is arbitrary unless the rows were sorted meaningfully first; if which duplicate survives matters, sort before deduplicating.df['event_date'] = pd.to_datetime(df['event_date']). Input: theevent_datecolumn as text. Output: the same column as an actual datetime type. Intent: text can't be compared or filtered as a date correctly for every format; converting first makes the next line's comparison mean what it looks like it means.df = df[df['event_date'] >= '2023-01-01']. Input: the dataframe with a real datetime column. Output: only rows on or after January 1, 2023. Intent: restrict analysis to a defined recent window; because the column is now a real datetime type, pandas correctly parses the string on the right for the comparison, rather than doing a plain text comparison.
Pitfalls
to_datetimeon an inconsistent or malformed date string raises an error by default rather than silently producing a wrong date, which is safer than it sounds but means the pipeline breaks hard on one bad row unless you explicitly decide how to handle it, for example converting unparseable values into a missing-date marker and then deciding whether to drop or flag them.- Deduplicating before the date conversion means "duplicate" is judged on the raw string form of the date. Two rows representing the same date but formatted differently would NOT be caught as duplicates, since the strings differ. Converting to datetime before deduplicating avoids that trap.
- The final filter creates a view that's a slice of the original; further chained assignment on it can trigger pandas' copy-versus-view warning if not handled with an explicit copy, worth flagging to a junior colleague since it's a very common pandas gotcha.
Worked example
I ran this exact three-line snippet against a small sample dataset to confirm the behavior:
import pandas as pd
df = pd.DataFrame({
'user_id': [101, 101, 102, 103, 103, 103],
'event_date': ['2022-11-05', '2022-11-05', '2023-01-15', '2023-02-01', '2023-02-01', '2023-03-10'],
'event_type': ['click', 'click', 'purchase', 'click', 'click', 'purchase'],
})
print("rows before:", len(df))
df = df.drop_duplicates(subset=['user_id', 'event_date'])
print("rows after dedup:", len(df))
df['event_date'] = pd.to_datetime(df['event_date'])
print("dtype after conversion:", df['event_date'].dtype)
df = df[df['event_date'] >= '2023-01-01']
print("rows after date filter:", len(df))
print(df)
Actual output:
rows before: 6
rows after dedup: 4
dtype after conversion: datetime64[us]
rows after date filter: 3
user_id event_date event_type
2 102 2023-01-15 purchase
3 103 2023-02-01 click
5 103 2023-03-10 purchase
Six rows go to four after deduplication (the two identical 101, 2022-11-05 rows collapse to one), then to three after the date filter drops the pre-2023 row. I separately confirmed the pitfall above is real: feeding pd.to_datetime a list containing 'not_a_date' raises a ValueError naming the exact string that failed to parse, rather than silently producing a wrong date; passing errors='coerce' instead turns that value into a missing-date marker (one row) rather than raising.
One unit test to add
def test_dedup_and_filter_drops_pre_2023_and_duplicates():
df = pd.DataFrame({
'user_id': [1, 1, 2],
'event_date': ['2022-12-31', '2022-12-31', '2023-01-01'],
})
df = df.drop_duplicates(subset=['user_id', 'event_date'])
df['event_date'] = pd.to_datetime(df['event_date'])
result = df[df['event_date'] >= '2023-01-01']
assert len(result) == 1
assert result['user_id'].iloc[0] == 2
This pins both behaviors together (the duplicate collapsed, the pre-2023 row excluded) against a known, hand-checkable expected result, so a future change to either line that breaks either behavior fails loudly instead of silently changing downstream numbers.
Trade-offs and pitfalls
- Silently dropping unparseable dates trades a hard failure for a soft one; only do this if you also log or count how many rows were affected, otherwise you lose visibility into a real data quality problem
- Deduplicating before type conversion is a subtle trap because it looks correct and usually is correct on clean data; it only breaks on inconsistently formatted date strings, exactly the kind of bug that survives review and shows up later as a mysterious discrepancy
- Comparing against a plain string relies on pandas correctly parsing it during the comparison; an explicit timestamp object is marginally more robust and easier for a junior engineer to trust at a glance
Describe concrete tactics for using code reviews as a mentoring tool for junior engineers. Include how you structure comments, what to pair-program versus comment, how to provide targeted learning resources, and how to measure progress over time for the mentee.
Sample Answer
Direct answer
Treat code review as one of several teaching tools, not the only one: use written comments for things that are efficient to explain in text, switch to pairing for anything that needs real back-and-forth, and track whether the same class of comment keeps recurring as the honest signal of whether the mentee is learning, not just complying.
Structured elaboration
Structuring comments
Lead with the "why," not just "change this to that." Separate must-fix from optional or learning-opportunity comments explicitly, so the mentee isn't guessing at severity. Ask a question ("what happens if this list is empty?") instead of dictating the fix when the goal is for them to reason through it themselves; state it directly when time pressure or risk is high enough that the learning moment can wait.
Pair-programming versus commenting
Pair when the concept is genuinely new to them, a new pattern or a new part of the codebase, or when a comment thread has gone back and forth more than twice without converging. Use written comments for anything they've seen before and just need a nudge on, since it's asynchronous and doesn't interrupt their flow.
Targeted learning resources
Link to the specific doc, prior PR (pull request), or style-guide section that addresses the exact gap, not a generic "read the docs on X." Even better: point to a real example already in the codebase that does it well, since it's concrete and has already passed review.
Measuring progress
Track whether the same category of comment (e.g. "add error handling," "extract this function") shows up less often across their later PRs. A good sign is the mentee starting to anticipate the class of feedback you'd give and addressing it before you comment. A bad sign is the same class of feedback repeating PR after PR with no change in how the code arrives.
Worked example
Mentoring a junior engineer over a couple of months: early PRs draw frequent comments on missing error handling and untested edge cases. When they hit an unfamiliar part of the codebase (an async job queue), that becomes a pairing session rather than a comment thread, since it's genuinely new. For a recurring "extract this into a function" pattern, the mentor points them at a specific earlier PR in the codebase that does it well, rather than a generic style guide link. The measurable, honest signal by the third or fourth PR: the error-handling comments mostly stop appearing, and their tests start covering edge cases unprompted, not a fabricated precise percentage.
Trade-offs and pitfalls
Mentoring through review can tip into micromanaging, rewriting their solution in comments instead of letting them arrive at it themselves. It can also become one-sided, where the mentor never learns anything from the mentee's perspective on the code. A common wrong turn is being so gentle that a genuinely blocking issue reads as optional and ships anyway, which helps no one.
Describe a specific code review you participated in recently. Explain the context (feature or bug), the role you took, the most important feedback you gave or received, any disagreement that arose, and the final outcome. Use concrete examples and, if possible, measurable improvements that resulted from the review.
Sample Answer
Direct answer
A strong answer to "describe a specific code review you participated in recently" picks one real, specific PR (pull request), not a generic description of review habits, and walks through concrete details: what the change was, what role was played, the most significant feedback exchanged, any real disagreement, and how it actually resolved, using technical specifics rather than vague praise like "it went well."
Structured elaboration
Context: name the actual feature or bug in one sentence, specific enough to sound real rather than generic.
Role: was the story from the author's or the reviewer's side; be specific about personal actions taken, not "we."
The key feedback: pick the single most substantive comment exchanged, not a list of minor ones, and explain the reasoning behind it, not just what changed as a result.
Disagreement, if any: say so honestly if there was friction, and describe how it resolved. Interviewers notice when every review is described as friction-free, it reads as either limited experience or a polished but hollow answer.
Outcome: describe what changed in qualitative, specific terms, a bug avoided, a design simplified, a teammate learning something, rather than an invented precise metric.
Worked example
"In a recent review I was the reviewer on a PR that added pagination to an internal reporting API. The author's approach paginated by offset, which is simple but degrades as the table grows and can skip or duplicate rows if data changes between page fetches. I left a comment explaining the specific failure case, a row inserted between two fetches shifts the offset and a row gets silently skipped, and suggested cursor-based pagination keyed on a stable, indexed column instead. The author pushed back that offset was simpler and the table was small today. I agreed it was simpler, but pointed out this endpoint already fed a nightly export job, so a silently skipped row would be a quiet correctness bug, not just a performance nit, later on. We agreed to switch specifically for that reason, and I paired with them for a short session to write the first version of the cursor logic. The result was a more correct API, and because we'd talked through the actual reasoning rather than just handing over a fix, the author used the same cursor pattern on a follow-up endpoint without needing another review comment about it."
Trade-offs and pitfalls
The two most common failure modes: choosing a story with no real substance, a trivial style-only review, which makes an interviewer question the candidate's real review experience; and inventing overly polished outcomes with fabricated precise numbers ("reduced errors by 40%"), which reads as rehearsed rather than genuine. A credible answer usually includes at least a small amount of honest friction along the way.
Design a peer-review rubric for ETL or transformation pull requests that balances velocity and reliability. List 6-8 rubric items (for example, tests, readability, performance considerations) and explain how each item should be scored and enforced in the review process.
Sample Answer
A peer-review rubric for extract, transform, load (ETL) or transformation pull requests has to score two different things at once: will this data be reliable, and how much reviewer time will this actually cost. I use seven items on a 0-2 scale (0 = missing, 1 = partial, 2 = complete), split between reliability items that gate the merge and delivery-health items that stay visible but do not block on their own. Whatever can be checked by a machine is enforced in continuous integration (CI); whatever needs judgment goes to a human reviewer.
The seven items
- Tests: 0 = none, 1 = unit tests only, 2 = unit plus an integration run against sample data. CI-enforced; unit tests block the merge, integration tests are required for anything touching a production table.
- Data-quality assertions: 0 = none, 1 = basic null/uniqueness checks, 2 = ranges, referential integrity (a foreign-key-style reference still points to a row that actually exists), and anomaly checks. CI-enforced; a failing assertion must be fixed or explicitly justified with a mitigation plan in the PR description.
- Schema and contract changes: 0 = undocumented or breaking with no migration, 1 = documented with no migration plan, 2 = documented with a migration or backfill plan and backward compatibility. Reviewer-enforced; a breaking change needs a second approver.
- Readability and maintainability: 0 = hard to follow, 1 = readable but undocumented, 2 = clear, modular, with docstrings. Style is linted in CI; architectural clarity is a human judgment call.
- Performance and resource cost: 0 = untested and likely expensive, 1 = a rough estimate, 2 = profiled with cost or latency numbers included. Reviewer-enforced for anything touching a heavy job.
- Observability: 0 = no metrics or logs, 1 = basic logs, 2 = row-count and latency metrics with an updated dashboard or alert. Reviewer-enforced; absence requires a documented follow-up.
- Security and access: 0 = sensitive data handled unsafely, 1 = encryption or access control where needed, 2 = approved masking and least-privilege access reviewed. CI checks for hardcoded secrets; a security owner signs off on anything touching regulated data.
Worked example
Consider a PR that adds a transformation masking a customer email column before it lands in an analytics table, on a dataset of moderate size. Scoring it: tests = 2 (unit tests plus a small integration run), data-quality = 1 (adds a null check but no uniqueness or range assertion), schema = 2 (documented, includes a backfill plan because it changes an existing column), readability = 2, performance = 1 (small change, no profiling numbers attached), observability = 0 (no new metric added for the masking step), security = 2 (masking is exactly what this item exists to reward). Total: 2 + 1 + 2 + 2 + 1 + 0 + 2 = 10 out of a possible 14. Under a threshold like "10 or higher clears with one reviewer," this PR passes with a single approver, but the reviewer should still call out the observability zero explicitly as a fast-follow rather than let a strong total quietly absorb it.
Trade-offs and pitfalls
A pure point-sum threshold can hide exactly that kind of zero on a critical item, so pair the total with a hard floor rule: any single 0 on tests or data-quality forces reviewer sign-off regardless of total. A rubric that is too granular also becomes its own overhead, where reviewers spend more time scoring than reviewing; keep CI-enforceable items automated so a human is only asked to judge genuinely subjective items like readability. Finally, the same rubric should not apply uniformly everywhere: a lighter version fits exploratory or one-off pipelines, and the full rubric belongs on anything that feeds a production or shared table.
As a reviewer, how do you provide constructive feedback that preserves morale and psychological safety? Describe at least six concrete practices (phrasing, prioritization, praise, examples, alternatives, next steps) and explain why each helps the author receive and act on the feedback.
Sample Answer
Direct answer
Constructive review feedback that preserves psychological safety (a shared sense that it's safe to be wrong or imperfect without punishment) comes down to a handful of concrete, repeatable practices: address the code rather than the person, lead with intent, label severity honestly, give real praise, offer a concrete alternative, and leave the door open on next steps.
Structured elaboration
At least six concrete practices, and why each helps the author actually receive and act on the feedback:
- Phrase it about the code, not the person ("this function doesn't handle X" rather than "you forgot X"). It keeps the comment about the artifact, which is easier to hear without feeling personally judged.
- Lead with a question or the underlying intent ("what happens if the list is empty here?" instead of "you missed the empty case"). It invites the author to reason it through rather than just comply, and softens the tone.
- Label severity explicitly (blocking versus a "nit:" versus optional). It removes the guesswork of whether every comment is a must-fix, which reduces the feeling of being buried under criticism.
- Include genuine, specific praise, not filler. It reinforces what to keep doing and signals the review isn't only a list of what's wrong.
- Give a concrete example or alternative, not just "this is unclear." A vague criticism with no path forward reads as judgment; a concrete suggestion reads as help.
- Offer next steps when there's no obvious fix ("happy to pair on this if useful"). It shows the reviewer is invested in the outcome, not just gatekeeping.
- Time the delivery, avoiding a flood of stylistic comments while the core design is still in question, since dozens of comments landing at once reads as harsher than any single one intended.
Worked example
A function is missing a null check. A comment that violates most of these practices: "this is wrong, add a null check." A comment applying several practices at once: "nice catch handling the retry case above! One thing: what happens if user is null here, e.g. a deleted account mid-request? Might be worth an early return. Happy to pair if useful." Same underlying concern, delivered in a way the author can act on without feeling attacked.
Trade-offs and pitfalls
Over-softening a genuinely blocking issue ("just a thought, feel free to ignore") creates ambiguity about severity, and the issue can ship anyway. Psychological safety is not the same as avoiding disagreement; being clear that something is blocking is itself respectful, because it's honest rather than vague.
Unlock Full Question Bank
Get access to all 10 Code Review and Working with Existing Codebases interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.