#Code review and standards questions
AddedThis 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).
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."
- 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.
- 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.
- 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.
- 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.
- 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.
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."
- 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.
- 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.
- "Read the tests first" is a strong concrete detail: missing cases jump out of the tests faster than out of the implementation.
- "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. - 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.
The admin UI needs a paged, searchable device list. The PR adds this function and one test.
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."
- Security first: a search box is user input. Pasted inside
LIKE '%…%', a quote ends the string and the rest becomes SQL.siteIdis interpolated too. The fix returns{ text, values }for a parameterised query and escapes%,_and\inside the search value so50%is literal. - Correctness:
listDevicesQuery({ siteId: 7 })producesOFFSET 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. - Ordering and limits: two rooms called "Huddle 2" can swap between pages, so order by
name, id. Clampsize(say 1–100) so one request can't ask for a whole big customer's table. - 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, nottext; size is capped. - 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.
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."
- 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.
- Conventional Comments (conventionalcomments.org) is
label (decorations): subject, with labelspraise,nitpick,suggestion,issue,todo,question,thought,chore,noteand decorations(blocking),(non-blocking),(if-minor). Google's guide gets the same effect withNit:,Optional:andFYI:prefixes. - Which labels block by default is a team choice, so write it down; "issue" should mean the same in Bengaluru and Austin.
- 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.
- 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.
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."
- "Why are you…" puts the author on the defensive; they'll explain their reasoning instead of fixing the code.
- "Bad practice" is an appeal to authority with no content. The content is the consequence: a
200with an empty list during an outage, and no log line. That's what makes it blocking. - 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.
- The explicit
issue (blocking)label matters most for someone junior or working in a second language, who otherwise has to guess severity from tone. - 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.
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."
- The general rule from the chapter: block on correctness, security, design and agreed standards; suggest on preference; automate formatting.
- 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.
- 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.
- 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.
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."
- The gotcha in the chapter: rubber-stamp approvals give false confidence and a name to blame.
- 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.
- 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. - 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.
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."
- 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.
- 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.
- 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.
- 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.
- 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.
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."
- 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.
- 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.
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.- 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 stackextension, or Graphite, git-town, Sapling, Jujutsu. Required reviews, checks and CODEOWNERS are evaluated as if each PR targeted the stack's base. - 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.
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."
- 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.
- 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.
- 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.
- 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.
- 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.
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."
- 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".
- 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.
- A required workflow must listen to
merge_groupas well aspull_requestif you use a merge queue; GitHub's docs say otherwise the check is never reported for the queued commit and the merge fails. - 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.
- 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.
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."
- 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). - In the code review chapter's plain-Git hook model, a commit with
test.only(…)is refused, the fixed commit goes through, and thengit commit --no-verifycommits aconsole.loganyway. - Hook and CI run the same tools in different modes:
prettier --writeandeslint --fixin the hook,prettier --checkandeslint --max-warnings=0in CI, so CI never changes code; it only says yes or no. - 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.
- 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.
/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."
// 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]}`);
}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- 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. src/api/README.mdmatches three rules. You might expect the API team, but*.mdcomes later, so only the docs team is requested and the API team isn't asked at all.- The migration has two owners on one line, so both teams are requested, and an approval from either satisfies the requirement for that file.
ui/App.jsxonly matches*, which is why the catch-all must be the first line: put it last and it would override every specific rule.- 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.
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."
- 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.
- 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.
- 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
400four times, logs a body containing anadminPin, and has a test assertingcallCount() === 4, which encodes the bug as the requirement. - The fix needs the server's cooperation: retry only
429,502,503,504, and make aPOSTsafe to retry with anIdempotency-Keythe device recognises. - 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/andAGENTS.mdso both reviewers check the same things.
Learn more: Reviewing AI-generated code.
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."
- The rule exists for the same reason as "the author can't approve their own PR": the requester is effectively the author.
- 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.
- 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.
- 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.
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."
- 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.
- 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.
- Give the reason next to each rule ("
===because==coerces types"); reasons get followed, bare rules get argued with. - The one-commit reformat goes in
.git-blame-ignore-revs(withgit config blame.ignoreRevsFile .git-blame-ignore-revs; GitHub's blame view reads that file name too), sogit blamestill shows who wrote each line. - 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.
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."
- Suppressions are counts per file and per rule, and cover errors only, not warnings.
- 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). - 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.
- 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).
- Watch the
eslint-disablecount too: a rising count means a rule is wrong or a habit is.
Learn more: Enforcing standards with tooling, not meetings.
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."
- 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).
- 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.
- 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.
- Write it as the proposal, before the work. ADRs written weeks later become justification: straw-man alternatives and missing costs.
- 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.
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."
- 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.
- 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.
- Its consequences include the costs: a new service to run, extra latency, a new place where calls are aggregated.
- 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.
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."
- 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.
- 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.
- Each line needs a checker (CI, the PR template, or the product owner). A DoD item with no checker is a wish.
- 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.
- 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.
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."
- The DoD applies to every item; the criteria to this one. Passing one never excuses the other.
- The bypass got the code onto
main, which is exactly why "merged" can't be the definition of done. - Rulesets let you restrict bypass to named people, so a bypass is a deliberate, visible act rather than a habit.
- 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.
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."
- Acceptance criteria: one backlog item; product owner with the team.
- Definition of done: every item; the whole team, or the organisation as a minimum.
- Definition of ready: items entering a sprint; the team. A common practice, not part of Scrum.
- Quality gate: every pull request or build, encoded in CI ("changed-line coverage at least 80%, zero lint errors").
- 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.
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."
- 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.
- RFC 9457 (2023):
application/problem+jsonwithtype,title,status,detailandinstance. One error shape lets the UI showdetailfor every endpoint. - The chapter's example:
POST /v1/devices/{id}/rebootrequires anIdempotency-Key(a reboot isn't naturally idempotent), returns202 Acceptedwith aLocationand a job, and has a unique camelCaseoperationId, because generators turn it into the SDK method name. - Versions as the chapter states them: OpenAPI 3.2.0 is current (September 2025); 3.1 is still the most widely supported by tools.
- House rules tools don't ship (no verbs in paths, collection GETs take
limitandcursor) can be custom Redocly or Spectral rules, or a short script over the parsed spec.
Learn more: API and documentation standards.
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."
- Rename: old clients get
undefined. Return both fields for a while withjobIdmarkeddeprecated: true, or bump the version. - Enum: the subtle one, and the one interviewers like. Document "clients must handle unknown values" up front if you expect to add states.
- Optional response field: safe. The mirror image isn't: a new required request field breaks old clients, because they don't send it.
- This is why the standard says the spec changes in the same PR: a breaking-change check (oasdiff's
breakingcommand compares two specs) can flag the rename and the enum automatically, before a human reviews it. - 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.
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?'"
- 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.
- The best test of a README is a new engineer, ideally in the other office, getting the service running without asking anyone.
- 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.
- 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.