Tech Lead NotesRohit’s interview study guide
Chapter 11

Review Questions

Rapid revision before an interview: short questions with answers you can say out loud in a minute, each linked to the topic that explains it in depth.

4 topics

#Code review and standards questions

Added

This page is for the last few days before an interview, when you don't need to learn a topic any more but need to say it well, quickly. Every question here is one an interviewer for a lead full stack role might ask, or a follow-up they'd ask after your first answer. The first paragraph of each answer is the version you'd say out loud: 30 to 90 seconds, with the decision first and the reason straight after. The walkthrough under it is the reasoning you need when the follow-up is "why?" or "what would you do if…", and the link at the end goes to the topic that teaches it properly.

Think of it as a deck of flashcards with the working shown. Read the question, answer it out loud in your own words, then open the answer and compare: did you say what you'd do, the reason or number behind it, and the trade-off? If one of the three is missing, go back to that card's topic. The few questions where behaviour or arithmetic matters have small runnable models; their output was produced on Node 26 (with the git CLI for the Git questions) and is re-checked every time the site is validated. Answers are written as what a lead would say, not as anyone's real experience: where a question asks for a story, the card gives the structure and points you to your own stories in My notes.

This first group covers code review (what it's for, what to look for, how to write comments, size and speed across time zones, automation, AI-written code) and engineering standards (coding standards, ADRs, the definition of done, API and documentation standards, and enforcing them with tools).

Why do we do code review, if not mainly to find bugs?
Show answer

Bugs are part of it, but most of review's value is knowledge sharing, consistent norms and a safety check on risky changes. "Google's 2018 case study of about nine million reviewed changes describes four expectations: education, maintaining norms, gatekeeping and accident prevention. Microsoft found only about 15% of review comments point at a possible defect, and at least half are about maintainability. So I treat a review as the moment two people come to understand a change instead of one, and the code keeps the team's conventions. It isn't where design gets decided, and it isn't a substitute for tests: if a review finds a bug, I ask for the test that would have caught it."

What’s happening
  1. The sources: Sadowski et al., Modern Code Review: A Case Study at Google (ICSE SEIP 2018) for the four expectations; Czerwonka, Greiler and Tilford, Code Reviews Do Not Find Bugs (ICSE SEIP 2015) for the 15%; Bacchelli and Bird (ICSE 2013) for the finding that developers expect review to find defects, but what it mostly delivers is code improvements, knowledge transfer and team awareness.
  2. Quote the 15% carefully: it's the share of comments about defects, not how many bugs reviews catch. "Most of review's value isn't bug-finding" is accurate; "reviews don't find bugs" isn't.
  3. The mental model to offer: a reviewer is an editor, not a proofreader. The proofreading (formatting, lint, types, tests) belongs to tools; the human asks whether the change should exist, whether it holds up, and whether the next reader will follow it.
  4. Likely follow-up: "so what isn't review for?" Not design (agree the approach in a short design note or ADR before the code), not QA, not the lead's personal approval stamp, and never a power move: the Google paper names tone and power as breakdowns.
  5. On a US–India team, add one sentence: review is one of the main ways a lead teaches, so tone and turnaround matter as much as the content.

Learn more: What code review is for.

Walk me through how you review a pull request.
Show answer

From big to small: context, design, correctness, tests, security, compatibility, and only then readability. "I read the description and ticket first, so I know why it exists and how urgent it is. I look at the shape of the diff; if it's too big to review well I say so first. I read the tests before the code, because they show what the author thinks it does. Then design, correctness on empty, huge, malformed and retried inputs and the error paths, whether the tests would fail if the code were wrong, authorisation and anything reaching SQL, HTML or logs, API and migration compatibility, and last of all names. Before I call something a bug I check it, and I put the repro in the comment. Every comment says whether it blocks, and the summary gives the verdict."

What’s happening
  1. The order is the point. The chapter's mental model is zoom levels on a map: country (should this change exist, is it in the right place), city (does it work for every input, do tests prove it), street (names, readability). A naming comment on a file that should be deleted is wasted.
  2. The order comes from Google's reviewer guide, What to look for in a code review: design first, then functionality, complexity, tests, naming, comments, style, consistency and documentation, plus reading every line and praising the good parts.
  3. "Read the tests first" is a strong concrete detail: missing cases jump out of the tests faster than out of the implementation.
  4. "Run it when reading isn't enough": UI changes, timing and concurrency, and anything you're about to call a bug (gh pr checkout 123, or a one-line repro). Claims in a review should be checkable.
  5. Close with turnaround: on a US–India team, reviewing first thing in a US morning means the feedback is waiting when the Bengaluru author starts their next day.

Learn more: What to look for in a review.

What's wrong with this pull request?

The admin UI needs a paged, searchable device list. The PR adds this function and one test.

JavaScript
export function listDevicesQuery({ siteId, search = "", page = 1, size = 50 }) {
  const offset = page * size;
  let sql = `SELECT id, name, status FROM devices WHERE site_id = ${siteId}`;
  if (search) sql += ` AND name LIKE '%${search}%'`;
  sql += ` ORDER BY name LIMIT ${size} OFFSET ${offset}`;
  return sql;
}
// test: assert.match(listDevicesQuery({ siteId: 7 }), /site_id = 7/);
Show answer

Four real findings: SQL injection through the search text, page 1 skipping the first 50 rows, unstable ordering with no size cap, and a test that would pass with every bug. "The design is fine: right module, right size. But search goes straight into the SQL, so x' OR '1'='1 changes the query; that blocks, and the fix is parameters, not escaping. Page defaults to 1 and the offset is page * size, so page 1 starts at row 50. ORDER BY name isn't unique, so rows can move between pages, and size has no cap. And the only test checks that the string contains site_id = 7, so I'd ask for three tests that would have caught all of it."

What’s happening
  1. Security first: a search box is user input. Pasted inside LIKE '%…%', a quote ends the string and the rest becomes SQL. siteId is interpolated too. The fix returns { text, values } for a parameterised query and escapes %, _ and \ inside the search value so 50% is literal.
  2. Correctness: listDevicesQuery({ siteId: 7 }) produces OFFSET 50. Either pages start at 0, or the offset is (page - 1) * size. Putting that one-line repro in the comment ends the discussion before it starts.
  3. Ordering and limits: two rooms called "Huddle 2" can swap between pages, so order by name, id. Clamp size (say 1–100) so one request can't ask for a whole big customer's table.
  4. Tests: a test is only worth what it would catch if the code were wrong. Name the tests you want: first page starts at offset 0; a quote in the search ends up in values, not text; size is capped.
  5. Say what's right too ("approach looks good"), so the author knows the remaining comments are fixes, not a rejection. Readability had nothing worth a nit, and a good review doesn't invent one.

Learn more: What to look for in a review.

How do you write review comments that people can act on, especially across a time difference?
Show answer

Each comment says what I'm asking, why it matters, and whether it blocks, with evidence attached. "I use labels such as Conventional Comments: issue (blocking), suggestion (non-blocking), nitpick, question, praise. I comment on the code, not the person, attach a repro or a link to our standard, and point at a direction rather than rewriting their code. Written comments lose tone and cross a time zone, so I avoid sarcasm, idioms and one-word comments. If I see the same issue across several PRs, I stop commenting on each one and talk to the person, then fix the root cause with a guideline, a lint rule or a pairing session."

What’s happening
  1. The mental model: each comment is a small ticket with a priority. The author must know what, why and how much in seconds. "Hmm, not sure about this" has none of the three.
  2. Conventional Comments (conventionalcomments.org) is label (decorations): subject, with labels praise, nitpick, suggestion, issue, todo, question, thought, chore, note and decorations (blocking), (non-blocking), (if-minor). Google's guide gets the same effect with Nit:, Optional: and FYI: prefixes.
  3. Which labels block by default is a team choice, so write it down; "issue" should mean the same in Bengaluru and Austin.
  4. The words to delete: "why didn't you…" (asks for a justification and sounds like blame), "just", "simply", "obviously". Thirty nits on a working change means the team is missing a formatter or a lint rule.
  5. Specific praise ("the retry test with the fake clock is really clear") teaches the whole team what good looks like, and Google's guide lists good things as something to look for.

Learn more: Giving and receiving feedback.

A reviewer writes "Why are you catching the error here? This is bad practice." What would you write instead?
Show answer

State the consequence, the label and a direction, and leave the choice of fix to the author. "Something like: issue (blocking): this catch swallows the error, so the API returns 200 with an empty list when the database is down, and nothing is logged. Could we let it propagate to the error middleware, which logs it and returns a 503 problem body, or rethrow after logging? That says what goes wrong, that it must be fixed before merge, and offers two ways forward."

What’s happening
  1. "Why are you…" puts the author on the defensive; they'll explain their reasoning instead of fixing the code.
  2. "Bad practice" is an appeal to authority with no content. The content is the consequence: a 200 with an empty list during an outage, and no log line. That's what makes it blocking.
  3. Offering two directions (propagate, or log and rethrow) respects that the author knows the code; the reviewer points at the problem, the author fixes it, which is how Google's guide divides the work.
  4. The explicit issue (blocking) label matters most for someone junior or working in a second language, who otherwise has to guess severity from tone.
  5. Follow-up: "and when you're the one being reviewed?" Answer every comment (even "Done in 3f2a91c"), disagree with facts and a way forward, move long threads to a call and write the outcome back, and thank people for catches.

Learn more: Giving and receiving feedback.

Block or suggest? A function named getData that deletes stale records; a for loop you'd write as .map(); a block Prettier would format differently.
Show answer

Block only the first; suggest the second at most; and the third should never reach a human. "A function called getData that deletes data is a correctness and readability problem: the next caller will be misled, so I block and suggest deleteStaleRecords. The loop is my preference, so it's a labelled nit the author can decline. And formatting is the formatter's job: if CI runs prettier --check it can't merge unformatted; if it doesn't, the fix is adding that check, not leaving a comment."

What’s happening
  1. The general rule from the chapter: block on correctness, security, design and agreed standards; suggest on preference; automate formatting.
  2. Google's review standard says that on matters of style the style guide is the absolute authority, and a purely style point it doesn't cover is personal preference. A reviewer may suggest, but not block, on preference.
  3. That's why a written standard helps the lead too: it moves the authority from "the lead's taste" to a document anyone can propose changes to.
  4. If you find yourself writing the same preference comment repeatedly, either get the team to adopt it into the standard (and a lint rule), or stop making it.

Learn more: Setting coding standards a team will follow and Giving and receiving feedback.

Someone approves a 600-line pull request 40 seconds after it was opened. What do you do?
Show answer

Treat it as a symptom: fix the causes (oversized PRs, overloaded reviewers, a culture where requesting changes feels rude) rather than adding a second required approver. "A 40-second LGTM on 600 lines is worse than no review, because everyone now believes it was checked. I'd talk to the reviewer privately, not in the PR, and then look at why it happened: was the PR too big to review properly, was review time missing from the plan, is blocking a colleague's PR seen as rude? Smaller PRs, protected review time and labels that make blocking normal fix it. A second approver tends to make each one assume the other read it."

What’s happening
  1. The gotcha in the chapter: rubber-stamp approvals give false confidence and a name to blame.
  2. Size is the usual root cause: the SmartBear/Cisco study (2006) found review effectiveness drops past 200–400 lines and at inspection rates above about 500 lines an hour. A 600-line PR doesn't get six times the review of a 100-line one; it gets one tired skim.
  3. Approving only part of a change is fine if you say so: "Reviewed src/api/; I haven't looked at the migration, @ana can you?" Approving silently tells everyone the whole change was checked.
  4. As a lead, "review the reviews" occasionally rather than every diff, and make it normal for the reviewer to say "this is too big; can we split it?"

Learn more: What code review is for and What to look for in a review.

How big should a pull request be, and why does size matter so much?
Show answer

Small enough to review properly in one sitting: around 100 lines is reasonable, a few hundred is the upper end, 1,000 is too large. "Size changes everything else: small changes are reviewed faster and more thoroughly, introduce fewer bugs, waste less work if the approach is wrong and are easier to roll back. Google's guide says 100 lines is usually reasonable and 1,000 usually too large, and a reviewer can reject a change for size alone. In Google's own data the median change was 24 lines. On a distributed team it matters even more, because the cost of a review is counted in round trips, and each round trip can cost a day."

What’s happening
  1. Google's Small CLs page gives the reasons: reviewed more quickly and thoroughly, less likely to introduce bugs, less wasted work if rejected, easier to merge, design well, unblock and roll back.
  2. Google's 2018 case study: median change 24 lines, over 35% touched one file, about 90% fewer than 10 files, median review time under four hours. Microsoft (Czerwonka et al., 2015) saw the share of useful comments fall as the number of files grew, though the drop only became noticeable for reviews with 20 or more changed files.
  3. SmartBear's 2006 Cisco study (2,500 reviews): under 200 lines and not over 400 at a time, under 300–500 lines an hour, under 60–90 minutes. It's one vendor-run study, so call it guidance, not law.
  4. The mental model is batch size: a big batch waits longer in every queue, gets a shallower inspection, and when it fails you can't tell which part did it. It's the same idea behind DORA's delivery research.
  5. Exceptions to mention: generated files, lockfiles, big deletions and mechanical renames can be large without being hard to review; say so in the description so the reviewer can skim them.

Learn more: Small pull requests and fast reviews.

A feature is 500 lines across schema, API and UI. How do you ship it as small pull requests without waiting on each one?
Show answer

Split it, usually as a stack: refactor first, then schema, then API, then UI, each branch built on the one below and each PR reviewed against its parent. "I keep working on the next layer while the bottom one is in review. When feedback lands on the bottom PR, I fix it there and rebase the stack with git rebase --update-refs, which moves every branch label along. Alternatives are vertical slices behind a feature flag, or splitting by reviewer so the DBA only sees the migration. What doesn't count is one PR with fourteen commits: the reviewer still gets one big diff."

What’s happening
  1. In the chapter's model, the device-groups feature becomes four PRs: a 70-in/40-out refactor, a 45-line migration, 190 lines of API and tests, 190 lines of UI and tests. Together that's 495 insertions, past the 400-line limit for one review; each layer alone is easy.
  2. The refactor goes alone on purpose: Google's guide says refactorings are best in a separate change, because "moves and renames, no behaviour change" is quick to review alone and invisible inside 300 lines of new code.
  3. git rebase --update-refs (Git 2.38, 2022), run from the top branch, replays the upper commits onto the fixed bottom and moves every branch in the stack. Without it you rebase each branch by hand, in order.
  4. Tools: GitHub's built-in stacked pull requests (generally available on all github.com plans since 6 October 2026, per GitHub's changelog; GitHub Enterprise Server support is still to come) with the optional gh stack extension, or Graphite, git-town, Sapling, Jujutsu. Required reviews, checks and CODEOWNERS are evaluated as if each PR targeted the stack's base.
  5. Each piece must make sense by itself; five tiny PRs that only make sense together just move the problem.

Learn more: Small pull requests and fast reviews.

An engineer in Bengaluru says their PRs take three days to merge, and you review them from Austin. What do you change?
Show answer

Remove round trips first, then time the reviews well; asking anyone to work faster doesn't help. "Each review round trip across a 10.5- to 11.5-hour gap costs about a day, so the levers are fewer rounds and better timing. I'd keep PRs small and clear enough to pass in one round, label what blocks, approve with comments when only nits are left, and review first thing in my morning, which is the end of their day, so feedback is waiting when they start. I'd also make sure each area has a second reviewer who can approve, ideally in India. A second required reviewer in the US would make it worse."

What’s happening
  1. The chapter's model follows one PR needing two rounds of changes, with the reviewer in New York in November (10 h 30 min behind Bengaluru; from Austin it's 11 h 30 min in winter, so the exact hours differ but not the pattern): reviewing once a day mid-afternoon took 57 hours; also reviewing first thing in the US morning (08:00 New York, 18:30 Bengaluru) took 50; approving round 2 with comments took 42.
  2. What didn't change: the fixes always landed at 11:00 Bengaluru time, because that's when the author's day allowed it. The time goes on waiting in queues, not on work.
  3. Approve with comments ("LGTM with comments") is in Google's guide, which says it's especially worth considering when author and reviewer are in different time zones. It removes a whole round trip.
  4. Google's turnaround guidance: respond within one business day at most, and if the author has finished for the day, finish the review before they start the next one. Write a team target and track the median time to first review, not a per-person score.
  5. When a thread has gone back and forth twice, take it to a short call in the overlap and write the outcome in the PR.

Learn more: Small pull requests and fast reviews and Working across US and India time zones.

What do you automate in code review, and what do you leave to people?
Show answer

Anything deterministic goes to tools that run before a human looks; people get design, correctness, test quality, security judgement and readability. "Prettier for formatting, ESLint and typescript-eslint for code-quality rules, tsc --noEmit for types, the test suite, dependency and secret scanning, and a size warning on big PRs, all as required status checks in GitHub Actions on every push and in the merge queue. CODEOWNERS routes risky paths, like migrations, auth and the CI config, to the right people. Pre-commit hooks give fast local feedback, but they can be skipped, so CI is the gate. When the same comment comes up in reviews three times, I look for a rule that makes it automatic."

What’s happening
  1. The mental model: the robot goes first. Editor on save, pre-commit hook, CI on every push, CODEOWNERS requests reviewers, branch protection refuses the merge until it's all green and approved. The human starts from "formatted, typed, linted, tested".
  2. Google works the same way: its 2018 case study describes results from its Tricorder platform's static analysers (110 at the time) shown inside the review tool.
  3. A required workflow must listen to merge_group as well as pull_request if you use a merge queue; GitHub's docs say otherwise the check is never reported for the queued commit and the merge fails.
  4. A check only becomes a gate when it's marked as a required status check in branch protection or a ruleset. Until then it's a coloured dot people learn to ignore.
  5. Good follow-up example: "the endpoint checks the right permission" can't be automated, but once a human has found it, a test (viewer gets 403) turns the finding into a machine check.

Learn more: Automating the boring parts and Enforcing standards with tooling, not meetings.

We have a pre-commit hook that runs ESLint and Prettier. Isn't that enough?
Show answer

No. A hook is a convenience for the author; the guarantee is the same check as a required CI status check. "git commit --no-verify skips a hook entirely, and a hook only runs on machines where someone installed it. So I keep the hook fast, staged files only with lint-staged, and fixing what it can, so nobody wants to skip it, and I run the same commands in CI in check mode, required to merge. The hook catches mistakes seconds after they're made; CI makes sure nothing reaches main without passing."

What’s happening
  1. The mental model from the standards chapter: layers of nets. Editor (seconds, optional), pre-commit (seconds, skippable with --no-verify), CI (minutes, can't be skipped), branch rules (make CI binding), code review (the most expensive net, for judgement only).
  2. In the code review chapter's plain-Git hook model, a commit with test.only(…) is refused, the fixed commit goes through, and then git commit --no-verify commits a console.log anyway.
  3. Hook and CI run the same tools in different modes: prettier --write and eslint --fix in the hook, prettier --check and eslint --max-warnings=0 in CI, so CI never changes code; it only says yes or no.
  4. Keep hooks to seconds. A hook that runs the whole test suite gets skipped by the second day; a 12-minute end-to-end suite belongs in CI.
  5. Secrets are the one check worth doing in every layer: hook, CI and the Git host's own secret scanning, and a pushed secret must be rotated, because it stays in Git history.

Learn more: Enforcing standards with tooling, not meetings and Automating the boring parts.

Your CODEOWNERS file gives /src/api/ to the API team, and later has *.md for the docs team. Who reviews src/api/README.md?
Show answer

The docs team only, because in CODEOWNERS the last matching pattern wins. "Order in CODEOWNERS is meaning: the catch-all * goes first and the specific rules after it, because a later match overrides an earlier one. GitHub reads the file from the PR's base branch, and one approval from any listed owner of a file satisfies the code-owner requirement for it. I'd also make the tech leads own .github/ itself, so nobody can remove themselves from review by editing the file in their own PR."

rq-codeowners.mjs
// Last match wins: a simplified matcher for the pattern shapes used here (GitHub follows .gitignore rules).
const rules = [
  ["*", "@acme/web-leads"],
  ["/src/api/", "@acme/api-team"],
  ["*.md", "@acme/docs"],
  ["/db/migrations/", "@acme/api-team @acme/dba"],
];
const matches = (pattern, file) =>
  pattern === "*" ? true
  : pattern.startsWith("*.") ? file.endsWith(pattern.slice(1))
  : pattern.endsWith("/") ? `/${file}`.startsWith(pattern)
  : `/${file}` === pattern;

for (const file of ["src/api/devices.js", "src/api/README.md", "db/migrations/0042.sql", "ui/App.jsx"]) {
  const hits = rules.filter(([pattern]) => matches(pattern, file));
  console.log(`${file.padEnd(24)} matched ${hits.map(([p]) => p).join(", ").padEnd(20)} → ${hits.at(-1)[1]}`);
}
Result
src/api/devices.js       matched *, /src/api/         → @acme/api-team
src/api/README.md        matched *, /src/api/, *.md   → @acme/docs
db/migrations/0042.sql   matched *, /db/migrations/   → @acme/api-team @acme/dba
ui/App.jsx               matched *                    → @acme/web-leads
What’s happening
  1. Each file is checked against every rule in file order, and the last hit decides the owners (hits.at(-1)). That's GitHub's documented precedence; the matcher itself is simplified to the four pattern shapes used here.
  2. src/api/README.md matches three rules. You might expect the API team, but *.md comes later, so only the docs team is requested and the API team isn't asked at all.
  3. The migration has two owners on one line, so both teams are requested, and an approval from either satisfies the requirement for that file.
  4. ui/App.jsx only matches *, which is why the catch-all must be the first line: put it last and it would override every specific rule.
  5. Use code owners sparingly, for risky areas (migrations, auth, billing, CI config, device interfaces). Owners on every directory turns into "every PR needs four teams", the opposite of fast review.

Learn more: Automating the boring parts and Enforcing standards with tooling, not meetings.

How do you handle AI-generated code in your team's reviews?
Show answer

The person who opens the PR owns every line, whoever typed it; the review standard doesn't change, but a few things get more attention. "They must have run it and be able to explain it, and the description says what was generated and what they verified. I look harder at whether the tests check the requirement or just the generated behaviour, whether APIs and packages really exist, input reaching SQL, HTML or logs, secrets in logs, and retries and error handling, where generated code is often plausible but wrong. Size limits still apply, because AI makes big diffs effortless, and DORA's 2024 report itself suspects growing changelists behind the estimated drop in delivery stability it associates with AI adoption. And Copilot code review is a useful first pass, not an approval."

What’s happening
  1. The mental model: review AI code like a PR from a confident new contractor: fluent, fast, knows every library's surface, doesn't know your system, your users or your incident history, and will write a test that confirms its own mistake.
  2. The evidence the chapter cites: DORA's 2024 report estimated that every 25% increase in AI adoption comes with "an estimated 1.5%" fall in delivery throughput and "an estimated 7.2%" fall in stability (p. 40; model estimates from survey data, not measured drops), and DORA's own hypothesis is that changelists are growing in size; Veracode's 2025 report found security flaws in about 45% of generated code; Spracklen et al. (USENIX Security 2025) found hallucinated package names at least 5.2% of the time for commercial models and 21.7% for open-source ones.
  3. The chapter's worked example: a generated retry helper with passing tests that retries a non-idempotent reboot (so a lost response reboots the room twice), sends a request that got a 400 four times, logs a body containing an adminPin, and has a test asserting callCount() === 4, which encodes the bug as the requirement.
  4. The fix needs the server's cooperation: retry only 429, 502, 503, 504, and make a POST safe to retry with an Idempotency-Key the device recognises.
  5. Follow-up "what about AI review?": GitHub's docs say Copilot code review isn't guaranteed to spot all problems and its reviews don't count toward required approvals by default. Keep the standards in .github/copilot-instructions.md, .github/instructions/ and AGENTS.md so both reviewers check the same things.

Learn more: Reviewing AI-generated code.

Copilot cloud agent opened a PR you asked for, and you approved it. Can it merge?
Show answer

Not on your approval alone (on a branch that requires an approval): another reviewer has to approve, and by default its workflows don't even run until someone approves them. "GitHub's docs say that for a PR created by Copilot cloud agent, an approval from the person who asked for it doesn't count toward required approvals. So someone who isn't the requester reviews it. By default GitHub Actions don't run on Copilot's pushes until someone clicks Approve and run workflows. A 2026 setting can skip that step; for a team like ours I'd leave the human step on."

What’s happening
  1. The rule exists for the same reason as "the author can't approve their own PR": the requester is effectively the author.
  2. The chapter checked GitHub Docs on reviewing Copilot's pull requests on 27 October 2026. "Copilot cloud agent" is GitHub's name since its 1 April 2026 changelog (formerly "Copilot coding agent"). A 13 March 2026 changelog added the setting that lets workflows run without the approval click.
  3. Copilot code review (the AI reviewer) is separate: by default its reviews don't count toward required approvals; an organisation can enable Copilot approvals (public preview), and such an approval is dismissed when new commits are pushed.
  4. The gotcha to mention: Copilot code review reads its custom instructions from the PR's head branch, so a PR can change the instructions its own AI review follows. Put the instruction files under CODEOWNERS with the tech leads.

Learn more: Reviewing AI-generated code.

How would you introduce coding standards to a team that has none?
Show answer

Start with tools, not a document: adopt a formatter and a mainstream lint preset, enforce them everywhere, then write a short page for the judgement calls. "I'd agree Prettier and ESLint with typescript-eslint's recommended configs, wire them into the editor, a pre-commit hook and a required CI check, and reformat the codebase in one dedicated commit so the style argument ends on day one. Then a one-page standard for what tools can't check, such as error handling, input validation, API conventions and PR expectations, each rule with its reason. Anyone can change a standard by a PR that also updates the tooling. For old code, new and touched code first, with a ratchet. And I'd involve the whole team, offshore engineers included, in choosing the deviations."

What’s happening
  1. The mental model: standards are the team's paved road. You can leave it with a reason, but on the road everything is easy: configured linter, templates, examples, reviewers wave you through. A wiki page nobody reads is a sign beside a dirt track.
  2. Adopt, don't invent: start from a published guide (Google's style guides, the ESLint and typescript-eslint recommended configs) and write down only your deviations.
  3. Give the reason next to each rule ("=== because == coerces types"); reasons get followed, bare rules get argued with.
  4. The one-commit reformat goes in .git-blame-ignore-revs (with git config blame.ignoreRevsFile .git-blame-ignore-revs; GitHub's blame view reads that file name too), so git blame still shows who wrote each line.
  5. The two failures to name: the 40-page standard one person wrote and nobody opened again, and the bikeshed, weeks of debate over things a formatter preset settles in an afternoon.

Learn more: Setting coding standards a team will follow.

You turn on no-floating-promises in a five-year-old codebase and get hundreds of errors. What now?
Show answer

Ratchet it: record today's violations as a baseline, fail only on new ones, and let the count only go down. "Fixing them all first blocks the rule forever, and turning it on as a warning means it's ignored. ESLint 10 has this built in as bulk suppressions: eslint --suppress-all writes the current errors to eslint-suppressions.json, which is committed as a visible list of debt. New violations fail CI; when old ones are fixed, --prune-suppressions removes them from the allowance, so the ratchet only tightens."

What’s happening
  1. Suppressions are counts per file and per rule, and cover errors only, not warnings.
  2. If someone adds a second == to a file allowed one, ESLint reports both and fails: it can't tell which is new, so the developer fixes theirs (and maybe the old one).
  3. It's enforced in the other direction too: with leftover suppressions that no longer occur, ESLint 10.13.0 exits with code 2 until you prune (checked in the standards chapter), so a fixed violation can't quietly stay in the allowance.
  4. The same idea works for anything countable: TypeScript strict errors during a migration, accessibility issues, bundle size, or coverage (where the ratchet goes up instead of down).
  5. Watch the eslint-disable count too: a rising count means a rule is wrong or a habit is.

Learn more: Enforcing standards with tooling, not meetings.

What is an ADR, and when do you write one?
Show answer

A short, numbered Markdown file in the repository that records one significant decision: its context, the decision, its status and its consequences, good and bad. "That's Michael Nygard's 2011 format; MADR adds the options considered and decision drivers. I write one when a decision is hard to reverse or will puzzle people later: a new database or framework, SSE versus WebSockets, an API versioning policy, a cross-team convention. It's proposed as a pull request, so the PR is the discussion and merging means accepted. On a distributed team it means the reasoning is written down for everyone, not living in one meeting in one time zone."

What’s happening
  1. Nygard's five parts (Documenting Architecture Decisions, 15 November 2011): Title (a short noun phrase with a number), Context (forces, as facts), Decision ("We will…"), Status (proposed, accepted, deprecated, superseded), Consequences (all of them, not just the benefits).
  2. The mental model: ADRs are the ship's log of the architecture. They don't describe the ship; they record what was decided, when and why, given what was known at the time.
  3. A good test for "does this need one?": would someone reasonably undo it in a year if they didn't know why we did it? A date-formatting library doesn't need a record.
  4. Write it as the proposal, before the work. ADRs written weeks later become justification: straw-man alternatives and missing costs.
  5. Since ADRs have a fixed shape, a small CI script can check numbering, required sections, valid statuses and that supersede links point at accepted records.

Learn more: Architecture decision records.

A colleague opens a PR that rewrites the Decision section of ADR 7, accepted a year ago. What should happen instead?
Show answer

Write a new ADR with the next free number that supersedes ADR 7, and change only ADR 7's status line. "An accepted ADR is a historical record: it explains why that decision was right a year ago. Rewriting it erases the reasoning, and every old PR or incident that cites 'ADR 7' would now point at a different decision. So ADR 12 gets its own context, what changed, its decision and its consequences, with 'Supersedes ADR 7', and ADR 7 becomes 'Superseded by ADR 12', both in the same PR."

What’s happening
  1. The status line is the only edit an accepted ADR ever gets. Reading ADR 7 should still tell you why direct browser-to-device-API calls made sense then.
  2. The new record's context is the interesting part: new security requirements, too many round trips, a second client. Those facts are what make a backend-for-frontend the right call now.
  3. Its consequences include the costs: a new service to run, extra latency, a new place where calls are aggregated.
  4. Both changes in one PR keep the cross-references consistent the moment it merges; the chapter's ADR check would flag a supersede link that points at a record that isn't accepted.

Learn more: Architecture decision records.

What's in your definition of done, and how do you enforce it?
Show answer

Tests, review, docs and a demo against the acceptance criteria, with every automatable line enforced as a required check. "For a web UI and API team: reviewed by a code owner; format, lint and types clean; unit and integration tests passing, with coverage on the changed lines; no new high-severity vulnerabilities; API changes in the OpenAPI document; docs and runbooks updated; accessibility checked; and demoed to the product owner on staging. Each automated line is a required status check, so a PR can't merge while it's red, and the human items are checkboxes in the PR template. On a distributed team, a shared written DoD is what keeps quality the same in both locations."

What’s happening
  1. The Scrum Guide (2020) makes the DoD a formal description of the Increment's state when it meets the product's quality measures. If the organisation has one, it's the minimum; teams on one product share it. Work that doesn't meet it isn't presented at the Sprint Review; it goes back to the Product Backlog.
  2. The mental model: acceptance criteria say what one story must do; the DoD says how well every story must be finished. A quality gate is the part of the DoD a machine checks.
  3. Each line needs a checker (CI, the PR template, or the product owner). A DoD item with no checker is a wish.
  4. Coverage on changed lines, not the whole codebase: an old codebase at 45% shouldn't block every PR, but new code should be tested. The threshold (80% in the chapter) is a team choice; the trend matters more.
  5. The trap: aspirations like "performance tested" with nothing behind them get ticked by habit. Automate the item, budget time for it, or drop it openly.

Learn more: Definition of done and quality gates.

A story's acceptance criteria were demoed, but an admin bypassed the coverage gate (52% against 80%) to hit the sprint deadline. Is it done?
Show answer

No. It passes the acceptance criteria but not the definition of done, so it isn't done, whatever the demo showed. "Acceptance criteria and the DoD are two separate tests. Merging isn't the same as done: the missing tests become a small follow-up before we call it complete, and under the Scrum Guide it can't be presented as done at the review. Then I'd look at the bypass, not the person: who can bypass, when it's acceptable (a recorded production incident, say), and why a deadline was the reason. Repeated bypasses mean the gate or the planning needs to change."

What’s happening
  1. The DoD applies to every item; the criteria to this one. Passing one never excuses the other.
  2. The bypass got the code onto main, which is exactly why "merged" can't be the definition of done.
  3. Rulesets let you restrict bypass to named people, so a bypass is a deliberate, visible act rather than a habit.
  4. The planning question is the real one: if the team can't meet the DoD inside a sprint, the plan is over-committed, and the lead's job is to fix the plan rather than lower the bar quietly.

Learn more: Definition of done and quality gates and Enforcing standards with tooling, not meetings.

Definition of done, acceptance criteria, definition of ready: what's the difference?
Show answer

Acceptance criteria are per item, the definition of done is for every item, and the definition of ready is a team practice for items entering a sprint. "Acceptance criteria say what this story must do, like 'offline devices show in grey with when they were last seen', written by the product owner with the team. The definition of done says how well everything is finished: tests, review, docs, staging. A definition of ready says an item has criteria, designs and known dependencies before we plan it. It isn't in the Scrum Guide, and I'd use it lightly, because a strict one becomes a wall between product and engineering."

What’s happening
  1. Acceptance criteria: one backlog item; product owner with the team.
  2. Definition of done: every item; the whole team, or the organisation as a minimum.
  3. Definition of ready: items entering a sprint; the team. A common practice, not part of Scrum.
  4. Quality gate: every pull request or build, encoded in CI ("changed-line coverage at least 80%, zero lint errors").
  5. The failure mode for each: vague criteria ("works well"), an aspirational DoD nobody checks, and a DoR used to say "not ready, not our problem".

Learn more: Definition of done and quality gates.

How do you keep API standards consistent across a team?
Show answer

Adopt a published guide, add a short house style, make the OpenAPI document the source of truth, and lint it in CI. "I'd base it on Google's AIPs, Microsoft's or Zalando's guidelines, and write down only our rules on naming, versioning, pagination, idempotency keys and one error format, RFC 9457 problem details. We work API first: the spec changes in the same PR as the code, so the contract is reviewed as a contract. CI lints it with Redocly or Spectral using our rules, checks for breaking changes against main and generates the docs, so they can't drift. Reviews are then about design and compatibility, not naming."

What’s happening
  1. The mental model: the style guide is a style guide for the contract, and the OpenAPI document is the contract itself, machine-readable, so it can be linted, diffed in review, turned into docs and SDKs, and tested against.
  2. RFC 9457 (2023): application/problem+json with type, title, status, detail and instance. One error shape lets the UI show detail for every endpoint.
  3. The chapter's example: POST /v1/devices/{id}/reboot requires an Idempotency-Key (a reboot isn't naturally idempotent), returns 202 Accepted with a Location and a job, and has a unique camelCase operationId, because generators turn it into the SDK method name.
  4. Versions as the chapter states them: OpenAPI 3.2.0 is current (September 2025); 3.1 is still the most widely supported by tools.
  5. House rules tools don't ship (no verbs in paths, collection GETs take limit and cursor) can be custom Redocly or Spectral rules, or a short script over the parsed spec.

Learn more: API and documentation standards.

A PR adds an optional response field, adds cancelled to a status enum, and renames jobId to id. Which are breaking?
Show answer

The rename definitely, the enum value very likely, and the optional field normally not. "Renaming a response field breaks every client that reads jobId, so it needs a new version or a transition where both are returned and the old one is deprecated. A new enum value looks additive, but clients switch over the documented values; a generated TypeScript type won't include it, and a loop waiting for 'succeeded or failed' may wait forever. Treat it as breaking unless the API said from the start that clients must tolerate unknown values. A new optional response field is normally safe, because well-behaved clients ignore what they don't know."

What’s happening
  1. Rename: old clients get undefined. Return both fields for a while with jobId marked deprecated: true, or bump the version.
  2. Enum: the subtle one, and the one interviewers like. Document "clients must handle unknown values" up front if you expect to add states.
  3. Optional response field: safe. The mirror image isn't: a new required request field breaks old clients, because they don't send it.
  4. This is why the standard says the spec changes in the same PR: a breaking-change check (oasdiff's breaking command compares two specs) can flag the rename and the enum automatically, before a human reviews it.
  5. On a device platform, also ask who the clients are: firmware in the field can't be updated as fast as the web UI, so compatibility windows are longer.

Learn more: API and documentation standards and What to look for in a review.

What documentation should every service have, and how do you stop it going stale?
Show answer

A README that's a hub, a runbook entry per alert, generated reference docs and dated ADRs, all in the repository and reviewed in the same PR as the change. "The README says in one sentence what the service is and links the ADR that explains why it exists, then copy-pasteable run and test commands, a config table, and owners and links. Each alert has a runbook entry for someone who's just been woken up: what it means, impact, what to check, how to fix, when to escalate. The API reference is generated from OpenAPI. Docs live next to the code, so a reviewer can ask 'behaviour changed but no doc did: why?'"

What’s happening
  1. Diátaxis (Daniele Procida) sorts docs into tutorials, how-to guides, reference and explanation. A runbook entry is a how-to; the generated API docs are reference; ADRs and design docs are explanation. Most bad docs mix kinds.
  2. The best test of a README is a new engineer, ideally in the other office, getting the service running without asking anyone.
  3. The runbook heading is the alert's exact name, so searching finds it; "Means" and "Impact" come first so the reader can judge urgency; every alert without an entry deserves a ticket.
  4. A wiki away from the code is updated by nobody, because nobody's PR touches it. Generate what you can and give explanation documents dates and statuses.

Learn more: API and documentation standards.

#Git and CI/CD questions

Added

This group covers how code gets from a branch to production: branching strategies, merge, squash and rebase, commit messages, releases and hotfixes, undoing mistakes, git bisect, and then the pipeline itself: what CI and CD mean, a GitHub Actions workflow, quality gates that are really enforced, environments and deployment strategies, pipeline speed, and the DORA metrics.

The mental model for the whole group is how long work stays apart, and how quickly you find out it's wrong. Almost every good answer here shortens one of the two: smaller branches merged sooner, checks that run earlier, artefacts promoted rather than rebuilt, rollbacks that are one command. Interviewers for a lead role probe the why behind each command, so for every card be ready with what the command does to history, and when it's dangerous.

Trunk-based development or GitFlow: which would you choose?
Show answer

Trunk-based for a web UI and API that we deploy continuously; and for device software with several supported lines, still one trunk plus a release branch per line. "Short-lived branches merged to main through pull requests at least daily, unfinished work behind feature flags, and main always releasable because CI runs on every PR. DORA lists trunk-based development as a capability linked to better delivery performance, and it removes the merge week before a release. GitFlow's machinery, develop plus release and hotfix branches with double merges, is for parallel versions, and a cloud service has exactly one version in production. The deciding question is how many versions we support at once and how they ship, not how big the team is."

What’s happening
  1. The mental model: main is a shared document and each branch a private copy. Paste your edits back by lunch and little has changed underneath you; keep the copy three weeks and you reconcile everyone's edits at once.
  2. GitFlow is Vincent Driessen's 2010 model. His "Note of reflection" (5 March 2020) says continuously delivered software such as web apps should use something simpler like GitHub flow, and git-flow still suits explicitly versioned software or several versions in the wild.
  3. DORA's trunk-based capability page names three practices: three or fewer active branches, merging to trunk at least daily, and no code freezes or integration phases.
  4. For firmware with a 4.x maintenance line alongside 5.x: fix on main first with a test, cherry-pick with -x to every line that has the bug, tag each build. Driessen's model has one production branch, so two supported lines need extra branches anyway.
  5. The follow-up "how do you merge unfinished work?": feature flags, each with an owner and a removal ticket, because flags are debt with a date on them.

Learn more: Trunk-based development vs GitFlow and Release branches, tags and hotfixes.

A feature takes three weeks. A developer wants to keep it on a branch and merge it when it's finished. What do you say?
Show answer

Merge it in small, flagged slices instead. "A three-week branch doesn't just risk conflicts: it hides integration problems until the end, two changes that each pass their own tests but break together, it arrives as one enormous PR nobody can review properly, and it blocks refactoring on main because 'it'll conflict with Sam's branch'. So we merge a slice every day or two behind a feature flag that's off in production, CI tests both paths, and when it's ready we turn it on for internal users, then a percentage, then everyone. Then we delete the flag and the old path."

What’s happening
  1. The CI chapter's model makes the cost concrete: six developers making the same edits, merging every day, saw 1.4 clashing files per merge, found the same day they were written; merging every 20 days saw 16.5 per merge (up to 25), found 13.5 days later.
  2. The total overlap barely changes; batch size and delay do. That's the whole argument for CI: you can't avoid integrating with colleagues, only choose to do it while it's small and fresh.
  3. Other ways to hide work in progress: a route nobody links to yet, a new API version nobody calls yet, branch by abstraction for big refactors, expand and contract for schema changes.
  4. Turning the flag off is the fastest rollback there is, because it needs no deploy.

Learn more: Trunk-based development vs GitFlow and What CI and CD actually mean.

Merge commit, squash or rebase: which do you use, and what does each do to history?
Show answer

All three give the same files; they differ in history. On a trunk-based team with small PRs I default to squash. "A merge commit keeps the branch's real commits with their hashes and shows where it joined, at the cost of a busier graph. Squash turns the PR into one new commit on main: history reads one line per PR, reverting a PR is one git revert, and people can commit freely on their branch, but the individual commits are gone. Rebase keeps each commit and a straight line, but rewrites every hash, so it needs a clean, deliberate commit series and must never be done to commits others have built on. With squash, I lint the PR title in CI, because it becomes the commit message."

What’s happening
  1. The chapter's mental models: merge is stapling the branch on with a cover note; squash is retyping the branch as one summary paragraph; rebase is replaying your edits onto today's version, which makes new commits.
  2. Rebase changes hashes because a commit's hash covers its parent and its committer date; change the first commit's parent and every later hash changes too. GitHub's "Rebase and merge" always creates new SHAs, even when a local rebase wouldn't.
  3. Undo: git revert -m 1 <merge> for a merge commit, a plain git revert for a squash commit, each commit (or a range) for a rebase.
  4. A ruleset can require linear history, which blocks merge commits, so PRs must be squashed or rebased.
  5. Keep merge commits where the join itself is information, such as merging a long-lived release branch.

Learn more: Merge, squash and rebase.

A PR was squash-merged, but git branch -d feature refuses: "not fully merged". Is Git wrong?
Show answer

No. Git checks whether the branch's commits are reachable from main, and after a squash they aren't: one new commit with the combined change landed instead. "The files are identical, but Git's 'merged' means 'these commits are ancestors of here'. So after a squash or a rebase-and-merge, git branch --merged doesn't list the branch and -d refuses. Once I've checked the PR really merged, I use -D, and let GitHub delete the remote head branch automatically when the PR merges. It's also why a stacked branch built on top of a squashed one needs git rebase --onto."

rq-squash-merged.mjs
// After a squash merge, is the branch "merged"? A throwaway repository with fixed names and dates.
import { execFileSync } from "node:child_process";
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

const dir = mkdtempSync(join(tmpdir(), "rq-squash-"));
let minute = 0;
const git = (...args) => {
  const when = `2026-10-20T09:${String(minute++).padStart(2, "0")}:00Z`;
  return execFileSync("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],
    env: { ...process.env, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_NOSYSTEM: "1", LC_ALL: "C",
      GIT_AUTHOR_NAME: "Meera", GIT_AUTHOR_EMAIL: "meera@example.com", GIT_AUTHOR_DATE: when,
      GIT_COMMITTER_NAME: "Meera", GIT_COMMITTER_EMAIL: "meera@example.com", GIT_COMMITTER_DATE: when } }).trim();
};
const commit = (file, text, msg) => { writeFileSync(join(dir, file), text); git("add", file); git("commit", "-q", "-m", msg); };

git("init", "-q", "-b", "main");
commit("app.js", "export const app = 1;\n", "Initial commit");
git("switch", "-q", "-c", "feature");
commit("login.js", "export const login = () => {};\n", "Add login form");
commit("login.js", "export const login = (u) => u.trim();\n", "Trim the user name");
git("switch", "-q", "main");
git("merge", "-q", "--squash", "feature");
git("commit", "-q", "-m", "Add login form (#12)");

console.log(git("log", "--oneline", "--graph", "--all"));
console.log("\nsame files on main and feature?", git("diff", "main", "feature") === "" ? "yes" : "no");
console.log("git branch --merged main:", git("branch", "--merged", "main").replace(/\s+/g, " "));
try { git("branch", "-d", "feature"); } catch (e) { console.log("git branch -d feature:", e.stderr.split("\n")[0]); }
console.log("git branch -D feature:", git("branch", "-D", "feature"));
rmSync(dir, { recursive: true, force: true });
Result
* f7ebb91 Add login form (#12)
| * c49455d Trim the user name
| * ed29775 Add login form
|/  
* 7b4949c Initial commit

same files on main and feature? yes
git branch --merged main: * main
git branch -d feature: error: the branch 'feature' is not fully merged
git branch -D feature: Deleted branch feature (was c49455d).
What’s happening
  1. The model makes a real repository with your global and system Git config ignored (GIT_CONFIG_GLOBAL=/dev/null, GIT_CONFIG_NOSYSTEM=1, LC_ALL=C) and a fake clock that advances a minute per command, so the hashes (f7ebb91, c49455d…) are identical on every run.
  2. feature gets two commits; git merge --squash feature then stages their combined change on main without recording a merge, and the commit that follows, f7ebb91 Add login form (#12), has one parent. The graph shows the two original commits still hanging off 7b4949c, on feature only.
  3. git diff main feature prints nothing, so the files are the same, yet git branch --merged main lists only main: neither ed29775 nor c49455d is an ancestor of main.
  4. So git branch -d refuses with the branch 'feature' is not fully merged, which is Git protecting you from losing commits it can't find anywhere else. -D deletes it anyway and prints the tip it had (c49455d), which you could still recover from the reflog.
  5. If you changed squash to git merge --no-ff feature, both original commits would become ancestors of main and -d would succeed. Of GitHub's three merge buttons, it's the only one that keeps "is it merged?" answerable by Git.

Learn more: Merge, squash and rebase.

You rebased your PR branch and need to push it. How do you force-push safely?
Show answer

git push --force-with-lease --force-if-includes, only ever on my own PR branch, and main and release branches protected from force pushes entirely. "A plain --force overwrites whatever is on the remote, including a commit a colleague pushed to my branch that I never fetched. --force-with-lease only overwrites if the remote still points where my remote-tracking ref says, so it's rejected as stale. But after a background fetch that ref is up to date even if I never looked at their commit, so --force-if-includes also checks that my branch has actually contained the remote tip. The golden rule underneath: never rebase commits other people have built on."

What’s happening
  1. In the chapter's model, Asha rebases a branch Ravi has pushed to: a plain push is rejected (fetch first), --force-with-lease is rejected (stale info), and after a fetch --force-if-includes is rejected (remote ref updated since checkout). A bare --force would have silently deleted Ravi's commit.
  2. --force-if-includes arrived in Git 2.30 (December 2020).
  3. The recovery is to start from what's really on origin (git reset --hard origin/feature/pairing), rebase that, and push with the lease.
  4. Make the safe form your only force push (an alias helps), and use a branch rule or ruleset that blocks force pushes on main (on by default for rulesets' targeted branches).

Learn more: Merge, squash and rebase.

What makes a good commit and a good commit message?
Show answer

One logical change that builds and passes tests on its own, with a short imperative subject and a body that explains why. "Each commit should be something you can review, revert or bisect to cleanly. The subject says what, in the imperative, and on our team in Conventional Commits form, fix(api): …, which also drives the version number and the changelog. The body says why: the problem, the cause, the decision, anything surprising, because the diff already shows what changed. I don't police it by hand: a hook or a CI check enforces the format. And with squash merging, the PR title and description become the commit on main, so that's where the effort goes."

What’s happening
  1. Chris Beams's seven rules (2014): blank line between subject and body, subject about 50 characters, capitalised, no full stop, imperative mood, body wrapped at 72, explain what and why rather than how.
  2. Conventional Commits 1.0.0: <type>[optional scope]: <description>; fix → PATCH, feat → MINOR, ! or a BREAKING CHANGE: footer → MAJOR. The two styles disagree on capitalisation; pick one per repository and let a tool enforce it.
  3. The mental model: an entry in the logbook for a reader who wasn't there. "fix stuff" and "address review comments" tell that reader nothing.
  4. Atomic in practice: git add -p to split a formatting change from a fix, refactors separate from behaviour changes, every commit builds (a broken commit is one git bisect has to skip).
  5. A commit-msg hook can be skipped with --no-verify and checks the wrong thing when you squash-merge; lint the PR title in CI.

Learn more: Commit hygiene and good commit messages.

The last release is v3.2.1. Since then: docs: fix typo, fix(ui): keep focus on the dialog, feat(api): add GET /v1/devices/{id}/logs and perf(api)!: drop the deprecated ?expand=all parameter. What's the next version?
Show answer

v4.0.0, because the ! marks a breaking change and breaking changes win. "Under Conventional Commits and Semantic Versioning, I look for breaking changes first: one ! or BREAKING CHANGE: footer means a MAJOR bump, whatever the type. MAJOR goes from 3 to 4 and resets the rest, so 4.0.0. The feature and the fix ship in it but don't add anything on top. Without the breaking commit it would be 3.3.0; with only the fix and the docs, 3.2.2; with only docs, no release at all."

What’s happening
  1. perf(api)!: is a performance commit that removes a parameter. Removing something clients may send is breaking whatever the motivation, which is why the marker is separate from the type.
  2. SemVer 2.0.0: MAJOR for incompatible API changes, MINOR for backwards-compatible features, PATCH for backwards-compatible fixes; bumping one resets the numbers after it.
  3. Release tools (semantic-release, release-please, changesets) do exactly this parsing and also write the changelog, so a wrong commit type is a wrong release.
  4. For a web app deployed continuously the version is mostly a label; for an API other teams call, or software on devices, it's a promise.

Learn more: Commit hygiene and good commit messages and Release branches, tags and hotfixes.

How do you handle a hotfix for a version that's already in production?
Show answer

Fix it on main first with a regression test, then cherry-pick it with -x to the release branch and tag the patch. "The fix goes through a normal, expedited PR on main, so it can never be lost from future releases. If production runs what's on main, that's it: deploy. If we support a released version separately, like software on devices that can't take every build, I cherry-pick the fix onto that version's release branch with -x, run its CI, tag v2.3.1 and ship. Only fixes go to release branches, never features, and release branches are never merged back."

What’s happening
  1. The rule is trunkbaseddevelopment.com's "branch for release": developers don't commit to release branches; fixes land on trunk and are cherry-picked down.
  2. -x appends (cherry picked from commit …) to the message, linking the two copies for anyone auditing what's in a patch.
  3. git log v2.3.0..v2.3.1 lists exactly what shipped in the patch; git cherry -v release/2.3 main shows which main commits are still missing (+) and which already have an equivalent on the branch (-), matched by patch content.
  4. The most expensive mistake is fixing only the release branch under pressure: the next minor release, cut from main, brings the bug back as a regression. If you must fix the branch first, open the main PR in the same hour and link them.
  5. In GitFlow the equivalent risk is forgetting the second merge of hotfix/* into develop.

Learn more: Release branches, tags and hotfixes.

release/4.1 was cut at v4.1.0. Since then main gained feat: device groups, fix: crash when a group is empty and fix: wrong time zone in call logs. What goes into v4.1.1?
Show answer

Only the time-zone fix. "A release branch only gets fixes for bugs that exist in that release. Device groups is a feature, so it waits for 4.2.0. The empty-group crash only exists in code that came with device groups, so 4.1 doesn't have that bug, and cherry-picking it would probably conflict or drag in half a feature. The time-zone bug is in 4.1.0, so that fix is cherry-picked with -x, the branch's CI runs, and it's tagged v4.1.1."

What’s happening
  1. Ask of each commit: does the bug it fixes exist in the released code? That's the only question for a patch branch.
  2. Afterwards git cherry -v release/4.1 main should show the time-zone fix with - (already there) and the other two with + (correctly missing).
  3. The tag is annotated (git tag -a v4.1.1 -m …): a real object with tagger, date and message, which is what releases use. Lightweight tags are bookmarks.
  4. Treat release tags as immutable: moving a published tag means two people can have different code under one version number. Rulesets can protect tags too.

Learn more: Release branches, tags and hotfixes.

A PR merged an hour ago breaks login, and three PRs have merged on top. How do you undo it?
Show answer

git revert -m 1 <the PR's merge commit>, through a normal PR, never git reset on main. "Revert adds a new commit that applies the inverse change, so everyone's history stays valid and the undo is itself reviewed, tested and deployed. Reset can only cut history at a point, so going back past the bad merge would also remove the three good PRs, and it rewrites main for everyone. -m 1 tells Git the first parent is the mainline, so it undoes what the branch brought in. With squash merging it's a plain revert of the squash commit. Reset is only for my own local or private work."

What’s happening
  1. Running the revert through a PR matters: CI checks that the three later PRs still work without the reverted code; they may depend on it.
  2. Resetting and force-pushing main "removes" commits only on the server. Everyone who already pulled still has them, and their next push may put them back. Block force pushes on main.
  3. The reset modes for your own work: --soft moves the branch and keeps changes staged, --mixed (default) keeps them unstaged, --hard discards them, and uncommitted work discarded by --hard can't be recovered.
  4. If someone already reset by mistake, git reflog on any machine that had the commits finds them; by default git gc expires reflog entries after 90 days, or 30 for unreachable ones. Recover promptly.

Learn more: Resolving conflicts and recovering mistakes.

You reverted a merged PR. The author fixed the bug on the same branch and it merged again, but the original feature is still missing. Why?
Show answer

Because Git already considers the original commits merged: re-merging the branch brings in only the new commits, so the revert stays in effect. Revert the revert. "The first merge made the feature's commits ancestors of main. The revert undid their changes but didn't un-merge them, so the second merge only applied the fix commit. The fix is git revert of the revert commit, which Git 2.54 titles 'Reapply …'. Alternatively, reapply the change on a fresh branch so the commits get new hashes. With squash merging this trap doesn't arise, because no branch commits were ever on main."

What’s happening
  1. In the chapter's model, after git revert -m 1 both export.js and escape.js are missing; after merging PR #9 only escape.js (the new commit) is present.
  2. git revert HEAD^1, the first parent of the second merge, is the revert commit; reverting it brings export.js back.
  3. Older Git titled it Revert "Revert …"; the "Reapply" wording arrived in Git 2.43.
  4. Put it in the same PR as the fix, so the feature comes back reviewed and tested together.

Learn more: Resolving conflicts and recovering mistakes.

How do you resolve a merge conflict well, and how do you get fewer of them?
Show answer

Read what each side changed against the common ancestor, combine both intents, ask the author when it's a product decision, then run the tests. "I turn on merge.conflictStyle=zdiff3, so the conflict shows the original lines as well as ours and theirs. With the ancestor I can see that, say, main changed the timeout and the retries while the branch changed only the timeout, so I keep main's retries change and decide the timeout with the author. Picking 'ours' or 'theirs' wholesale silently drops someone's work. A resolution is new code nobody reviewed, so the tests run before I push. To get fewer: integrate daily, one formatter everywhere, split hot files, and regenerate lockfiles rather than hand-merging them."

What’s happening
  1. zdiff3 arrived in Git 2.35 (January 2022). The markers are <<<<<<< HEAD (ours), ||||||| <base> (the ancestor), =======, >>>>>>> branch (theirs).
  2. git status --short shows UU: unmerged, changed on both sides. The merge is in progress until you git add and commit, or git merge --abort.
  3. rerere ("reuse recorded resolution") replays a resolution you've already made when the identical conflict appears again during repeated rebases.
  4. For package-lock.json, take either side and re-run npm install, then commit the regenerated file.
  5. A conflict isn't an error; it's Git asking a question only the authors can answer.

Learn more: Resolving conflicts and recovering mistakes.

Something broke between the last release and main, 1,000 commits apart. How do you find the commit?
Show answer

git bisect run with an automated check: about 10 test runs, because each one halves the range. "First I write the smallest script that exits 0 for good and 1 for bad, ideally the failing unit test or a request to the endpoint. Then git bisect start <bad> <good> and git bisect run <script>, and Git binary-searches on its own. If some commits don't build, the script exits 125 so they're skipped instead of blamed. This is also why I care about small commits that each build: bisect lands on one small, explainable commit instead of a 2,000-line squash or a broken intermediate state."

What’s happening
  1. 1,000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: ten halvings, since 2^10 = 1,024. The chapter's 24-commit example took five runs.
  2. Exit codes for bisect run (Git 2.54 docs): 0 good, 1–127 except 125 bad, 125 skip, anything else aborts. "Doesn't build" isn't "has the bug", so a load failure must exit 125.
  3. Skips cost runs without halving anything, and if the bug sits next to skipped commits, bisect can only return a short list of candidates.
  4. A flaky check is worse than a slow one: one false "good" sends the search into the wrong half and it confidently names an innocent commit.
  5. Useful options: --first-parent to test one merged PR at a time, -- src/api/ to consider only commits touching a path, --term-old/--term-new for performance regressions, and keep the script outside the repository so old checkouts can't change it.

Learn more: Finding the commit that broke it with git bisect.

What's the difference between continuous integration, continuous delivery and continuous deployment?
Show answer

Integration is a habit, delivery is "main is always deployable", deployment is "every green commit ships automatically"; each needs the one before. "Continuous integration is everyone merging to main at least daily, every merge verified by an automated build and tests, and a broken build fixed in minutes. Continuous delivery means any commit that passes the pipeline could go to production with one click, and when is a business decision. Continuous deployment removes the decision. For the web UI and API I'd aim for continuous deployment with feature flags, so deploying and releasing are separate; for software installed on devices I'd stop at continuous delivery with staged rollouts, because a bad release is much harder to undo there."

What’s happening
  1. Fowler's "Continuous Integration" (revised 18 January 2024) lists "Everyone Pushes Commits To the Mainline Every Day" among its practices. A CI server running on two-week branches is what he calls semi-integration.
  2. Fowler's test for continuous delivery (2013): a business sponsor could ask for the current development version to go live and nobody would panic.
  3. Jez Humble's three questions, as Fowler retells them: does everyone push to mainline at least daily; does every push trigger a build and tests; is a broken build usually green again within ten minutes? Most hands go down at the third.
  4. Name the habits before the tools in an interview: daily merges, fix-or-revert within minutes, flaky tests quarantined.

Learn more: What CI and CD actually mean.

A team runs all its tests on every PR. PRs stay open six to nine days, and main is green 95% of the time. Are they doing CI?
Show answer

No. They're doing automated testing on branches, which is useful, but work only integrates every six to nine days. "Humble's first question is whether everyone pushes to the shared mainline at least daily, and here they don't. Each PR is tested against main as it was when the branch last synced, so two PRs that each pass can still break each other once both merge. To make it CI: smaller PRs that merge within a day, unfinished features behind flags, and a merge queue so each PR is tested with what's ahead of it."

What’s happening
  1. The tests on a long-lived PR prove the branch works alone, not that it works with everyone else's changes.
  2. The integration-cost model: at a five- to ten-day rhythm, clashes surface days after the code was written, in bigger merges.
  3. "Main green 95% of the time" also hides something: red for an hour a day across a team means people pull broken code. The bar is fixing or reverting within minutes.
  4. A merge queue tests each PR combined with the ones ahead of it on a temporary branch; required workflows must listen to merge_group for it to work.

Learn more: What CI and CD actually mean and Quality gates in the pipeline.

Walk me through the CI pipeline you'd set up for a web UI and an API on GitHub Actions.
Show answer

One workflow on PRs, pushes to main and the merge queue, cheapest checks first, the build tested as an artefact, and one aggregator job as the required check. "With a read-only token: lint and type-check first; in parallel, unit tests as a matrix over the packages and Node versions we support, and API integration tests against a real Postgres in a service container. The web build waits for lint, uploads the bundle as an artefact, and the end-to-end jobs download that exact artefact and run Playwright in shards, so we test what we'd deploy. npm ci with the npm cache, never a cached node_modules. Short timeouts, superseded PR runs cancelled, and a final 'CI OK' job with if: always() as the single required check. It should take under ten minutes, or people stop waiting for it."

What’s happening
  1. The mental model: every job gets its own empty, disposable kitchen. Anything another job needs is packed and shipped as an artefact; anything worth not fetching again goes in the pantry, the cache, which may be empty.
  2. merge_group is required for merge queues; permissions: contents: read limits what a compromised dependency could do; concurrency cancels superseded runs on PRs only, so every commit on main is checked.
  3. Matrix: workspace: [web, api] × node: [24, 26] is four jobs; fail-fast: false lets the others finish, so you can see whether a failure is Node-specific. Artefact names must be unique within a run (immutable since upload-artifact v4).
  4. The service container waits for the pg_isready health check, and migrations run the same way as in production. Every job has a timeout-minutes far below the 360-minute default.
  5. The chapter's workflow passes actionlint 1.7.12 but hasn't been run on GitHub; its action versions (checkout@v7, setup-node@v7, and so on) were checked on 27 October 2026.

Learn more: A GitHub Actions pipeline for a web UI and API.

Cache or artefact? And why not just cache node_modules?
Show answer

A cache is a speed-up that may be missing; an artefact is a product a later job depends on. And caching node_modules ties the cache to one OS and Node version and skips npm ci's lockfile check. "I cache inputs: npm's download cache through setup-node, and incremental lint and TypeScript caches with the commit SHA in the key and restore-keys for the latest earlier entry. I use artefacts for outputs: the built bundle, coverage and test reports. If a cache is missing the job must still be correct, just slower; if an artefact is missing, the job that needs it fails."

What’s happening
  1. GitHub's docs say caches and artefacts "cannot be used interchangeably".
  2. Caches: removed after 7 days without access, evicted by oldest last access past the repository limit (10 GB by default); a run can restore from its own branch, the default branch and a PR's base branch, never from sibling branches. Keys are immutable, which is why incremental caches put the SHA in the key.
  3. Artefacts: 90 days by default (retention-days 1–90 unless the repository allows more), scoped to the run that made them.
  4. Don't cache Playwright's browsers either: Playwright's CI guide says restoring takes about as long as downloading.
  5. Caches are readable by anyone with read access, so never put tokens in them; setup-node's README recommends package-manager-cache: false in privileged workflows such as deploys.

Learn more: A GitHub Actions pipeline for a web UI and API.

What's wrong with run: echo "Testing ${{ github.event.pull_request.title }}" in a workflow?
Show answer

It's script injection: the PR title is attacker-controlled text pasted into a shell script before the shell runs. "A title like "; curl … | sh; echo " becomes part of the command. The fix is to pass it through an environment variable, env: TITLE: ${{ github.event.pull_request.title }}, and use "$TITLE" in the script, so the shell receives it as data. Branch names and PR bodies are the same. actionlint flags this as 'potentially untrusted', which is one reason we run it on every workflow change."

What’s happening
  1. ${{ }} expressions are substituted into the script text before execution, so quoting inside the script can't protect you.
  2. The related trap: pull_request_target runs with the base repository's secrets and a write token. Checking out and running a fork's code there hands an attacker your secrets ("pwn request"). actions/checkout@v7 refuses to check out fork PR code under pull_request_target and workflow_run unless you set allow-unsafe-pr-checkout: true.
  3. Use pull_request for anything that runs PR code; keep pull_request_target for jobs that only label or comment.
  4. What actionlint missed in the chapter's example: github.event.pull_request.draft == 'false' compares a boolean with a string, which is coerced to numbers (0 vs NaN), so it's never true. Write !github.event.pull_request.draft.
  5. Pin third-party actions by full commit SHA with the version in a comment, because a tag can be moved to malicious code.

Learn more: A GitHub Actions pipeline for a web UI and API and Keeping pipelines fast and reliable.

The only required check is "E2E". A PR broke a unit test, yet it merged. How?
Show answer

e2e needs the unit tests, so when they failed it was skipped, and GitHub counts a skipped job's check as passing. "That's documented behaviour: a job skipped by an if: or because a job it needs failed reports success, and dependent jobs skipped after a failure may not block merging. The fix is one aggregator job, 'CI OK', that needs every gate, runs with if: always() and fails if any result is failure or cancelled, and make that the only required check from the workflow. It also means renaming matrix jobs never breaks branch protection."

What’s happening
  1. unit fails → e2e is skipped, not failed → its check is green → the only required check passes.
  2. The chapter's model plays four PRs against three choices of required checks: requiring every job by name gets stuck when the matrix moves from Node 24 to 26/27 (the rule waits for a check nobody reports); requiring only E2E lets a lint failure merge; requiring "CI OK" handles both; only a path filter on the workflow's trigger defeats every choice (next card).
  3. Required checks are matched by name: GitHub's docs say a check and a commit status that share a name must both pass, two workflows that each have a job called test make the rule ambiguous, and a rename silently changes what's required. Give jobs explicit, unique names.
  4. And make the gates required in a ruleset or branch protection in the first place; otherwise they're coloured dots.

Learn more: Quality gates in the pipeline.

A docs-only PR can't merge: the required check says "Waiting for status to be reported". What happened?
Show answer

The required workflow has a paths-ignore (or paths/branches) filter on its trigger, so it never started, and a check that never reports stays pending forever. "GitHub's troubleshooting docs say not to put path or branch filters on a required workflow. Instead the workflow always starts, and the jobs decide inside, for example a small 'detect changes' job followed by jobs with if: conditions. A job skipped by if: reports success, so the docs-only PR merges, and code PRs still get the full checks."

What’s happening
  1. A workflow that doesn't run creates no check at all, and branch protection waits for a check it was told to require.
  2. In the chapter's model the paths-ignore version leaves every required check pending under all three strategies; the version where jobs skip themselves with if: is mergeable under all three.
  3. The asymmetry is the whole lesson: "skipped" means success, "never ran" means pending.
  4. Keep the required workflow unfiltered and fast, and put any path logic inside it.

Learn more: Quality gates in the pipeline and Automating the boring parts.

How would you set up deployments with approvals on GitHub?
Show answer

Build once, promote the same artefact: staging deploys automatically with a smoke test, production waits for a required reviewer on a protected environment. "Every merge to main builds one artefact named after the commit. Staging deploys it and runs a smoke test against its real URL. Production is a GitHub environment with two or three reviewers across time zones, self-review prevented, deployments only from main, and the production credentials as environment secrets, so the job can't read them until someone approves. Each environment has a concurrency group with cancel-in-progress off, so deploys never overlap or stop halfway. The steps live in a reusable workflow, the cloud login uses OIDC, and schema changes are expand-and-contract so rollback is one command."

What’s happening
  1. Rebuilding per environment ships something you didn't test: a dependency can publish a patch between the two builds. Inject environment config at deploy or run time (a single-page app can load /config.json).
  2. GitHub's rules for environments: one approval from up to six listed reviewers is enough; wait timers up to 30 days; environment secrets unlock only after approval; all protection rules must pass before the job is sent to a runner.
  3. The plan trap: on GitHub Free, Pro and Team, required reviewers and wait timers only work on public repositories; a private repository needs Enterprise. Deployment branch rules and environment secrets do work privately on Pro and Team, so the setup half-works and nothing looks broken. Check a client's plan before promising the control.
  4. A called reusable workflow can only keep or reduce the permissions it's given; the caller grants id-token: write for OIDC.
  5. Never cancel a running deploy: half-finished deploys are how you get half-migrated databases.

Learn more: Environments, approvals and deployment strategies.

Blue-green, canary, feature flags or a staged rollout: when do you use which, and why is the database the hard part?
Show answer

Canary to limit blast radius for risky backend changes, blue-green for a fast switch back, flags to separate release from deploy, and staged rings for software on devices; and every one of them depends on schema changes that old and new versions can both run against. "A canary sends 1–5% of traffic to the new version and compares its error rate and latency with the baseline over the same windows before widening. Blue-green runs a full new environment and switches traffic, so rollback is seconds but capacity doubles. Flags ship code switched off. Devices get rings: internal, a small customer group, then everyone. The database is what makes rollback hard: if version 2 renamed a column, version 1 can't run, so we expand and contract: add and write both, move readers, drop the old one later."

What’s happening
  1. The mental model: deploying is moving the furniture in; releasing is opening the door to the guests. Every strategy here separates the two.
  2. The canary's engineering is the decision rule. In the chapter's model: wait for a minimum sample (1,000 requests) before judging, compare with the baseline over the same windows, and allow twice the baseline plus 0.1 points; a release with 1.2% errors showed 1.75% at the first 1% step and was rolled back.
  3. Rolling deploys and canaries run old and new side by side, so APIs and schemas must be compatible both ways during the rollout.
  4. On a device platform the API devices call must also accept the previous device version's requests for as long as those devices are in the field.
  5. Rollback only works if you kept the old artefact and practised it. Know before you deploy whether you'd roll back or roll forward.

Learn more: Environments, approvals and deployment strategies.

Our CI takes 40 minutes. What would you do?
Show answer

Measure the critical path first, then cache dependencies, parallelise, remove false needs, and shard the end-to-end suite, while fixing flakiness at the same time. "With jobs in parallel, the pipeline takes as long as its longest chain of needs, not the sum of its jobs, so speeding up a job off that chain saves money but no waiting. The usual wins: the npm cache so jobs don't spend minutes installing, splitting one big job into parallel lint, unit, integration and E2E jobs, removing dependencies that aren't real, and sharding E2E, which is almost always the long pole. Cancel superseded runs, aim for under ten minutes on PRs, run the full browser matrix nightly. A fast pipeline nobody trusts is still useless."

What’s happening
  1. Fowler's CI article cites the XP guideline of a ten-minute build: long enough for real tests, short enough that people wait for the result.
  2. In the chapter's model (illustrative durations): one job, 27.5 minutes; parallel jobs, 24.2 (and double the runner minutes, because each job pays setup); plus npm cache, 19.7; two E2E shards, 13.7; four, 10.7; build no longer waiting for lint, 7.7.
  3. Diminishing returns: two to four shards saved 3 minutes, not 6, because every shard pays setup.
  4. Look at queue time too (waiting for a runner), which no caching fixes, and at whether slow tests hit the network or sleep.
  5. "Re-run failed jobs" as a reflex is how flaky tests survive; a re-run for a test failure should come with a flaky-test ticket.

Learn more: Keeping pipelines fast and reliable and Flaky tests.

What are the DORA metrics, and how would you use them?
Show answer

Five delivery metrics in two groups, throughput and instability, read together as trends for one service, never as targets or for individuals. "Throughput is change lead time, from commit to running in production, deployment frequency, and failed deployment recovery time. Instability is change fail rate, the share of deployments that need immediate intervention, and since 2024 deployment rework rate, the share of deployments that only happened because of an incident. DORA's key finding is that the best teams are fast and stable at the same time, because small, frequent changes are safer. I'd collect them automatically from the deploy pipeline and the incident tracker and use them to choose what to improve, like a long lead time pointing at slow reviews."

What’s happening
  1. Source: dora.dev's metrics page (updated January 2026). Recovery time was redefined in 2023 to count only failures caused by a change; rework rate was added in 2024.
  2. The mental model: a speedometer and an accident log. Throughput alone rewards driving into walls; instability alone rewards never leaving the garage.
  3. Measure from events you already have: production deployments (each environment: production job creates a deployment record), commits between deploys for lead time (use the median), incidents linked to the deploy that caused them.
  4. dora.dev's guide lists seven pitfalls: setting the metrics as goals (it names Goodhart's law), one metric to rule them all, using the industry as a shield, making disparate comparisons, siloed ownership, competing, and measuring instead of improving, where what you change is the capabilities DORA links to the metrics (trunk-based development, CI, test automation, small batches). Our own advice on top, not on dora.dev's list: never rate individuals with them.
  5. Worth knowing: the 2025 report, "State of AI-assisted Software Development", describes seven team profiles from a cluster analysis instead of leading with the low/medium/high/elite tiers of earlier reports; whether those tiers are gone for good isn't stated.

Learn more: DORA metrics.

A team was told to raise deployment frequency. It now makes 400 deploys a quarter instead of 100, lead time hasn't moved, change fail rate fell from 20% to 5% and rework rate from 5% to 1.25%. Did delivery improve?
Show answer

Probably not: the team most likely split releases into more deploys without changing how fast work flows, and the same failures are spread over a bigger denominator. "If changes really got smaller and moved faster, lead time would fall too, and it didn't. Change fail rate is failed deploys over all deploys, so four times the deploys with the same 20 incidents gives a quarter of the rate. Rework rate fell by exactly the same factor, which is the tell: I'd check the counts behind the ratios, and if the tracker still shows 20 deploy-caused incidents and 5 hotfix or rollback deploys, nothing got safer. That's what happens when one metric becomes a target. The useful question is what stops a commit reaching production faster: reviews, the pipeline, a manual release step."

What’s happening
  1. Lead time is the honest signal here: it measures waiting between commit and production, which splitting deploys doesn't change.
  2. The fail-rate arithmetic: 20 incidents in 100 deploys is 20%; the same 20 in 400 deploys is 5%.
  3. Rework rate is unplanned deploys over all deploys: the same 5 hotfixes and rollbacks are 5% of 100 and 1.25% of 400. Both rates fell by exactly the factor the deploy count rose; real improvement shows up in the counts and in lead time, not only in a ratio.
  4. DORA's guidance: read the five together, as trends for one service over time. Write the definitions next to the dashboard and don't change them when the numbers look bad.

Learn more: DORA metrics.

#Testing and delivery questions

Added

This group covers testing strategy (which kind of test catches what, the pyramid and the trophy, coverage, flaky tests, legacy code) and day-to-day delivery (Scrum as the Guide defines it, estimation and forecasting, Kanban and flow, shifting priorities with incomplete documentation, and the recurring events).

The mental model for this group is a lead who answers "it depends" and then says what it depends on. Interviewers aren't looking for the one right shape of test suite or the one right process; they want to hear the trade-off, the number that decides it (a flake rate, a WIP count, a capacity), and what you'd measure afterwards. Where a card quotes the Scrum Guide, it's the November 2020 edition, still the current one in October 2026.

How do you decide what kind of test to write?
Show answer

I ask what could break and which is the cheapest test that would catch it. "Logic with lots of cases, like validation, formatting or permission rules, gets unit tests, because I can cover every edge in milliseconds. Wiring, the SQL, status codes, the JSON shape and middleware get integration tests against the real HTTP layer and a real database in a container, because that's where most API bugs live. Where two things deploy separately, the web UI and the API, or the API and devices in the field, I add contract tests so a breaking change fails in the provider's pipeline. Then a small set of end-to-end tests for the journeys that would page someone. Each level answers a different question."

What’s happening
  1. The mental model: checking a car. Unit tests are parts on a workbench, integration tests the engine on a test stand, a contract test the agreed drawing of the bolt pattern between two suppliers, and E2E the test drive. You wouldn't find a faulty caliper by driving round the block a thousand times.
  2. The more real system a test includes, the more it can catch, and the slower, flakier and less precise it gets: a failing unit test names a function; a failing E2E test says "something between the browser and the database".
  3. Names are fuzzy: Fowler distinguishes solitary unit tests (collaborators faked) from sociable ones (real collaborators, still fast). Define tests by what they include and what they're for.
  4. E2E: ten to thirty critical journeys, not three hundred; "a 33-character name is rejected" belongs in a unit test, and the E2E suite checks once that a rejection is shown.
  5. Device platforms add hardware-in-the-loop tests on real units, slow and scarce, run on a schedule or before a release; contract tests keep everyday changes from needing them.

Learn more: Unit, integration, contract and end-to-end tests.

Which is the cheapest test for each bug? (a) stepVolume(95, 10) returns 105; (b) the API sends firmware_version but the UI reads firmwareVersion; (c) a missing migration makes PATCH /devices/:id fail; (d) the production build points at the staging API.
Show answer

(a) unit, (b) contract, (c) integration, (d) end-to-end or a post-deploy smoke test. "The clamp is pure logic at a boundary, so a unit test with (95, 10) catches it in microseconds. The field-name mismatch passes both sides' own tests, because each is self-consistent; a consumer contract verified against the provider catches it in the API's pipeline. The missing migration passes with a mocked repository and fails against PostgreSQL. And the wrong URL is configuration of the deployed artefact, which no test inside the codebase sees."

What’s happening
  1. The question behind each answer is "where does this bug live?": in a function, between two deployables, in the wiring to real infrastructure, or in the deployed configuration.
  2. (b) would also be caught by E2E, but later and slower; contract testing exists to catch it on the provider's own pipeline.
  3. (c) is the classic argument against mocking what you own: a "unit" test with a fake database says nothing about the SQL.
  4. (d) is one reason for "build once, inject config at deploy time": a smoke test against the real URL right after the deploy catches it.

Learn more: Unit, integration, contract and end-to-end tests.

Stubs, spies, fakes and mocks: what's the difference, and what should you mock?
Show answer

A stub returns canned answers, a spy is a stub that also records how it was called, a fake is a working simplified implementation, and a mock is pre-programmed with the calls it expects and verifies them itself. Mock the edges you don't own; use fakes or the real thing for what you do own. "The vocabulary is Meszaros's, from xUnit Test Patterns. I'd use a fake device store that really stores things in a Map, a stub for 'latest firmware version', and a spy on the push channel to check it was called once, for the right device. What I avoid is mocking the database, the HTTP layer and the validation library in one test: then it only proves my code calls the mocks the way the test says, passes with wrong SQL and breaks on every refactor."

What’s happening
  1. A dummy is passed but never used, the fifth kind; Fowler's "Mocks Aren't Stubs" popularised the terms.
  2. node:test has mock.fn() and mock.method() built in; despite the name they make spies: push.mock.callCount() and push.mock.calls[0].arguments are the record the test checks afterwards. In everyday speech people call both "mocks", so say which you mean.
  3. Fakes survive refactors because they implement the real interface rather than a script of expected calls.
  4. Assert on outcomes before calls. Doubles also surface real problems: in the chapter's example, making the push spy throw showed that one failed push silently skips every device after it.
  5. Edges worth doubling: third-party APIs, push services, the clock, other teams' services. The database is cheap to run for real in a container.

Learn more: Unit, integration, contract and end-to-end tests.

When are contract tests worth it, and what do they catch that other tests miss?
Show answer

When the consumer and the provider deploy separately: the web UI and the API, or the API and software on thousands of devices. "Each side's own tests pass, because each is self-consistent, so a breaking change only shows up when both are deployed together. A contract records what each consumer actually reads; the provider verifies its responses against every consumer's contract in its own pipeline. Then 'flatten firmware.version' or 'add status updating' is blocked before it ships, and the report names exactly who would break. Pact is the best-known tool, and it's consumer-driven: contracts come from the consumers' own tests."

What’s happening
  1. Pact describes contract testing as checking an integration point by testing each application in isolation (docs.pact.io, checked by the testing chapter on 27 October 2026).
  2. In the chapter's model, adding lastSeen is safe for everyone; flattening firmware.version breaks only the device page; adding an enum value "updating" breaks both consumers even though nothing was removed.
  3. The enum case is the most commonly missed kind of API break, for any consumer that switches on the value.
  4. With a blocked change, the provider can expand and contract: add the new field alongside the old one, migrate the UI, then remove the old one.
  5. A Pact broker answers "can I deploy this version?" against the consumer versions actually in production.

Learn more: Unit, integration, contract and end-to-end tests and API and documentation standards.

Test pyramid or testing trophy?
Show answer

Both are ways to spend a testing budget, and they agree on what matters: few end-to-end tests, and most of the suite fast and reliable. "The pyramid, from Mike Cohn and Martin Fowler, puts most tests at the unit level because UI tests are slow and brittle. Kent C. Dodds's trophy adds static analysis at the base and puts most tests at the integration level, because in a JavaScript UI many units are thin glue and the bugs are in how pieces fit. Fowler's point is that the disagreement is largely about what we call a unit test. For a web UI I lean towards the trophy, with component tests that render real children; for the API, lots of fast tests against real routes and a real database. I'd choose by where our bugs actually come from."

What’s happening
  1. Cohn's "Test Automation Pyramid" (Succeeding with Agile, 2009); Fowler's "TestPyramid" (2012); the anti-pattern is the ice-cream cone, mostly manual or UI tests on top of a few unit tests.
  2. The trophy grew from Guillermo Rauch's 2016 tweet "Write tests. Not too many. Mostly integration." and Dodds frames it as return on investment: confidence per minute.
  3. The mental model: both are budgets, not laws. Fowler's 2021 article argues that what trophy fans call integration tests are often what he calls sociable unit tests.
  4. The resulting shape for a web UI and API is closer to a trophy on the UI side and a pyramid on the API side. Say so and say why.
  5. When tests run is part of the strategy: types and lint on save, staged-file checks before commit, everything that blocks merging in under ten minutes on every push, the full browser matrix and mutation testing nightly.

Learn more: Test pyramid vs testing trophy.

A new initiative adds 80 end-to-end tests "for coverage" to a suite of 2,000 unit, 200 integration and 20 E2E tests. What happens to red builds?

State the per-test assumptions you use.

Show answer

Red builds with no bug go from about one run in 39 to about one in 10, and the run gets four times longer. "A run is green only if no test flakes, so the chance compounds with every flaky-ish test, and E2E tests are both the slowest and the flakiest. Eighty more takes the suite from 2.6% to about 10% of runs red for nothing, and from 1.5 to 6.5 minutes on four workers. Covering the same behaviour with 800 integration tests instead would cost 2.3 minutes and 4.1%. So I'd ask what those E2E tests were meant to protect, cover most of it at the integration or contract level, and keep only the ones that guard a critical journey."

rq-flake-odds.mjs
// Red builds with no bug: a run is green only if no test flakes. Per-test numbers are illustrative assumptions.
const kinds = { unit: { secs: 0.005, flake: 1e-6 }, integration: { secs: 0.25, flake: 1 / 50_000 }, e2e: { secs: 15, flake: 1 / 1_000 } };
const WORKERS = 4;

function suite(label, counts) {
  let green = 1, secs = 0;
  for (const [kind, n] of Object.entries(counts)) {
    green *= (1 - kinds[kind].flake) ** n;
    secs += kinds[kind].secs * n;
  }
  const red = (1 - green) * 100;
  console.log(`${label.padEnd(29)} ${(secs / 60 / WORKERS).toFixed(1).padStart(4)} min  red with no bug ${red.toFixed(1).padStart(4)}%  (1 run in ${Math.round(100 / red)})`);
}

suite("2,000 unit, 200 int, 20 E2E", { unit: 2000, integration: 200, e2e: 20 });
suite("... plus 80 more E2E", { unit: 2000, integration: 200, e2e: 100 });
suite("... or 800 more integration", { unit: 2000, integration: 1000, e2e: 20 });
Result
2,000 unit, 200 int, 20 E2E    1.5 min  red with no bug  2.6%  (1 run in 39)
... plus 80 more E2E           6.5 min  red with no bug 10.1%  (1 run in 10)
... or 800 more integration    2.3 min  red with no bug  4.1%  (1 run in 24)
What’s happening
  1. The per-test figures are the testing chapter's illustrative assumptions, not measurements: a unit test takes 5 ms and flakes once in a million runs, an integration test 250 ms and once in 50,000, an E2E test 15 s and once in 1,000.
  2. Green means every test passed, so green is the product of (1 − p)ⁿ over the three kinds; red with no bug is 1 − green. For the base suite, 0.999²⁰ × 0.99998²⁰⁰ × 0.999999²⁰⁰⁰ ≈ 0.974, so 2.6% of runs are red for nothing.
  3. With 100 E2E tests, 0.999¹⁰⁰ ≈ 0.905 on its own, and the whole suite is green only about 89.9% of the time: 10.1% red. Time goes from 10 + 50 + 300 = 360 seconds of tests to 10 + 50 + 1,500 = 1,560, divided by four workers.
  4. The third line is the alternative, 800 more integration tests (1,000 in all, five times as many as before): ten times as many new tests as the 80 E2E ones, yet only 2.3 minutes and 4.1%, because each is 60 times faster and 50 times less flaky than an E2E test.
  5. The lesson the chapter draws from the same numbers: the number of slow, flaky tests sets the cost of the whole suite, so the top of either shape must stay small. Change the assumptions and the figures move: the time saving holds for any integration test far faster than 15 s, but the red-build saving holds only while an integration test is much less flaky than an E2E one (at 1 in 10,000 instead of 1 in 50,000, 800 integration tests go red as often as 80 E2E tests).

Learn more: Test pyramid vs testing trophy and Flaky tests.

What coverage target would you set?
Show answer

I'd use coverage to find untested code, not as a target: a floor a little below where we are, and in review, coverage of the changed lines. "Coverage says which code ran, not whether anything was checked; a test with no assertions can reach 100%. So a floor stops decay without inviting games, and an untested new branch in a PR is the useful signal. I'd count branches, not just lines. Google's guidance is roughly 60% acceptable, 75% commendable and 90% exemplary, with per-commit coverage more useful than the project figure, and Fowler is suspicious of 100%. Critical code like auth or the device command path gets a higher bar, and occasionally mutation testing, which shows whether the tests actually check anything."

What’s happening
  1. The mental model: coverage is the map of where the inspector walked, not a record of what they inspected. A room missing from the map wasn't looked at; a room on it was only passed through.
  2. Sources: Fowler's "TestCoverage" (2012); Google Testing Blog, "Code Coverage Best Practices" (2020), which warns against top-down mandates.
  3. Line versus branch: in the CI chapter's Vitest run, one happy-path test gave 100% lines but 50% branches, because if (…) return …; counts as a covered line when only the condition ran.
  4. The testing chapter's diff-coverage model: a file at 84% overall, but only 57% of the lines the PR added had run, and the uncovered ones were the whole new feature.
  5. Mandate 90% and you get tests without assertions, snapshot tests of everything and ignore comments. Goodhart's law applies to coverage as much as to DORA metrics.

Learn more: Coverage: useful signal or vanity metric and Quality gates in the pipeline.

isMaintenanceWindow(hour) should be true from 2 a.m. up to but not including 5 a.m. The code is hour >= 2 && hour <= 5, the one test checks hour 3, and line coverage is 100%. What's wrong?
Show answer

The code includes 5 a.m., and a mid-window test can't see it; boundary tests at 1, 2, 4 and 5 would. "Coverage is 100% because the only line ran, and both sides of the && were evaluated, so branch coverage doesn't help either. At hour = 5, 5 <= 5 is true, so devices could reboot at 5 a.m. A test for 5 → false fails against this code and passes once it's < 5. That's the mutation-testing lesson in one function: a mutant that turns < into <= survives a mid-range test and dies to a boundary test."

What’s happening
  1. Boundary values are exactly where clamps, windows and limits fail in production, and exactly what a mutation tester pushes you to write.
  2. In the testing chapter's model, a weak suite with 100% line coverage killed 0 of 6 mutants of a volume clamp; a suite asserting exact results at the boundaries killed 5.
  3. The survivor, > 100 → >= 100, is an equivalent mutant: at exactly 100 both versions return 100, so no test can tell them apart. Reading a mutation score means recognising those.
  4. Stryker is the common mutation tool for JavaScript and TypeScript; it's slow, so run it nightly or weekly on a few critical modules.

Learn more: Coverage: useful signal or vanity metric.

How do you handle flaky tests on a team?
Show answer

Make them visible from CI data, quarantine them quickly with an owner, fix the cause, and never retry silently. "A test that passed and failed on the same commit is flaky by definition; a test that fails on every run since a commit is broken, and needs bisecting, not retrying. Flaky tests get tagged out of the blocking suite but run every night, each with an owner and a ticket, so they stop teaching people to ignore red. Then we fix the cause: usually shared state between tests, sleeping instead of waiting for an event, real time instead of a fake clock, or a real network call. Retries are allowed only if retried passes are reported as flaky. Google found that most of their pass-to-fail transitions involved a flaky test, so this is a real cost."

What’s happening
  1. Google's numbers (Micco, 2016): about 1.5% of test runs reported a flaky result, almost 16% of tests had some flakiness, and about 84% of observed pass-to-fail transitions involved a flaky test.
  2. The mental model: a smoke alarm that goes off when you make toast. Someone takes the battery out, and then it doesn't sound for a fire either.
  3. Fowler's causes (2011): lack of isolation, asynchronous behaviour, remote services, time, resource leaks. Fixes: fresh state per test, wait for the event (Playwright's auto-waiting), mock.timers, stubs at the edge, port 0 and temporary directories.
  4. Tools in the chapter: Node 26.1's --test-randomize with --test-random-seed=<n> to replay an order-dependent failure; Playwright's @quarantine tag with --grep-invert in CI and a nightly --repeat-each=20 run.
  5. The lead's part: a weekly flake report, a two-week limit per quarantined test, and a rotating "build cop" with a handover note between India and the US. Before quarantining, ask whether it's a real race in the product: then it's a bug, not a test problem.

Learn more: Flaky tests.

CI history for one test: c1 passed; c2 failed, then passed on re-run; c3 failed twice; c4 failed; c5 failed twice. Flaky or broken?
Show answer

Broken since c3, so bisect between c2 and c3; the flake on c2 is a separate, smaller problem. "On c2 it failed and passed with the same code, so it's flaky there. But from c3 onwards it never passes again, which means a change in c3 broke it. Re-running would just burn CI minutes. I'd find the change with git bisect between the last pass and c3, fix or revert, and then look at the c2 flake on its own, with repeats or randomised order."

What’s happening
  1. The detector logic from the chapter: a commit in both the "failed" and "passed" sets proves flakiness; a commit after which the test never passes again marks a breakage.
  2. Treating a breakage as a flake ("just re-run") is how real bugs ship; treating a flake as a breakage costs a day bisecting noise. Separating the two is the value.
  3. A flaky check also poisons bisect itself: one false "good" sends the search into the wrong half. Make the check deterministic first, or run it several times inside the script.

Learn more: Flaky tests and Finding the commit that broke it with git bisect.

You join a codebase with almost no tests. Where do you start?
Show answer

Not with a big testing project: make what exists run in CI, then test where the next changes are, starting with characterisation tests. "Before touching a piece of code I'd pin what it does today, quirks included, so any behaviour change becomes a visible decision. Every bug fix starts with a failing test that reproduces it. To get code under test I'd make small, safe seams, like passing the clock or the HTTP client in as a parameter with the old default. Beyond that I'd prioritise hotspots, files that are both complicated and frequently changed, from the Git history. For an API with poor documentation I'd record real request and response pairs as golden-master tests; they double as documentation."

What’s happening
  1. Michael Feathers (Working Effectively with Legacy Code, 2004): legacy code is code without tests; a characterisation test records what the code actually does; a seam is a place where you can alter behaviour without editing in that place.
  2. The mental model: photograph every room before renovating an old house. The photos aren't a judgement that the house is right; they're how you'll know what you changed.
  3. In the chapter's example, a tidy refactor of a duration formatter failed on exactly two pinned inputs (59.6 and -30), turning an invisible change into a decision for someone who knows whether devices send those values.
  4. Hotspots (Adam Tornhill, Your Code as a Crime Scene, 2nd edition 2024): churn from git log --name-only times a complexity measure; a complicated file nobody changes can wait.
  5. For a Telnet-style device protocol, record real sessions and replay them against the rewrite: in the chapter's transcript, upper-case SET VOLUME 70 worked in the old implementation and not in the new one, which no unit test would have thought to check.

Learn more: Adding tests to a legacy codebase.

You must change how a 400-line, untested function rounds prices. It reads the current date and calls a pricing service over HTTP. What do you do first?
Show answer

Make seams for the clock and the pricing call, pin the current outputs with characterisation tests, and only then change the rounding. "The smallest safe edit is a parameter with defaults, { now = () => new Date(), getPrice = realPricingCall } = {}, so production callers are unaffected and tests can pass a fixed clock and a fake price. Then I'd run it over a spread of real, anonymised inputs: month ends, leap days, every price tier, odd cases from the logs. I'd pin what it returns today, change the rounding, and the tests that fail are exactly the prices that change, which I'd check with whoever owns pricing before updating those pinned values in the same PR."

What’s happening
  1. That's Feathers's "parameterise method": the signature change is backwards compatible.
  2. The suite passes against the legacy code by construction; its job is to make the next change visible.
  3. The trap: writing down what the code should do while pinning it. Then the test fails on day one and you either change behaviour someone depends on or weaken the test.
  4. Behaviour changes go in a separate, named step with the test change in the same PR, so the reviewer sees "this used to return X, now Y, because…".

Learn more: Adding tests to a legacy codebase.

Explain Scrum in two minutes.
Show answer

A lightweight framework for complex work based on empiricism: three accountabilities, five events and three artifacts, each with a commitment. "The Product Owner maximises value and orders the Product Backlog; the Scrum Master is accountable for the team's effectiveness and for Scrum being understood; the Developers build a usable, Done Increment each Sprint. Inside Sprints of one month or less, Sprint Planning sets a Sprint Goal and a plan, the Daily Scrum is a 15-minute check on progress toward that goal, the Sprint Review inspects the outcome with stakeholders, and the Retrospective improves how we work. The artifacts are the Product Backlog with the Product Goal, the Sprint Backlog with the Sprint Goal, and the Increment with the Definition of Done. Story points, user stories and velocity are optional practices, not Scrum. That's the 2020 Guide, still the current edition."

What’s happening
  1. The mental model: the Scrum Guide is the rulebook of a sport, not a coaching manual. Six-week Sprints and a manager-run standup are a different sport with the same name; "Scrum says you must use story points" quotes a coaching manual as the rulebook.
  2. Pillars: transparency, inspection, adaptation. Values: commitment, focus, openness, respect, courage.
  3. Timeboxes are maximums for a one-month Sprint (Planning 8 h, Review 4 h, Retro 3 h), and the Guide says only that shorter Sprints usually have shorter events. "Four hours for a two-week Sprint" is a rule of thumb, not the Guide.
  4. Not in the 2020 text: story points, velocity, user stories, the word "estimate" (the Developers are responsible for "sizing"), Definition of Ready, Sprint Zero, and the three standup questions (the 2017 Guide's example, dropped in 2020).
  5. The 2025 Scrum Guide Expansion Pack calls itself a companion to the 2020 Guide, not a replacement, so "the rules of Scrum" are still the 2020 text.

Learn more: Scrum in one page.

Where does a tech lead fit in Scrum?
Show answer

The Guide doesn't define one: a tech lead is one of the Developers, and the team is self-managing, so I don't assign the work. "What I bring is technical direction and quality. I help the Product Owner understand the cost and risk of items, make sure refinement produces items the team can actually build, keep the Definition of Done meaningful, unblock people, especially across the time zone, and review and pair a lot. I influence what the team decides through clarity and coaching, not by deciding for them. On a team split between the US and India, a big part of the job is making sure decisions and context reach everyone the same day, in writing."

What’s happening
  1. The Guide's accountabilities are Product Owner, Scrum Master and Developers; "self-managing" means the team decides who does what, when and how.
  2. A tech lead asked to "just tell people what to work on" can make priorities and dependencies clear enough that the team picks the right work itself.
  3. The Scrum Master isn't the manager either: assigning tickets or running the Daily Scrum as a status report stops self-management.
  4. "No sub-teams or hierarchies" matters for distributed teams: "the onshore team designs, the offshore team executes" breaks it. Either one team with shared goals, reviews and retros, or two Scrum Teams under one Product Owner and Product Backlog.

Learn more: Scrum in one page and What a senior or lead engineer is expected to do.

On day 6 of a two-week Sprint, the client's engineering manager (not the Product Owner) tells your team to drop the Sprint Goal for an urgent integration. What do you do?
Show answer

Route it to the Product Owner, the same day, with the trade-off made visible; don't start it quietly in parallel. "The Guide says no changes that would endanger the Sprint Goal, and only the Product Owner can cancel a Sprint or renegotiate scope with the Developers. So I'd say: 'If we start this now, the firmware-scheduling goal won't be met; here's what's done and what the integration would take', and get the Product Owner and the engineering manager into one conversation. Either the goal is obsolete and the PO cancels the Sprint and we replan, or the integration goes to the top of the backlog for the next Sprint. Then it's written down so the India developers see the decision and the reason at the start of their day."

What’s happening
  1. "During the Sprint", the Guide sets four rules: no changes that endanger the Sprint Goal; quality doesn't decrease; the Product Backlog is refined as needed; scope may be clarified and renegotiated with the Product Owner.
  2. "Only the Product Owner has the authority to cancel the Sprint." An engineering manager doing it takes away the one power the Guide gives only to the PO.
  3. Quietly starting the integration "in parallel" is the most common way a Sprint fails without anyone deciding to fail it.
  4. A goal written as an outcome ("an admin can schedule a firmware update") rather than "tickets 101–112" is what gives the team room to absorb small surprises.

Learn more: Scrum in one page and Shifting priorities and incomplete documentation.

How do you estimate and plan as a lead?
Show answer

Three different questions, three levels of precision: rough sizes to order the backlog, capacity-based ranges for the Sprint, and probabilistic forecasts for dates. "The Developers size items themselves, usually in points with planning poker. For the Sprint I plan from capacity, who's actually here, with holidays on both the US and India sides, leave, and a new joiner who'll take time from a buddy, times the usual velocity, as a range, and the Sprint Goal comes first. For dates I don't divide by average velocity; I forecast from throughput with a range, like '80% likely by mid-March', and re-forecast every Sprint. Unknowns become timeboxed spikes. And I never use velocity to compare people or teams."

What’s happening
  1. The mental model: a weather forecast: '70% chance of rain', updated as information arrives, and nobody blames the forecaster personally. Turn estimates into promises and they inflate until they're useless.
  2. Ron Jeffries (2019) says he may have invented story points and is sorry now; points are a tool for the team's planning conversation, not a currency.
  3. The chapter's capacity model: a Sprint containing US Thanksgiving (26 November 2026, with the Friday after also off) has 87% of normal capacity, so a 22-point average becomes a plan of 16–20 points.
  4. Planning poker's value is the conversation: when one person says 2 and another 13, one of them knows something. Anything that keeps coming out at 13 or more gets split.
  5. A spike has a specific question, a timebox and a written output; at the timebox you stop and report.

Learn more: Estimation and planning.

A five-person team averages 25 points. Three developers join in India on Monday, and the manager asks you to commit to 40 points next Sprint. What do you say?
Show answer

Plan for less than 25 for the first Sprint or two, not 40, and say why with numbers. "New joiners spend their first Sprint mostly learning: environment, codebase, domain, our review standards. And their onboarding takes time from the experienced people who produce most of the 25: buddying, reviewing first PRs, pairing, answering questions across the time zone. So I'd say 'about 20 next Sprint, back to 25 in two, and then we'll see the new baseline from real data'. It's Brooks's law in small: adding people to a late project makes it later, at least at first."

What’s happening
  1. 25 × 8 ÷ 5 = 40 assumes every new person is instantly as productive as the old ones and costs nobody anything. Both are false in the first weeks.
  2. The chapter's capacity model gives one new joiner a focus factor of 0.4 and lowers the lead's and the buddy's focus because of the onboarding; three joiners at once concentrate that cost.
  3. Measure the new velocity rather than assuming it; it typically recovers and then grows over several Sprints.
  4. Fred Brooks, The Mythical Man-Month (1975): "adding manpower to a late software project makes it later".

Learn more: Estimation and planning and Onboarding and mentoring offshore engineers.

The client asks when the remaining 40 items will be done. What do you tell them?
Show answer

A range with probabilities, from the team's real throughput, not one date from an average. "From the last eight Sprints a Monte Carlo forecast says: 8 Sprints is likely, about 80%; 9 is almost certain; 7 is roughly a one-in-three bet. If you need 7, the conversation is which items can wait, not working harder. And I'll re-forecast every Sprint, because a forecast is a living number."

What’s happening
  1. Dividing 40 by the average throughput (5.375 items a Sprint) gives "8 Sprints" and hides the spread.
  2. The chapter's seeded simulation replays the team's own past Sprints (5, 7, 4, 6, 6, 3, 7, 5 items) in random order 10,000 times: 83% of futures finish within 8 Sprints, 99% within 9, 31% within 7.
  3. Counting items rather than points works when the team splits items to roughly similar size, and it avoids estimating the remaining 40 at all.
  4. Giving the probability up front turns a deadline conversation into a scope conversation, which is the one a client can actually act on.

Learn more: Estimation and planning.

When would you choose Kanban over Scrum?
Show answer

When work arrives unpredictably and the job is to respond quickly: a support or bug queue, ops work, a platform team serving others. "There a two-week commitment would be broken by the first urgent issue. Kanban makes the flow visible, limits work in progress, and manages it with four measures: WIP, throughput, Work Item Age and cycle time. With a service level expectation like '85% of issues resolved within 5 days' the client gets a promise we can keep. For feature work with a client who wants a regular review I'd use Scrum, often with Kanban practices inside it. And the reason WIP limits matter is Little's Law: cycle time is WIP divided by throughput."

What’s happening
  1. The Kanban Guide (May 2025, Coleman and Vacanti) defines three practices (visualise, actively manage items, improve the workflow) and requires those four flow measures.
  2. The mental model: a motorway. Once it's full, more cars don't get more cars home per hour; they make every journey slower.
  3. Work Item Age is the most useful number on the board because it's about items you can still do something about: past the SLE means swarm, split, or chase what it's waiting on (often a reviewer in the other time zone).
  4. A feature team that also owns production issues can keep Sprints and add an interrupt lane with its own WIP limit, sized from the last few Sprints' real interrupt load.
  5. "Scrumban" isn't defined by either guide; say what you mean if you use the word.

Learn more: Kanban and when it fits better.

A team has about 12 items in progress and finishes 3 a week. Requests take a month. How do you get to two weeks without adding people?
Show answer

Limit WIP to about 6: by Little's Law, cycle time is WIP divided by throughput, so 12 ÷ 3 = 4 weeks and 6 ÷ 3 = 2 weeks. "Throughput is set by the team's capacity and item sizes, so starting more work doesn't finish more. The lever left is WIP: stop starting, start finishing. When a column is at its limit, the next useful thing is to help finish or unblock something. It will feel slower to individuals at first and faster for the work, and I'd measure for a few weeks rather than expect it overnight."

What’s happening
  1. The chapter's simulation runs the same three-person team on the same backlog with WIP limits of 3, 6 and 12: throughput stays about 0.95 items a day, and cycle time scales with WIP (3.2, 6.3, 12.5 days), as Little's Law predicts.
  2. That model charges nothing for context switching or waiting across time zones, so real high-WIP teams are slower still.
  3. Little's Law holds for a stable system on average; in practice throughput often rises a little when WIP falls, because less is waiting.
  4. Watch for limits dodged by hiding work: an unlimited "blocked" column, or side work tracked nowhere. A limit nobody enforces is decoration.

Learn more: Kanban and when it fits better.

How do you handle shifting priorities and incomplete documentation?
Show answer

Treat both as normal and make them explicit: an assumption log for what we don't know, and every priority change decided by the Product Owner with its cost visible. "For missing docs, I keep an assumption log next to the code, each assumption with its evidence, the risk if wrong, an owner and a date to confirm it. Easy-to-reverse decisions I make quickly on an assumption; hard-to-reverse ones, like a public API's shape, I confirm first, often with a timeboxed spike against a real device or simulator, and I pin what we learn with contract tests. For priority changes I don't refuse and I don't silently absorb them: I show the impact on the Sprint Goal, give two options with dates and a recommendation, and get the decision in writing so the India team sees it at the start of their day."

What’s happening
  1. The mental model: exploring with a half-drawn map. Confirmed facts in ink, assumptions in pencil, a scout ahead before the whole group marches. The danger is pencil marks everyone treats as ink.
  2. One-way and two-way doors: Bezos's 2015 shareholder letter (published April 2016) separates hard-to-reverse "Type 1" decisions from easy "Type 2" ones.
  3. Questions for the client go in one maintained log with a "needed by" date and a default if no answer, so silence becomes a decision rather than a delay. Pick the default that's safer to be wrong about.
  4. With an undocumented device API: ask once in writing with a deadline, observe and record real behaviour per firmware version, pin it with tests, write notes in the repository with the date and firmware version, and send corrections back to the client.
  5. The trap is the silent scope swap: "sure" on a call, one developer starts the new thing, nobody updates the board or the goal, and the Review shows half of two things.

Learn more: Shifting priorities and incomplete documentation.

"Can you just squeeze this in?" A 3-point login bug and an 8-point demo feature both arrive on day 4 of a full Sprint. What do you reply?
Show answer

Do the arithmetic before replying: the bug fits by moving one non-goal item out; the demo feature doesn't fit without dropping goal work, so that's the Product Owner's decision. "With 14 points of capacity left and the Sprint exactly full, taking the login bug means moving UI-9, which isn't part of the Sprint Goal, to next Sprint, so I can say yes and say what was traded. The demo feature would leave us at 16 of 14 even after removing every non-goal item, so the reply is two options for the PO: keep the goal and do the demo next Sprint, or change the goal."

What’s happening
  1. The chapter's model removes not-started, non-goal work first, largest first, and never drops goal work by itself, because that's the decision the Guide reserves for the Product Owner.
  2. "Yes, and here's what moves" is a better answer than either "no" or a silent "sure": it keeps trust and keeps the plan honest.
  3. The written reply template: acknowledge the change, the impact if we switch now, the unconfirmed assumptions the new work depends on, two options with dates (including "the India team picks it up tomorrow morning IST"), a recommendation, and who decides by when.
  4. Pushing back on whether priorities may change is a losing position; the client owns priorities. What you own is making the cost visible.

Learn more: Shifting priorities and incomplete documentation and Working with a client's engineering team.

Your Daily Scrum takes 40 minutes at 08:00 in Austin, 19:30 in India, and has turned into status for the manager. What would you change?
Show answer

Go back to what the Daily Scrum is for: the Developers inspecting progress toward the Sprint Goal and planning the next day, in 15 minutes. "Status moves into short written updates posted before the call. The call starts by reading the Sprint Goal, then walks the board from right to left, closest to Done first, asking what each item needs to move one column today. Blocked and aged items get an owner on the spot, and anything longer than a minute leaves the call with the two people who need it. If a manager wants status, they get it from the board and a written summary. A 40-minute call starting at 19:30 IST ends after the India working day, so this is also about respect for their time."

What’s happening
  1. Eight people × four minutes is 32 minutes of individual status before any discussion; written posts remove most of it, and questions in them get answered in the call instead of tomorrow.
  2. The 2017 Guide's "three questions" were an example and were dropped in 2020; what's required is focus on the Sprint Goal and an actionable plan.
  3. Walking right to left puts finishing before starting and surfaces "it's waiting for a review from Austin" so a reviewer is assigned on the spot.
  4. Once people report upward, they stop asking each other for help, which is the opposite of what the event is for.
  5. Check it worked after two Sprints: call length, blockers resolved the same day, and what the team says in the retro.

Learn more: Running useful standups, refinement and retrospectives.

Your retrospectives produce the same actions every Sprint and nothing changes. What do you do?
Show answer

Fewer, smaller actions, each with one named owner and an observable change, put into the next Sprint Backlog, and start every retro by reviewing last time's. "'Improve communication', owned by 'the team', never happens. 'An end-of-day handoff note in the channel by 19:00 IST, owned by Dev 3' does. An action that keeps reappearing never got time in a Sprint, and the Guide explicitly allows improvements to go into the Sprint Backlog. For a distributed team I'd collect retro input in writing for two days first, so quieter people and people working in a second language contribute equally, and I'd keep managers and the client out of the room, because honest data needs psychological safety."

What’s happening
  1. The Guide's purpose for the Retrospective is to plan ways to increase quality and effectiveness; the most helpful changes are addressed as soon as possible.
  2. In the chapter's model, three retros produced eight actions and four were done; one action appeared three retros in a row; the two owned by "Team" were never done.
  3. Persistent themes (handoffs, CI) are where the lead invests outside the retro too: a handoff template, or a Sprint Goal that includes fixing CI flakiness.
  4. Norm Kerth's Prime Directive points the discussion at the system, not people; Derby and Larsen's five stages warn against skipping "gather data", or the loudest memory becomes the data.
  5. Psychological safety: Amy Edmondson's 1999 study, and the top factor in Google's Project Aristotle (re:Work, 2015).

Learn more: Running useful standups, refinement and retrospectives.

#Leadership and communication questions

Added

This group covers the lead role itself (what it owns, a workstream, the first 90 days, which code to write, balancing delivery, quality and people), leading a team split between the US and India, working with a client's engineering team and its stakeholders, and the behavioural round.

The mental model for this group is evidence, not adjectives. "I'm a collaborative leader" scores nothing; "I give each site whole pieces of work, and every handoff note ends with one decision request, a default and a deadline" can be written down and checked. For the "what would you do" cards, the spoken answer is a plan you can defend. For the "tell me about a time" cards, the answer here is deliberately only the structure: the story has to be yours. Find it with the story prompts, write it in My notes, and rehearse it until the shape is automatic. Nothing on these cards is anyone's real experience.

What does a tech lead do, in your view?
Show answer

A tech lead is responsible for the team delivering good software, not just for their own code, across four areas: technical direction, delivery, quality and people. "Direction is the design and standards, and making and recording the key decisions. Delivery is breaking work down, sequencing it, tracking risks and keeping stakeholders honestly informed. Quality is review standards, tests, CI gates and managing technical debt deliberately. People is onboarding, mentoring, delegating real ownership and feedback. I stay hands-on, usually on the riskiest or most cross-cutting pieces, but I measure myself by what the team ships and how well it works without me. In a new role I'd agree in writing what I own and what I escalate, because those boundaries differ a lot between companies."

What’s happening
  1. The mental model: you're measured by the team's output, not your own. A great week can be one where you wrote little code but three engineers shipped because you unblocked, reviewed and taught.
  2. Will Larson's Staff Engineer (2021) describes the Tech Lead as one of four senior technical archetypes (with Architect, Solver and Right Hand): guiding one team's approach and execution, partnering closely with its manager, scoping complex work, unblocking the team, keeping cross-team relationships working, defining its technical vision, and by default delegating the team's most complex projects rather than building them personally.
  3. A lead who only does direction is an architect; one who only does delivery is a project manager. The role is the overlap.
  4. A one-page role charter agreed with your manager early (scope, what you don't own, which decisions are yours, shared or escalated, a target week) prevents most later confusion.
  5. The trap to name: the super-IC lead who keeps the hardest tickets and rewrites others' code in review, and becomes the bottleneck for every decision.

Learn more: What a senior or lead engineer is expected to do.

How is a tech lead different from an engineering manager?
Show answer

The tech lead owns how the team builds; the engineering manager owns the people system. "The lead owns technical direction, design and review standards, the breakdown and sequencing of work, and technical risks. The manager owns hiring, performance, careers and team health. They overlap day to day: a lead often mentors and gives technical feedback, but usually doesn't decide promotions or ratings. I'd partner closely: I tell the manager what I see about each engineer's technical growth; they tell me about priorities and staffing. On US teams leading offshore or contract engineers, the lead often does part of the manager's job day to day, so I'd write the line down early."

What’s happening
  1. Lead: owns how the team builds, writes code a substantial part of the week, decides technical approach and task breakdown.
  2. Manager: owns the people, rarely writes code, decides staffing, promotions and process.
  3. Architect: cross-team technical direction in a domain. Product manager or owner: what gets built and why.
  4. Good example of the boundary: a client emails "Karthik seems slow lately". That's a performance concern about a person, for their manager, privately; the lead replies "thanks, I'll follow up internally" and routes it.

Learn more: What a senior or lead engineer is expected to do.

What does owning a workstream mean to you?
Show answer

I can answer at any moment what we're building and why, the plan and how we're tracking, the main risks, what's decided and what's open, and who's doing what, and those answers are written down where anyone can find them. "In practice: a one-page brief with an explicit out-of-scope list and dated milestones, a risk register I review weekly with an owner for every high risk, and a decision log. I don't do all the work; I make sure it's broken down, sequenced and unblocked, and I raise bad news early with a proposal. I also try not to be a single point of failure: answers in public channels, a second person per area who can decide while I'm asleep, and recurring questions turned into documentation."

What’s happening
  1. The mental model: the pilot in command, not the person flying every leg. Answerable for the flight, keeps the picture, makes the calls that matter, says early when the plan has to change.
  2. The brief starts with Why (two sentences), has an Out list (scope creep arrives as "while you're in there…"), and plans in milestones that are checkable states, not tasks.
  3. A risk without an owner, a mitigation and a date is a worry, not a managed risk. In the chapter's register, a score-15 risk with no owner and 21 days since review is how a known risk becomes a surprise.
  4. The critical path tells you where to put your best people: in the chapter's plan it ran through a firmware spike and a contract with the client, not through the biggest task (the UI), and two days' slip on the spike moved the end date by two days.
  5. On a team split across a 10.5- to 11.5-hour difference, a vague ticket costs a day, so owning the workstream includes making tickets ready before the sprint.

Learn more: Owning a workstream.

"Device groups: going well. API mostly done. UI in progress. Some concerns about firmware. Will update next week." What's wrong with this status update?
Show answer

Nearly everything a reader needs is missing: a status against the plan, dates, the specific risk, and what you need from whom. "I'd lead with a fixed status word against the milestone, 'AT RISK for 15 December', then what's done and what's next with dates and names, then the risk named with its consequence, 'without the firmware team's bulk-command API, bulk update slips to January', and exactly what would remove it: a 30-minute call with their lead by Friday. And end with the decision I need and my recommendation."

What’s happening
  1. "Going well" isn't a status. Use on track / at risk / off track (or green / amber / red with definitions agreed with the client in advance) against a named milestone.
  2. "Mostly done" and "in progress" don't say how far; milestones with dates do ("milestone 2 done, on time"; "UI by 8 Dec").
  3. "Some concerns about firmware" hides the most important line in the update. A stakeholder can act on a named risk, a consequence and a dated ask.
  4. Put the bottom line first (BLUF) and the status in the subject line, so a sponsor reading only the subject knows whether to open it.
  5. Silence is a status too: if nothing changed, send "no change, still green, next milestone Friday".

Learn more: Owning a workstream and Status updates and escalation.

What would your first 90 days look like in this role?
Show answer

Learn, then contribute, then lead, with the plan written down and shared in week one. "In the first month I'd mostly listen and learn: one-to-ones with every engineer, here and in India, the PM and the client's engineering contact, asking everyone the same questions, including what's working that I shouldn't break. I'd get the system running, deploy it myself, read the incident history and the busiest code, review PRs daily and ship a few small fixes. In the second month I'd own a real feature, fix one pain point the team named, and start a risk register and a weekly status. By the third month I'd set direction with the team: a roadmap, standards and growth conversations. And I'd adjust the pace to the situation: a team in trouble needs changes sooner."

What’s happening
  1. The source is Michael Watkins, The First 90 Days (2003, updated 2013): diagnose before acting, secure early wins, agree expectations explicitly. His STARS situations (start-up, turnaround, accelerated growth, realignment, sustaining success) change the right first moves.
  2. The same five questions for everyone let you compare answers; report the themes back, never who said what.
  3. The most common mistake is changing things in week two before knowing why they're like that. The exception is anything unsafe (secrets in the repo, no backups): fix it now and explain.
  4. On a contract: two sets of expectations (your employer's and the client's), the client's standards win by default, and access (repos, VPN, devices, CI) takes days, so list it on day one and chase it daily.
  5. A first-month Git habit: hotspots (files that change often and are large) and knowledge concentration per area show where to read first and where to pair people.

Learn more: The first 30, 60 and 90 days on a new team or contract.

Four of six engineers say CI takes 40 minutes and fails randomly. Your manager wants trunk-based development; the client wants a feature by quarter end. What's your first visible improvement?
Show answer

The CI pain: it's the team's own diagnosis, it helps everyone every day, it speeds up the client's feature, and it's a prerequisite for trunk-based development anyway. "I'd say: 'I'd like to start with CI. The team named it, it'll speed up the feature work, and it's what we need before moving to trunk-based development. I'll have a plan in two weeks.' That lines up the team's pain, the client's date and my manager's goal in one sentence."

What’s happening
  1. A win the team asked for earns trust; a better one they didn't ask for spends it.
  2. A 40-minute flaky pipeline costs every engineer hours a week and makes people batch changes, which makes reviews slower and PRs bigger.
  3. Trunk-based development means merging small changes several times a day, which isn't possible when every merge takes 40 minutes and fails randomly.
  4. The CI fixes themselves are in the CI chapter: measure the critical path, cache dependencies, shard E2E, quarantine flaky tests.

Learn more: The first 30, 60 and 90 days on a new team or contract and Keeping pipelines fast and reliable.

How do you balance coding with leading?
Show answer

I stay hands-on, but I'm deliberate about which code is mine: the work where my experience changes the outcome or that unblocks everyone, and nothing on the critical path while my days are full of interruptions. "I usually take the spikes that decide a design, the first instance of a new pattern so it becomes the template, and cross-cutting changes that need context from several teams. Each sprint I check my hands-on work against a time budget, often around a third of my week, and when it's over I hand something off with support rather than working evenings. I watch for knowledge concentrating in me or anyone else, and my own PRs are the example: small, tested, well described, and reviewed like everyone's."

What’s happening
  1. The mental model: you set the pattern; the team fills it in. Write the first endpoint or test of a new kind exemplary; hand over the next ten.
  2. The chapter's two questions per task: how costly is getting it wrong, and how much would doing it teach someone? High risk and low learning defaults to the lead; high risk and high learning means an engineer drives and the lead pairs.
  3. Added up, the per-task answers in the chapter's model came to 20 hours against a 12-hour budget, which forces the question every sprint: what am I holding on to that someone else should be learning?
  4. Typical follow-up: a two-day rate-limiting ticket on the critical path, which only you have done before, in a week with three days of client workshops. Pair: design it with the engineer on day one, let them build it, review closely, and tell the release owner the fallback.
  5. Don't rewrite people's code in review; point at the problem and the principle, and if you'd have designed it very differently, the design conversation should have happened earlier.

Learn more: Leading by example in code.

How do you balance shipping features with technical debt and quality?
Show answer

Treat debt like a loan: deliberate, recorded debt with a payback plan is a business decision; reckless debt isn't. Describe it as interest, and pay it down with a steady share of every sprint. "What I avoid is skipping tests to save two days in an area that keeps breaking. To make it a real decision I describe the interest a product owner can weigh: how much longer changes take in that area, which bugs came from it. I prefer a fixed budget, often 10 to 20% of each sprint agreed with the PO and spent mostly on code we're about to change anyway, over a refactoring sprint that never gets approved. Some things aren't negotiable at all: security basics, data integrity, legal requirements and a releasable main. And if a date needs repeated overtime, the plan is wrong."

What’s happening
  1. Ward Cunningham's metaphor (1992) and Martin Fowler's Technical Debt Quadrant (2009): deliberate or inadvertent, prudent or reckless. "Poll every 10 s for the pilot, ticket DEV-430 to switch to push before general release" is prudent and deliberate; "no time for tests, we'll add them when things calm down" is reckless.
  2. The mental model for stakeholders: interest, not mess. "Every change to device sync takes three days instead of one, and four of our last six bugs came from it" can be weighed against features; "the code is messy" can't.
  3. The chapter's toy model (made-up parameters, shape only): no paydown is ahead early, then drag climbs to 53%; 10–20% falls behind first and overtakes by sprints 13–14; 30% never catches up, because past a point extra quality work displaces features.
  4. Saying no is "yes, if": the cost and a choice ("we can do CSV export this sprint if the groups page moves").
  5. Brooks's law belongs here too: adding people late doesn't speed things up quickly; scope is usually the faster lever.

Learn more: Balancing delivery, quality and people.

How do you handle the time difference with a team in India?

Assume Austin works 08:00–17:00 and the India team a shifted 11:00–20:00 IST day.

Show answer

Make the numbers concrete, then design around them: the overlap is for what needs a conversation, and everything else is written so it can be picked up without one. "India is UTC+5:30 with no daylight saving; Austin is UTC−5 in summer and UTC−6 in winter, so India is 10.5 or 11.5 hours ahead, and with a shifted India day we share 90 minutes in summer and 30 in winter. The overlap is for the Daily Scrum, decisions, unblocking and live reviews. Everything else is async: tickets that stand on their own, handoff notes with a decision request, a default and a deadline, decisions written in the repository. I give each site whole pieces of work so handoffs happen at boundaries, start my day with the India questions, and rotate who takes the inconvenient slots."

rq-overlap.mjs
// Austin 08:00–17:00 and India 11:00–20:00 (IST): the shared window either side of the US clock change
// on 1 November 2026, computed from the time-zone database rather than a hard-coded offset.
const offsetMin = (zone, instant) => {
  const name = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "longOffset" })
    .formatToParts(instant).find((p) => p.type === "timeZoneName").value; // e.g. "GMT-05:00"
  const [, sign, h, m] = name.match(/GMT([+-])(\d\d):(\d\d)/);
  return (sign === "-" ? -1 : 1) * (Number(h) * 60 + Number(m));
};
const hhmm = (min) => `${String(Math.floor(min / 60)).padStart(2, "0")}:${String(min % 60).padStart(2, "0")}`;

for (const day of ["2026-10-30", "2026-11-02"]) {
  const noon = new Date(`${day}T12:00:00Z`);
  const austin = offsetMin("America/Chicago", noon), india = offsetMin("Asia/Kolkata", noon);
  // Both working days in UTC minutes after midnight.
  const aStart = 8 * 60 - austin, aEnd = 17 * 60 - austin;
  const iStart = 11 * 60 - india, iEnd = 20 * 60 - india;
  const start = Math.max(aStart, iStart), end = Math.min(aEnd, iEnd);
  console.log(`${day}  India ${hhmm(india - austin)} ahead  overlap ${hhmm(start + austin)}–${hhmm(end + austin)} Austin` +
    ` = ${hhmm(start + india)}–${hhmm(end + india)} IST  (${end - start} min)`);
}
Result
2026-10-30  India 10:30 ahead  overlap 08:00–09:30 Austin = 18:30–20:00 IST  (90 min)
2026-11-02  India 11:30 ahead  overlap 08:00–08:30 Austin = 19:30–20:00 IST  (30 min)
What’s happening
  1. offsetMin asks Intl for each zone's offset at a specific instant (longOffset gives GMT-05:00 or GMT+05:30), because Chicago's offset depends on the date and India's doesn't. Node 26 here ships tz data 2026a.
  2. Each working day is converted to UTC minutes (08:00 − offset), and the overlap is the later start to the earlier end. On Friday 30 October, Austin is UTC−5: the shared window is 08:00–09:30 Austin, 18:30–20:00 IST, 90 minutes.
  3. On Monday 2 November, after the US change on 1 November, Austin is UTC−6: the same hours give 08:00–08:30 Austin, 19:30–20:00 IST, 30 minutes. Nobody's hours changed; only the US clocks did.
  4. With a typical India office day (09:30–18:30 IST) there's no overlap in any season, which is why US-facing India teams often shift their day. Small, deliberate shifts on both sides (Austin at 07:30) are how teams buy overlap.
  5. The follow-ups this prepares you for: who absorbs the clock change (whoever created the recurring meeting in their zone keeps their time; the other side moves), why a blocking question asked at 12:00 IST costs most of the India day and one asked on Friday evening costs the weekend, and why you write "08:00 CT / 19:30 IST" (both sides' times, and "CT" in every season) rather than a bare "8:00 CST", which is wrong for Austin from March to November and is also China Standard Time.

Learn more: Working across US and India time zones.

Why not run "follow-the-sun" development, so work progresses around the clock?
Show answer

Because in development every handoff costs context, and the research found distributed work takes longer, not shorter. "Herbsleb and Mockus's 2003 study found distributed work items appeared to take about two and a half times as long as similar single-site items, and tied the delay to more people being involved. So for a product team I'd do the opposite: give each site whole pieces it can finish, a feature, a component or an API end to end, and hand over at boundaries like contracts and PRs, not mid-task. Follow-the-sun does work for queue-shaped work in small self-contained units: support tickets, incident response with a good runbook, monitoring."

What’s happening
  1. The receiver of a mid-task handoff has to rebuild what the sender knew, which often costs more than the hours gained.
  2. The practical conclusion usually drawn from the study (the chapter is explicit that this is its reading) is to reduce the need for cross-site communication by dividing work so each site works independently.
  3. The relay-race model from the chapter: the overlap is the exchange zone; everything outside it is a baton left where the next runner will find it.
  4. Incidents are the exception that proves the rule: when one runs past the US day, hand it to a named person in India with a written note, not "the team will pick it up".

Learn more: Working across US and India time zones.

How do you make async communication work across an 11-hour gap?
Show answer

Treat writing as the interface between the sites and hold it to the standard of an API: complete enough that the reader can act without calling the author. "Tickets have context, checklist acceptance criteria, out of scope, how to test, and a questions section naming who answers in the overlap and a default assumption if they're blocked, so a question costs minutes instead of a day. Anything bigger gets a one-page spec reviewed in writing by both sites. Decisions made in calls aren't decisions until they're in the decision log. Short recorded demos replace live walkthroughs. And I watch for the side effect the research describes, groups becoming siloed, by deliberately pairing people across sites."

What’s happening
  1. The mental model: an API contract between people. "Fix the volume thing as discussed" is an undocumented endpoint that only works for the one person who was in the room.
  2. The default-if-blocked line is the single most valuable habit: it turns a blocking question into a non-blocking one.
  3. Yang et al. (Nature Human Behaviour, 2021, 61,182 Microsoft employees) found collaboration became more siloed and more asynchronous when work went remote.
  4. Channels by need: the record goes in docs, tickets and ADRs; questions about one piece of work on the ticket or PR; blocking questions in the team channel with a default; feedback and disagreement on a call, then written up.
  5. Decisions in direct messages split a team into inner and outer circles. Model the fix: move a DM'd technical question to the public channel and answer it there.

Learn more: Async communication that works.

At 16:30 Austin time you need to tell an offline India developer their PR needs changes. Your draft says "Hey, can we chat about your PR tomorrow?" What's wrong with it?
Show answer

It carries no information and wastes their whole morning: they read it at 11:00 IST, can't act on it, and nobody in Austin is awake until the overlap. Send the content, not a meeting request. "Something like: 'Thanks for this. One change before merge: please move the range check into the API handler, because the API is the source of truth (ADR-014); the UI may also check but mustn't be the only check. The rest looks good. If you'd rather keep it in the UI for a reason I'm missing, say so in the PR and we'll talk at 08:00 CT / 19:30 IST.' They can do the work in their morning and have it ready when Austin wakes up."

What’s happening
  1. "Can we chat?" also creates worry, and the developer will probably stop work on the PR until the call.
  2. The better message says what to change, why (with a link), what's fine, how to disagree, and when a conversation can happen if needed.
  3. The rule: never send a message whose only content is "let's talk". Even for sensitive feedback that needs a call, say what it's about and when.
  4. Both times in the message, with the zone, prevent the "my Thursday or yours?" mistake.

Learn more: Async communication that works.

How do you onboard and mentor engineers in India when you're in the US?
Show answer

Design onboarding instead of letting it happen, because the default outcome for a remote joiner is isolation. "Before day one, access is requested and a small, real first ticket is chosen. In week one the joiner gets the system running from the README, fixing any gaps, merges a first PR, and learns our working agreement: the overlap hours, how to ask a blocking question, and the default-if-blocked rule. They get a local buddy in India and short pairing sessions with me in the overlap. Over 30 days they own one area and review others' PRs; by 60 they own a feature from spec to release and demo it themselves; by 90 they lead a design discussion and we agree a growth plan. Check-ins at 30 and 90 days are booked on day one."

What’s happening
  1. The mental model: scaffolding on a building. Lots at the start, taken down on purpose as the structure becomes self-supporting; leaving it up forever is micromanagement, taking it down too early is how people fall.
  2. Reviewing both sites' PRs from the first month makes review a two-way street rather than "Austin approves India".
  3. Teach the reasoning, not just the fix: "the API is the source of truth because other clients call it too (ADR-014)" travels to the next decision; "move this to the API layer" doesn't.
  4. A growth plan has capabilities as goals, a real opportunity attached to each, support, evidence, and the lead's own commitments.
  5. The trap: if India only gets bug fixes, the good engineers leave and knowledge concentrates in one site, which makes the time difference hurt more.

Learn more: Onboarding and mentoring offshore engineers.

Three weeks in, your new India-based engineer has merged one PR and asks almost no questions. What do you do?
Show answer

Treat the silence as a signal, not as "fine": make asking cheap and explicit, look for blockers you can't see, and add scaffolding for a while, without making it feel like a performance problem. "I'd have a one-to-one in the overlap with specific questions: 'what's the slowest part of your day?', 'what did you try on the simulator setup?', 'what would you need from me to finish UI-207 this week?'. Specific questions get specific answers; 'any problems?' gets 'no'. I'd check for missing access, a simulator that won't run on their machine, a ticket waiting on a client answer. Then a smaller next ticket, a daily 15-minute slot with the buddy, a rule that a blocker unresolved after an hour goes in the channel with its default, and a look again in two weeks."

What’s happening
  1. Silence often means not wanting to look slow, or not knowing that asking is expected. Don't read it as competence or as laziness.
  2. Three weeks with one PR often means an invisible blocker rather than a skills problem.
  3. Make asking visible and normal: ask your own questions in the public channel, and thank people publicly for raising blockers.
  4. The same idea applies to the rest of the team: "any questions?" gets silence; "can you play back what you'll build first and how you'll test it?" tells you what was understood.

Learn more: Onboarding and mentoring offshore engineers and Cultural awareness and giving feedback across cultures.

How do you delegate without micromanaging, especially across time zones?
Show answer

Delegate outcomes and decisions, not just tasks, and make the level of delegation explicit per decision. "Across eleven hours, every decision that waits for me costs the India team a day. So on a delegation board, how to implement a ticket is fully the developer's call within our standards; a new dependency is 'you decide after asking my view'; a public API change needs agreement; a production hotfix outside Austin hours is 'follow the runbook and tell me'. For bigger pieces I write a short brief: outcome, why, constraints, decision rights, resources, scheduled check-ins and when to escalate. The guardrails are the standards and CI, not me approving every approach, and as people show judgement, decisions move up a level."

What’s happening
  1. The mental model: guardrails on a mountain road. You don't steer from the passenger seat; you build the guardrails and let them drive, with stronger rails and more check-ins on the dangerous stretches.
  2. The vocabulary is Jurgen Appelo's seven levels from Management 3.0 (tell, sell, consult, agree, advise, inquire, delegate). Levels go down as decisions get harder to reverse.
  3. Some levels are set by roles, not trust: accepting a mid-Sprint request belongs to the Product Owner.
  4. Reverse delegation (Oncken and Wass, HBR, 1974): a developer brings a problem, the lead says "let me look into it", and now the lead owns it. Ask "what do you recommend?" instead.
  5. "Just ping me if you have questions" delegates the risk and keeps the decision; give decision rights and a default instead.

Learn more: Delegation and trust without micromanaging.

How do you give feedback to engineers from a different culture?
Show answer

The same structure everywhere, situation, behaviour, impact, then ask for their view and agree one concrete change, delivered privately and live for anything corrective. "SBI is specific about the work and respectful of the person, which lands well whether someone's used to direct or indirect feedback. Corrective feedback is one-to-one, on video, in the overlap, then summarised in writing so nothing is ambiguous; praise can be public. I know frameworks like Erin Meyer's Culture Map, where the US is very low-context and India high-context, but I use them to check my own assumptions, not to predict individuals. In practice I don't treat silence as agreement, I ask for options or a playback, I collect views in writing before meetings, and I write requirements literally: 'please change', not 'maybe consider'."

What’s happening
  1. SBI comes from the Center for Creative Leadership. Situation anchors it to one event, Behaviour is observable ("the endpoint returns 500 for an unknown id"), Impact connects it to something that matters.
  2. Meyer (The Culture Map, 2014) stresses that positions are relative and are midpoints; individuals vary widely, and India is enormously diverse.
  3. Words that differ: "I'll revert" (I'll reply), "I have a doubt" (a question), "prepone"; and from the US side, "let's table this", "ballpark", and "maybe consider…" used as a polite requirement.
  4. The red flag interviewers listen for: generalisations like "Indian engineers never say no". Talk about what you do and about individuals, not nationalities.
  5. Making disagreement safe: ask for two options and a pick, ask what would make it fail, go round juniors first, and visibly reward the first person who disagrees.

Learn more: Cultural awareness and giving feedback across cultures.

You asked an India-based developer on Tuesday whether the API would be ready for Thursday's demo. They said "Yes, it will be done." It isn't. What happened, and what would you do differently?
Show answer

"Yes" may have meant "I understand and I'll try my best", and the question gave no safe way to say "it's at risk". Next time, ask questions that produce information, and make the work visible so you don't depend on the answer. "A closed question from a senior person invites 'yes' anywhere, more so where contradicting the lead is uncomfortable. I'd ask 'what's left to do, and what could stop it being ready by Thursday?' or 'how confident are you, one to five?', look at the board and the draft PR myself, and agree up front what happens if it's at risk: 'tell me by Wednesday 19:00 IST and we'll demo the UI against the mock'. And I wouldn't conclude they're unreliable; the process failed to surface the risk."

What’s happening
  1. The answer told you about the relationship, not about the work.
  2. Open questions about what's left and what could go wrong make risk normal to mention.
  3. Visible state (board, draft PR, handoff note) would have shown on Tuesday that the contract tests didn't exist yet.
  4. Thank people when they raise risks early, so it becomes the expected, safe move.

Learn more: Cultural awareness and giving feedback across cultures.

Defects are higher in an area mostly built in India. A manager suggests a mandatory Austin review for every India PR. What do you say?
Show answer

Don't add a site-based gate; investigate the area, not the site, and fix the cause with shared mechanisms. "A site gate would slow every India PR by up to a day, signal distrust, and miss the cause if it's something else. I'd check the area's tests (does device-settings have contract tests against each firmware version?), its inputs (is the device's settings API documented, or are people building on assumptions?), and its review flow (who reviews it, with what expertise?). Then: contract tests in that area's definition of done as a required check, an owner with device expertise on each site in CODEOWNERS, and written device-API notes, and measure escaped defects again in two months."

What’s happening
  1. The mental model: one recipe, two kitchens. The written recipe is the definition of done and the standards; the equipment is CI; tasting from both kitchens is two-way review.
  2. In the chapter's model, Austin-only review took an India PR from opening to the author acting on the review in 19–23 hours on a Tuesday and 68 over a weekend; owners on both sites made every case about an hour.
  3. Reviews go both ways, including India engineers reviewing the lead's PRs; one-way review is also a quiet statement of hierarchy that good engineers notice.
  4. Measure the system, not the sites: site league tables mostly measure who reviews whom.
  5. CI can enforce the habit that matters across time zones: a required check that every PR description has "what and why", "how I tested it" and "risks and rollback", with the body passed through an environment variable, never pasted into the script.

Learn more: Keeping quality consistent across locations.

When does a change need a design doc, and what goes in it?
Show answer

When it changes a contract others rely on, needs more than one team, has at least two credible designs with real consequences, touches security, migration or firmware compatibility, or spans several people or sprints. "The doc has the problem with evidence, goals and non-goals, the proposed API with request and response examples including errors, two or three real alternatives and why they lose, compatibility for old clients and firmware, testing and rollout, and open questions with owners and dates. I share it with a comment deadline so people in India and the US review in their own hours, there's one named approver, and decisions go into a log in the doc. For small, reversible changes a good PR description is enough."

What’s happening
  1. The mental model: the design doc is the code review you hold before the code. A wrong API shape caught in a doc costs a comment; caught after two sprints in two time zones, a rewrite and a partner migration.
  2. Malte Ubl's Design Docs at Google (2020): 10–20 pages for a large project, 1–3 for a mini doc, and don't write one when the solution is obvious.
  3. Non-goals save more review time than any other section; "Alternatives considered" is the section reviewers trust you by, so give each alternative its real strength.
  4. Size the doc to the cost of being wrong and the number of people outside the team who depend on it: adding one field to a public response gets a mini doc; replacing polling with a WebSocket stream from the device gets a full one.
  5. Reviewing a junior's first doc: ask for one page (problem, two options, a preference), review it in 20 minutes in the overlap, then let them expand it, and put their name on it when it goes to the client.

Learn more: Writing a design document.

A deadline is going to slip. What do you do?
Show answer

Understand it, then tell the people who committed to the date the same day, before it shows up in a group update, with options and a recommendation. "First: how much, why, and what options we have, cutting scope, adding a person, moving a dependency. Then my manager and the client's PM hear it directly, with my recommendation. In the written update the item goes amber or red by the rules we agreed at the start, with the new forecast next to the original date. What I don't do is keep it green while hoping we'll catch up: a two-day slip reported early is a planning detail, a two-week slip reported late is a trust problem."

What’s happening
  1. The single rule with stakeholders is no surprises: they can handle late; they can't handle late and unannounced.
  2. Agree what green, amber and red mean before anything goes wrong, or every lead reports green until it's obviously red: the watermelon.
  3. Escalate to the lowest level that can make the decision, with facts, impact in the reader's terms, options including "do nothing", one ask and a date, and tell the person you're going over first.
  4. Escalating by adding someone's manager to the CC line is an escalation disguised as an update; write to the manager directly.
  5. During an incident, update on a clock ("update 3, 09:00 EDT / 18:30 IST"), say what you don't know yet, and keep the next-update promise even when nothing changed.

Learn more: Status updates and escalation.

It's Wednesday. The Telnet API work is still forecast for its committed date, but the platform team has owed you an answer since last Thursday and you've chased once. Amber after 2 working days blocked, red after 4. What's the status?
Show answer

Red, and you escalate today. "Thursday, Friday, Monday, Tuesday: four working days blocked on someone outside the team. The forecast date hasn't moved, but a four-day external blocker makes that forecast a hope; the rule makes the colour follow the cause, not the optimism. I've already chased the engineer once, so the next step is their lead, with the question, since when, the impact on the date and the options, such as building against the commands both OS versions support. I'd tell the engineer I'm raising it and why, then report it red in the weekly update with the ask."

What’s happening
  1. Count working days, skipping the weekend: the blocker is four working days old by Wednesday, which meets the red threshold.
  2. The blocker's age is the leading indicator; the date is the lagging one. An on-time forecast with an old external blocker is the classic watermelon.
  3. The thresholds are an example of what a team agrees with its client, not a standard; what matters is that they're agreed in advance, so a colour change is a fact rather than a confession.
  4. Escalating at amber for an external cause, and always at red, is how you raise it while there's still time for someone to fix it.

Learn more: Status updates and escalation.

How would you handle disagreeing with the client's architect on a design?
Show answer

Understand their reasons privately first, bring evidence and an option that addresses their constraint, let the owner decide, then commit fully. "There's usually a constraint I can't see: a release date, a partner contract, a support cost. I'd restate their position until they agree I've got it, then bring data, a failing test or a small prototype, and propose a cheap way to find out if it's uncertain. If they still choose their way and they own that part of the platform, it's their call: I'd build it properly, back it to my team, write down the decision with what would make us revisit it, and look for ways to reduce the risk I was worried about inside their decision. What I wouldn't do is go around them or reargue it in code review."

What’s happening
  1. The mental model for a client engagement: you're a guest with keys. You act like an owner (flag risks, hold a quality bar, push back), but it's their house, and they make the final call on what they'll live with.
  2. Amazon's "Have Backbone; Disagree and Commit" names both phases; interviewers for lead roles listen for both.
  3. A working agreement written in the first weeks says who owns what (the device API contract may be the client architect's even if your team builds much of it), how changes are agreed, and how disagreements are settled, so disagree-and-commit feels like following agreed rules.
  4. The written summary afterwards: thank them and restate their constraint, your concern once and factually, the decision attributed to its owner, follow-ups that shrink the risk, a measurable revisit trigger, and the commitment said out loud.
  5. Watch your own team's language: "their platform team is blocking us again" travels and shapes how your engineers treat the client's.

Learn more: Working with a client's engineering team.

A client director asks your team to switch to a partner demo mid-sprint, and a client manager has started asking one of your India developers directly for "quick changes". How do you handle both?
Show answer

Say yes to the conversation and make the trade visible, confirm the change in writing with whoever owns priorities, and give your engineers a simple rule for direct requests. "To the director: 'Understood, the demo is the priority. If we move two people onto it now, the firmware upload page moves from the 24th to about the 2nd and the settings work stays on track. Is that the trade you want?' Then I confirm it with the client PM, update the shared roadmap, and explain the why to the engineers being moved. For direct requests, direct contact is good, but anything that changes what's being built goes through the ticket and the person who owns priorities, and my engineers can always say 'happy to; can you add it to the board?', knowing I'll back them."

What’s happening
  1. Pushing back on whether priorities may change is a losing position; the client owns priorities. You own making the cost visible and the decision explicit.
  2. A team that hears "drop that, do this" without a reason twice a month stops caring about the plan.
  3. If changes become constant, raise it in a retrospective with the client: maybe the sprint is too long, or the team needs a reserved interrupt lane.
  4. Never promise on someone else's behalf ("the platform team will have that by Friday"); say what you'll do and when you'll know their date.
  5. This is the client-facing half of the Scrum rule: mid-Sprint changes go through the Product Owner, onto the board, and into a written note the same day.

Learn more: Working with a client's engineering team and Shifting priorities and incomplete documentation.

The client's platform is thinly documented, and the person who knows how a Telnet command behaves is on their team. How do you get answers without becoming a bottleneck?
Show answer

Ask one specific question at a time, in writing, with what you've checked, your current assumption and when you need it, and teach the team to ask directly the same way. "'When DHCP is switched off with no static IP set, does 4.x reject the save or keep the old address?' can be answered in a line; 'how does the network page work?' can't. Saying what I've checked respects the expert's time and often reveals the answer. My current assumption makes silence productive: the team keeps moving on it, and the expert can correct it in one line. 'Needed by, because…' lets them prioritise honestly, and it's the date I'd use if I had to escalate. And my India engineers ask the client directly, because a lead who relays every question becomes the bottleneck."

What’s happening
  1. Keep the questions in one maintained log, reviewed in a weekly call, with a default if there's no answer; ten scattered chat messages get two answers.
  2. Record assumptions next to the code with evidence, risk if wrong, owner and confirm-by date, so pencil marks don't get treated as ink.
  3. Observe and pin real behaviour: record requests and responses per firmware version and turn them into contract or golden-master tests.
  4. Feed corrections back to the client's team; being precise about their system builds trust.

Learn more: Working with a client's engineering team and Shifting priorities and incomplete documentation.

How do you explain a technical trade-off to a non-engineer, such as a sponsor or a product manager?
Show answer

As a menu with prices: the decision and its deadline first, two or three options described by their consequences, my recommendation in one sentence, and one ask. "I translate rather than simplify: same substance, their units, which are outcomes, dates, costs and risks. For example: 'We need a decision by Friday on the settings page for launch. We can rebuild everything and slip about three weeks, or launch on time with the rarely used settings on the old-style page and finish them in the first update. I recommend launching on time; the cost is a mixed look for a few weeks. Are you comfortable with that?' Ranges, not false precision, and I check understanding by asking what they'd tell their boss."

What’s happening
  1. One option is an announcement; five is a burden. Leave the mechanism (the kitchen) out unless they ask.
  2. Make risk concrete with likelihood, impact and what you're doing about it; three or four rows, and say which one you'd lose sleep over.
  3. Analogies help when they carry one property: technical debt as a loan, where the interest is the point. Say where the analogy stops.
  4. Some items aren't options (Telnet enabled from any network by default, skipping a security fix to hit a date); say that plainly, separately from the menu.
  5. Presenting three options neutrally can feel like handing them homework; unless it's purely a business value judgement, say which you'd pick.

Learn more: Explaining technical trade-offs to non-engineers.

How do you run a technical discussion where the team disagrees?
Show answer

Agree the question and who decides before arguing about answers, give everyone a way to be heard, find the criterion you actually disagree on, and end with a written decision. "Usually one approver, the person who owns that area. I send a short pre-read with the options a day ahead, start with silent reading, and ask the quieter and more junior people first. If we're split, a quick weighted comparison often shows it's one assumption, like whether we need two-way messaging, and that becomes a question with an owner. I match the process to how reversible the decision is. And I end by saying the decision, the reasons and any dissent out loud, then write it down the same day."

What’s happening
  1. The mental model: a meeting is a synchronous API call: it needs inputs (a pre-read), a defined output (a decision or dated next step) and a timeout. If it can be done asynchronously, it usually should be.
  2. DACI (Atlassian's Team Playbook): a Driver, one Approver, Contributors who have a voice but not a vote, and the Informed.
  3. In the chapter's decision matrix, the web team's weights picked SSE by one point (40 to 39) and the platform team's picked WebSocket; raising only the "two-way" weight flipped the result. The teams disagreed about one requirement, not about protocols.
  4. Two-way doors can be decided by "lazy consensus": a written proposal with a deadline in both time zones, where silence means agreement. Contracts still need the owner's explicit yes.
  5. Traps: consensus as a requirement (a veto for the most stubborn person), the highest-paid opinion going first, and reopening settled decisions without new evidence.

Learn more: Running technical discussions and making decisions.

One-way or two-way door? (a) a charting library for the diagnostics page; (b) field names in a new public REST endpoint; (c) the order of settings tabs; (d) whether the device accepts Telnet from any network by default.
Show answer

(a) two-way, light process; (b) one-way: design doc and the contract owner approves; (c) two-way: decide, ship, watch; (d) one-way and a security decision: design review, security sign-off, owner approves. "The test for each: if we're wrong, how expensive is it to undo, and how many people outside the team pay? A library inside the web UI can be undone and affects only us, so a short ADR is enough. Public field names become a contract the moment partners script against them and firmware ships with them. Tab order is reversible next release. And a Telnet default ships on every device, sends credentials in clear text, and is very hard to take back from a fleet in the field."

What’s happening
  1. The terms come from Jeff Bezos's shareholder letters: hard-to-reverse "Type 1" decisions and reversible "two-way doors" that deserve a light, fast process.
  2. The 2016 letter also suggests most decisions should be made with around 70% of the information you wish you had.
  3. The mistake is using one weight of process for everything: the heavy one for everything is slow; the light one for contracts is dangerous.
  4. Undoing (a) isn't free (every chart gets rewritten), which is why it still gets a short written reason and one reviewer.

Learn more: Running technical discussions and making decisions and Shifting priorities and incomplete documentation.

How are behavioural interviews scored, and what does that mean for how you answer?
Show answer

Against a rubric, from the interviewer's written notes, competency by competency, so anything you don't say didn't happen. "In a structured interview each interviewer has a few competencies, asks prepared questions with prepared follow-ups, writes down what I say and rates the evidence afterwards, usually before hearing anyone else's view. So I make the notes easy to write: one specific time, 'I' for my actions, the options I saw and why I chose one, a result with a number, and what I learned. For a lead role, at least half my stories should be lead-sized: other people did most of the building, and my actions were deciding, unblocking, reviewing, teaching and communicating."

What’s happening
  1. The mental model: the interviewer is a court reporter, not a judge of character. "We improved the pipeline" gives them nothing to write under "ownership".
  2. Google's re:Work guide to structured interviewing (updated March 2026) describes the same vetted questions for every candidate, follow-ups and rubrics; Sackett et al. (2022) ranked structured interviews as the top selection procedure (the chapter notes only the abstract was checked).
  3. Signals: ownership, judgement, impact, scope, collaboration and conflict, growing others, self-awareness, communication. "Borderline" is usually missing evidence, not bad behaviour.
  4. Answering "tell me about a time" with "I would…" scores as poor: there's no evidence in a hypothetical.
  5. Interviewers compare notes in the debrief, so use the same stories and the same numbers with everyone, and don't share confidential details.

Learn more: How behavioural interviews are scored.

"Tell me about a time you led a team across time zones." How do you structure the answer?
Show answer

One specific time, told as STAR with a learning, in about two minutes, with "I" for your actions; the story itself has to be yours, from your story bank. "I'd open with a one-line headline of what I did and the result, give about 30 seconds of situation and task (team, sites, the problem and why it mattered), spend about half the answer on three to five actions, each with a 'because', end with a result that has a number and says how I know it, and one thing I learned or kept doing. Then stop and let the interviewer steer with follow-ups." The card can't give you the story: find it with the story prompts for requirement 8, mentoring and distributed teams, and write it in My notes.

What’s happening
  1. What the interviewer listens for on this one: how you used the overlap, how handoffs worked, async habits and written clarity, and what you changed in your own working hours. Those are the action steps to have ready.
  2. The mental model: a camera that zooms in on the action: a short establishing shot, a slow close-up on what you did, then pull back for the result.
  3. Prepare three levels deep on the key decision (what you did, why, what you knew at the time), and have a 30-second version with just the headline, one or two actions and the result.
  4. Check proportions on a written draft with the chapter's star-check.mjs model: set-up 20–30%, action about half, more "I" than "we", a number in the result.
  5. If you haven't led across time zones, say so and give the closest real example; an honest adjacent story scores far above an invented one, and invented ones rarely survive the third follow-up.

Learn more: The STAR method and its variants, Story prompts: build your own stories and your own stories in My notes.

"Tell me about a time priorities changed suddenly, or you had to deliver with incomplete documentation." What's the shape of a strong answer, and what's the common mistake?
Show answer

The shape is one real time where you made the ambiguity explicit: what you wrote down and confirmed, the trade-off you made visible, and what you left behind for the next person. The common mistake is a generalised answer about what you "usually do". "'Documentation is always out of date; what I generally do is read the code and talk to people' describes good habits and scores nothing, because there's no situation, no action that happened and no result. The fix is to anchor those habits in one story: the specific feature, what the only specification was, what I did to find out and confirm the behaviour, the result, and the document or tests that existed afterwards." Find the story with the prompts for requirement 10, ambiguity and shifting priorities.

What’s happening
  1. What interviewers listen for on these: making the trade explicit, communicating why, protecting focus; assumptions written down and confirmed; small increments; a method for learning a system (code, tests, people, experiments) and leaving docs behind.
  2. Identify the competency before choosing the story: "the only specification was how the old version behaved" is mainly incomplete documentation, with legacy migration close behind.
  3. Likely follow-ups to prepare: "how did you decide what to stop doing?", "what did you write down for the next person?", "how did you know your assumptions were right?".
  4. The technical substance behind the story (assumption logs, defaults with dates, one-way and two-way doors, priority changes through the Product Owner) is in the delivery chapter; the story is what proves you've done it.

Learn more: Questions for a lead full stack role, Story prompts: build your own stories and Shifting priorities and incomplete documentation.

"Tell me about a disagreement with a client or another team." What must the answer show?
Show answer

Both halves of disagree-and-commit: that you understood their side and disagreed with evidence, and what happened to the decision and the relationship afterwards. "The structure: the decision and why it mattered in a sentence or two; their position restated in a way they'd agree with; what I did, privately first, with evidence or a cheap way to find out; who decided, and what I did after the decision, including how I reduced the risk I'd worried about; the result, including the working relationship; and what I'd do differently." The story has to be a real one of yours; the prompts for requirement 9, client and stakeholder communication, are the place to look for it.

What’s happening
  1. Conflict stories are scored on giving the other side a fair hearing; "they were wrong" is a red flag, and winning the argument while losing the colleague isn't a good outcome for a lead.
  2. The rubric's "outstanding" isn't "won": it's understanding the other side, bringing evidence, and either improving the outcome or committing to theirs and lowering its risk.
  3. Failure and feedback stories have their own emphasis: failure is scored mainly on the learning, with evidence that it stuck; feedback needs an honest reaction ("at first I was defensive…") rather than "I welcomed it immediately".
  4. Keep at least two stories in the bank about something that went wrong and was yours, or you'll improvise the hard questions.
  5. Anonymise: "a large device manufacturer", roles rather than names, relative numbers when the absolute ones are internal.

Learn more: The STAR method and its variants, Story prompts: build your own stories and Working with a client's engineering team.

The role asks for experience with MCP servers, and you haven't used them in a job. What do you say?
Show answer

Exactly what's true, in three parts: the adjacent thing you have done, what you understand about the gap, and how you'd close it in the first weeks. "A shape: 'I haven't used MCP in a job yet. What I do use daily is [your real assistant workflow], and here's how I verify its output. I understand MCP as an open protocol that lets an AI host connect to tool and data servers through a standard interface' (and only if you've built one: 'I've built a small server that…'). 'In the first weeks I'd…'. Interviewers hire people who learn quickly and tell the truth, and reject people caught overstating."

What’s happening
  1. The brackets are yours to fill from real experience; the behavioural chapter is explicit that it doesn't supply the example.
  2. Anything you build while studying is a legitimate, honest example, as long as you say it was a study project.
  3. The same rule covers any requirement you haven't done: give the closest real example rather than a hypothetical or an invented one.
  4. Personal projects count when they're real evidence for the competency asked about, said plainly as personal projects; they're weaker evidence for leading people, so pair them with work stories there.
  5. The question bank lists the AI questions to expect: how you use assistants day to day, a time one was wrong and how you caught it, how you'd introduce Copilot to a team, and whether you've built or used an MCP server.

Learn more: Questions for a lead full stack role, Reviewing AI-generated code, and MCP itself from zero on the AI-Assisted Dev site: What MCP is and the problem it solves.

What would you ask the interviewer at the end?
Show answer

Three to five specific, open questions tailored to who they are, ideally one that follows up on something they said earlier. "With a hiring manager: 'What would make you say in six months that this hire was a great decision? And what's the hardest part of getting there?' Then something from the conversation: 'You mentioned the Telnet API has partners on old firmware; how does the team decide how long to support an old version?' And: 'How do the US and India teams split the work today, and what would you change?' Then I listen and take notes, because the answers are data about the job."

What’s happening
  1. The mental model: you're interviewing the project. Imagine it's your first week and ask what you'd most want to know about the workstreams, the team, the client relationship and how success is judged.
  2. Tailor it: recruiters for process and practicalities; the hiring manager for success criteria and challenges; peer engineers for a normal week and what's painful; client stakeholders for what they need from the workstreams.
  3. Avoid what's on the website, "what does the company do?", yes/no questions ("do you use Agile?"), and salary in a technical round.
  4. Listen for patterns ("the India team mostly takes tickets", "the documentation is pretty thin") and ask a follow-up before concluding anything.
  5. "No, I think you've covered everything" reads as low interest; always have two prepared per interviewer.

Learn more: Questions to ask the interviewer.

Written fresh for a lead full stack role. Git examples were run; GitHub Actions workflows were checked with actionlint. Behavioural stories are yours to write in My notes.

RohitDownloads · All chapters

Sync progress across devices

Type the same private phrase on your Mac and your phone, and your Learned ticks follow you between them — on every study site.

The phrase never leaves this device: only a fingerprint of it is sent, and the server stores a fingerprint of that. Anyone who knows the phrase could see or change your ticks, so pick something you don’t use elsewhere.

Sync is on in this browser.

Sync ID

This ID must be the same on every device. If another device shows a different one, its phrase is different (capital letters count): tap “Turn off here” on it and type the phrase again exactly.