Tech Lead NotesRohit’s interview study guide
Chapter 07

Agile and Scrum in Practice

What the Scrum Guide actually says (and doesn't), estimating and forecasting without fooling anyone, Kanban and flow, working with shifting priorities and missing documentation, and running Daily Scrums, refinement and retrospectives that change something.

5 topics
Parts marked Advanced are extra depth. Skip them on a first read or a quick revision.

#Scrum in one page

Added

Scrum is a small framework for doing complex work by trying something, looking at the result and adjusting. It doesn't tell you how to write software. It tells you who is accountable for what, which five events happen every Sprint and why, and which three artifacts keep the work visible. Everything else (story points, user stories, Jira, burndown charts, "the three standup questions") is a practice teams add on top. The authority is the Scrum Guide by Ken Schwaber and Jeff Sutherland; the current edition is the November 2020 one at scrumguides.org. Checked in October 2026: there's no newer official edition. In 2025 Jeff Sutherland, John Coleman and Ralph Jocham published the Scrum Guide Expansion Pack (scrumexpansion.org), but it calls itself a companion to the 2020 Guide, not a replacement, so interview questions about "the rules of Scrum" are still answered from the 2020 text. Everything on this page is checked against that text.

A good mental model: the Scrum Guide is the rulebook of a sport, not a coaching manual. The rulebook says how long a match lasts, how many players there are and what counts as a goal. It doesn't tell you which formation to play. When a team says "we do Scrum, but our Sprints are six weeks and the manager runs the standup", they're playing a different sport with the same name. When someone says "Scrum says you must use story points", they're quoting a coaching manual as if it were the rulebook.

The whole framework fits in one sentence per piece. Underneath it are three pillars: transparency, inspection and adaptation (empiricism: decide from what has actually happened). The Guide also lists five values: commitment, focus, openness, respect and courage.

The Sprint: one month or less; the next one starts as soon as this one ends Product Backlog commits to: Product Goal Sprint Planning why · what · how Sprint Backlog commits to: Sprint Goal Increment commits to: Def. of Done Daily Scrum 15 min adapts the plan daily Sprint Review with stakeholders Retrospective how we work next Sprint Review adapts the Product Backlog
Three accountabilities do the work (Product Owner, Scrum Master, Developers), five events inspect and adapt it, and each of the three artifacts has one commitment.
PieceWhat the 2020 Guide says
Scrum TeamOne Scrum Master, one Product Owner and Developers; no sub-teams or hierarchies; cross-functional and self-managing ("internally decide who does what, when, and how"); typically 10 or fewer people
Product OwnerAccountable for maximising the value of the product; owns and orders the Product Backlog; one person, not a committee; the only one who can cancel a Sprint
Scrum MasterAccountable for establishing Scrum as the Guide defines it and for the Scrum Team's effectiveness; coaches, removes impediments, makes sure events happen and are positive, productive and in their timebox
DevelopersWhoever does the work of the Increment; they create the Sprint Backlog, hold the Daily Scrum, adhere to the Definition of Done, and are responsible for sizing
The SprintA container for the other events; fixed length, one month or less; the next starts right after the last
Sprint PlanningThree topics: why this Sprint is valuable (the Sprint Goal), what can be done, how it will be done; at most 8 hours for a one-month Sprint
Daily Scrum15 minutes, for the Developers, same time and place every working day; inspects progress toward the Sprint Goal and adapts the plan
Sprint ReviewInspects the outcome with key stakeholders and decides what to do next; a working session, not a presentation; at most 4 hours for a one-month Sprint
Sprint RetrospectivePlans ways to increase quality and effectiveness; ends the Sprint; at most 3 hours for a one-month Sprint
Product Backlog → Product GoalAn emergent, ordered list of what's needed; the Product Goal is the long-term objective it serves
Sprint Backlog → Sprint GoalSprint Goal + selected items + the plan; the Sprint Goal is the single objective for the Sprint
Increment → Definition of DoneA usable step toward the Product Goal; work that doesn't meet the Definition of Done isn't part of an Increment and can't be presented at the Review

The timeboxes are maximums for a one-month Sprint, and after each one the Guide adds "For shorter Sprints, the event is usually shorter." It doesn't give a formula for a two-week Sprint. And "during the Sprint" it sets four rules worth knowing word for word: no changes are made that would endanger the Sprint Goal; quality does not decrease; the Product Backlog is refined as needed; scope may be clarified and renegotiated with the Product Owner as more is learned.

Here's that table turned into a checker. It takes two illustrative teams (not real ones) and tests each against only the rules the Guide states:

scrum-check.mjs
// "Is this Scrum?" Checks a team's process against the rules the 2020 Scrum Guide actually states.
// Timeboxes in the Guide are maximums for a one-month Sprint; "for shorter Sprints, the event is usually shorter."
const rules = [
  ["Sprint is one month or less", (t) => t.sprintWeeks <= 4],
  ["Daily Scrum is 15 minutes", (t) => t.dailyMinutes <= 15],
  ["Daily Scrum is for the Developers", (t) => t.dailyRunBy === "developers"],
  ["Sprint Planning ≤ 8 h (one-month Sprint)", (t) => t.planningHours <= 8],
  ["Sprint Review ≤ 4 h (one-month Sprint)", (t) => t.reviewHours <= 4],
  ["Sprint Retrospective ≤ 3 h (one-month Sprint)", (t) => t.retroHours <= 3],
  ["Typically 10 or fewer people", (t) => t.people <= 10],
  ["The Product Owner is one person", (t) => t.productOwners === 1],
  ["Every Sprint has a Sprint Goal", (t) => t.hasSprintGoal],
  ["The Increment meets a Definition of Done", (t) => t.hasDefinitionOfDone],
  ["Only the Product Owner cancels a Sprint", (t) => t.whoCancels === "product owner"],
];

// Illustrative teams, not real ones.
const teams = {
  "Team A (two-week Sprints)": {
    sprintWeeks: 2, dailyMinutes: 15, dailyRunBy: "developers", planningHours: 3, reviewHours: 1.5,
    retroHours: 1, people: 7, productOwners: 1, hasSprintGoal: true, hasDefinitionOfDone: true,
    whoCancels: "product owner",
  },
  "Team B (\"we do Scrum\")": {
    sprintWeeks: 6, dailyMinutes: 45, dailyRunBy: "manager", planningHours: 4, reviewHours: 1,
    retroHours: 0.5, people: 14, productOwners: 3, hasSprintGoal: false, hasDefinitionOfDone: true,
    whoCancels: "engineering manager",
  },
};

for (const [name, team] of Object.entries(teams)) {
  const broken = rules.filter(([, ok]) => !ok(team)).map(([rule]) => rule);
  console.log(`${name}: ${broken.length ? `${broken.length} of ${rules.length} rules broken` : "follows the Guide's rules"}`);
  for (const rule of broken) console.log(`  ✗ ${rule}`);
}

// Searched for in the 2020 text: none of these appear in it. Teams may add them; they aren't Scrum's rules.
const notInTheGuide = ["story points", "velocity", "user stories", "estimates", "Definition of Ready", "Sprint Zero"];
console.log(`Not in the Guide: ${notInTheGuide.join(", ")}`);
console.log("Named only as optional ways to forecast: burn-downs, burn-ups, cumulative flows");
Result
Team A (two-week Sprints): follows the Guide's rules
Team B ("we do Scrum"): 7 of 11 rules broken
  ✗ Sprint is one month or less
  ✗ Daily Scrum is 15 minutes
  ✗ Daily Scrum is for the Developers
  ✗ Typically 10 or fewer people
  ✗ The Product Owner is one person
  ✗ Every Sprint has a Sprint Goal
  ✗ Only the Product Owner cancels a Sprint
Not in the Guide: story points, velocity, user stories, estimates, Definition of Ready, Sprint Zero
Named only as optional ways to forecast: burn-downs, burn-ups, cumulative flows
What’s happening
  1. Each rule is a sentence from the 2020 Guide paired with a test. The timebox rules compare against the one-month maximums (8 h, 4 h, 3 h) because that's all the Guide fixes; Team A's 3-hour Planning for a two-week Sprint passes, and so would 5 hours. A shorter Sprint "usually" has shorter events, which is advice, not a rule a checker can enforce.
  2. Team A, a two-week Sprint with a 15-minute Daily Scrum run by the Developers, seven people, one Product Owner, a Sprint Goal and a Definition of Done, breaks nothing. Notice what it doesn't need to pass: points, velocity, Jira, a burndown chart. None of those are tested because none are required.
  3. Team B breaks 7 of 11 rules. Six-week Sprints exceed "one month or less". A 45-minute standup that a manager runs is a status meeting, not a Daily Scrum: the Guide makes it a 15-minute event for the Developers (the Product Owner and Scrum Master join as Developers only if they're working on Sprint Backlog items). Fourteen people exceeds "typically 10 or fewer", and the Guide's advice is to split into several cohesive Scrum Teams sharing one Product Goal, Product Backlog and Product Owner.
  4. Three Product Owners breaks "one person, not a committee", and no Sprint Goal means there's nothing for the Daily Scrum to inspect progress toward, and no basis for saying no to a mid-Sprint request. An engineering manager cancelling Sprints takes away the one power the Guide gives only to the Product Owner.
  5. The last two lines come from searching the 2020 text itself: "story points", "velocity", "user stories", "Definition of Ready" and "Sprint Zero" don't appear, and there's no word "estimate" (only that the Developers are responsible for "sizing"). Burn-downs, burn-ups and cumulative flows are mentioned once, as optional ways to forecast that "do not replace the importance of empiricism".
  6. This is the point to make in an interview: Scrum is a small set of rules plus a lot of freedom. Know which is which, and you can say "that's our practice, not Scrum" without sounding dogmatic, and "that breaks Scrum, here's what it costs" without sounding pedantic.
AdvancedWhat changed in the 2020 Guide

Interviewers who learned Scrum before 2020 sometimes use the old vocabulary. The revision notes at scrumguides.org list most of these changes; the refinement row comes from comparing the 2017 and 2020 texts, since the notes don't mention it. The ones that come up:

2017 Guide2020 GuideWhy it matters
"Development Team" inside the Scrum TeamOne Scrum Team; "Developers" is an accountabilityremoves the "us and the PO" split
self-organisingself-managing (who does what, when, and how)the team also decides what within the Sprint, with the PO
no long-term goal artifactProduct Goal added, as the Product Backlog's commitmentSprints serve a larger objective
Sprint Planning: what and howadds why (the Sprint Goal) as the first topicthe goal comes first, the items follow
three Daily Scrum questions (yesterday, today, impediments), offered as "an example of what might be used"removed; any structure that focuses on the Sprint Goal"the three questions" were never mandatory, and now aren't mentioned at all
refinement "usually consumes no more than 10%" of capacityno figurestill an ongoing activity, size it yourself
commitments impliciteach artifact has a commitment: Product Goal, Sprint Goal, Definition of Donethree things to be transparent about
IT-specific wordingdomain-neutral and shorterScrum isn't only for software

The 2025 Expansion Pack is a separate, much longer document (version 2.0.9 at scrumexpansion.org when checked in October 2026) that adds guidance on product thinking, complexity, leadership and AI. Its listed creators are Jeff Sutherland, John Coleman and Ralph Jocham (Ken Schwaber isn't among them), and it doesn't change the 2020 rules. If an interviewer mentions it, it's fine to say you know it exists as an optional companion and that you work from the 2020 Guide.

AdvancedOne Scrum Team across Austin and India

The Guide's rules have consequences for a team split between two sites ten and a half to eleven and a half hours apart (worked out in Working across US and India time zones):

  • "No sub-teams or hierarchies": if the India developers are "the offshore team" that receives tickets and the Austin developers are "the real team" that designs, you have two teams sharing a board. Either make it one team with shared goals, shared review and shared retros, or make it two Scrum Teams with their own Sprint Goals under one Product Owner and one Product Backlog. Both are legitimate; pretending to be the first while running the second is what causes trouble.
  • "Same time and place every working day" for the Daily Scrum: on a distributed team "the place" is the video call and "the time" has to be in the overlap window, which is 30–90 minutes long or zero depending on the India shift and the US clock change. Some teams hold the Daily Scrum in the overlap and use a written update for the rest; that's a practice, and the Developers are free to choose it, as long as it inspects progress toward the Sprint Goal and produces a plan for the next day.
  • Sprint Review with the client's stakeholders: if they're in the US, the Review falls in the India evening. Rotate who carries the late slot, record it, and let the India developers demo their own work (not the lead on their behalf).
  • The Product Owner is one person: often on the client's side. The lead's job is to make the PO's decisions reach the India team the same day, in writing.
AdvancedA two-week Sprint calendar across both sites

Where the events land matters as much as what they are. This model books a two-week Sprint's events in Austin time and counts, for each site, the minutes that fall outside its working day (Austin 08:00–17:00, India on a shifted 11:00–20:00 day), for one Sprint before the US clock change on 1 November 2026 and one after:

sprint-calendar.mjs
// The Scrum events of a two-week Sprint, booked in Austin time, seen from both sites.
// Austin works 08:00–17:00 America/Chicago; India works 11:00–20:00 Asia/Kolkata.
const zones = { Austin: "America/Chicago", India: "Asia/Kolkata" };
const hours = { Austin: [8 * 60, 17 * 60], India: [11 * 60, 20 * 60] };

function offsetMinutes(zone, instant) {
  const name = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "longOffset" })
    .formatToParts(instant).find((p) => p.type === "timeZoneName").value;
  const m = name.match(/GMT([+-])(\d\d):(\d\d)/);
  return m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
}
function wallToInstant(date, time, zone) {
  const asIfUtc = Date.parse(`${date}T${time}:00Z`);
  let guess = asIfUtc - offsetMinutes(zone, new Date(asIfUtc)) * 60_000;
  guess = asIfUtc - offsetMinutes(zone, new Date(guess)) * 60_000;
  return new Date(guess);
}
const minuteOfDay = (zone, t) => {
  const [h, m] = new Intl.DateTimeFormat("en-GB", { timeZone: zone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" })
    .format(t).split(":").map(Number);
  return h * 60 + m;
};
// Minutes of the event that fall outside a site's working day (events here never cross midnight locally).
const outside = (site, start, minutes) => {
  const from = minuteOfDay(zones[site], start), to = from + minutes;
  return Math.max(0, hours[site][0] - from) + Math.max(0, to - hours[site][1]);
};
const hhmm = (site, t) => new Intl.DateTimeFormat("en-GB", { timeZone: zones[site], hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).format(t);

const events = [ // [name, day of the Sprint (0 = first Monday), Austin start, minutes, how many times]
  ["Sprint Planning", 0, "07:00", 90, 1],
  ["Daily Scrum", 1, "08:00", 15, 8], // the other weekdays, except Planning day and the last day
  ["Refinement", 7, "08:15", 45, 1],
  ["Sprint Review", 11, "07:00", 60, 1],
  ["Sprint Retrospective", 11, "08:00", 60, 1],
];

for (const monday of ["2026-10-19", "2026-11-02"]) { // before and after US clocks go back on 1 November
  const late = { Austin: 0, India: 0 };
  console.log(`Sprint starting ${monday}`);
  for (const [name, day, time, minutes, times] of events) {
    const date = new Date(Date.parse(`${monday}T12:00:00Z`) + day * 86_400_000).toISOString().slice(0, 10);
    const start = wallToInstant(date, time, zones.Austin), end = new Date(start.getTime() + minutes * 60_000);
    const cost = Object.fromEntries(Object.keys(zones).map((s) => [s, outside(s, start, minutes)]));
    for (const s of Object.keys(late)) late[s] += cost[s] * times;
    const note = Object.entries(cost).filter(([, m]) => m).map(([s, m]) => `${s} +${m} min`).join(", ") || "inside both days";
    console.log(`  ${name.padEnd(21)} Austin ${hhmm("Austin", start)}–${hhmm("Austin", end)}  India ${hhmm("India", start)}–${hhmm("India", end)}  ${note}`);
  }
  console.log(`  Minutes outside working hours this Sprint: Austin ${late.Austin}, India ${late.India}`);
}
Result
Sprint starting 2026-10-19
  Sprint Planning       Austin 07:00–08:30  India 17:30–19:00  Austin +60 min
  Daily Scrum           Austin 08:00–08:15  India 18:30–18:45  inside both days
  Refinement            Austin 08:15–09:00  India 18:45–19:30  inside both days
  Sprint Review         Austin 07:00–08:00  India 17:30–18:30  Austin +60 min
  Sprint Retrospective  Austin 08:00–09:00  India 18:30–19:30  inside both days
  Minutes outside working hours this Sprint: Austin 120, India 0
Sprint starting 2026-11-02
  Sprint Planning       Austin 07:00–08:30  India 18:30–20:00  Austin +60 min
  Daily Scrum           Austin 08:00–08:15  India 19:30–19:45  inside both days
  Refinement            Austin 08:15–09:00  India 19:45–20:30  India +30 min
  Sprint Review         Austin 07:00–08:00  India 18:30–19:30  Austin +60 min
  Sprint Retrospective  Austin 08:00–09:00  India 19:30–20:30  India +30 min
  Minutes outside working hours this Sprint: Austin 120, India 60
What’s happening
  1. Every event is booked in America/Chicago, the way an Austin-based organiser's calendar stores it. The India times are derived from the time-zone rules, not typed in, so they shift by themselves when US clocks change.
  2. In the October Sprint (US daylight time, India 10 h 30 min ahead) the India day ends at 20:00 = 09:30 Austin, so everything from 08:00 to 09:30 Austin fits both sites. Planning and the Review are put at 07:00 Austin so they fit inside the India day; Austin carries the cost, 60 minutes early each, 120 minutes for the Sprint.
  3. In the November Sprint (US standard time, India 11 h 30 min ahead) the same Austin times land an hour later in India. The Daily Scrum still fits (19:30–19:45), but refinement and the Retrospective now run until 20:30 IST, 30 minutes past the India day. Nothing on the Austin calendar changed; the cost moved to India silently.
  4. The Daily Scrum, eight times a Sprint, is the event to protect: it's inside both days in both seasons. A Daily Scrum at 08:30 Austin would have been fine in October and outside the India day all winter.
  5. The habit this teaches: put these numbers in front of the team when the clocks change (twice a year in the US, never in India), and rebalance who starts early or stays late. The full overlap arithmetic is in Working across US and India time zones.
AdvancedScrum anti-patterns and what they cost
Anti-patternWhat breaksWhat to do instead
Sprint Goal = list of ticketsno flexibility; every surprise is a failurean outcome a stakeholder could check in the Review
Daily Scrum as status to a managerself-management; people stop asking each other for helpDevelopers' event, walk the board, status in writing
"Hardening" or "stabilisation" Sprintsthe Increment wasn't really Done; quality debt hiddena Definition of Done that includes testing and release readiness
Carrying unfinished items over silentlyvelocity and forecasts become fictionunfinished work returns to the Product Backlog and is re-ordered
A committee acting as Product Ownerconflicting priorities reach the team directlyone Product Owner who represents the stakeholders
Review as a slide demono real feedback on working softwarestakeholders use the Increment; the Product Backlog is adapted
Retro actions with no ownernothing changes; retros lose credibilityone or two actions, one owner each, in the next Sprint Backlog
Onshore team plans, offshore team executes"no sub-teams or hierarchies" is broken; knowledge stays in one placeeveryone in Planning and refinement; India developers own designs too
AdvancedTrap: the Scrum Master is not the manager
AdvancedTrap: the Sprint Goal is not "finish every ticket"
Who decides, mid-Sprint?

On day 6 of a two-week Sprint, the client's engineering manager tells your team to drop the Sprint Goal and start on an urgent integration instead. The engineering manager isn't the Product Owner. What does the Guide say, and what do you do?

Show answer

The Product Owner decides. Only the Product Owner can cancel a Sprint, and only the Product Owner can renegotiate scope with the Developers. The engineering manager has to go through the Product Owner.

What’s happening
  1. Check the Guide's rules for "during the Sprint": no changes that would endanger the Sprint Goal. Starting a different integration endangers it, so it can't just be slotted in.
  2. There are two legitimate paths. If the Sprint Goal is now obsolete, the Product Owner can cancel the Sprint ("Only the Product Owner has the authority to cancel the Sprint"), and the team plans a new one around the integration. If the goal still matters, the Product Owner orders the integration at the top of the Product Backlog for the next Sprint, or works out with the Developers whether part of it fits without endangering the goal.
  3. Your job as the lead is to make the trade-off visible fast: "If we start this now, the firmware scheduling goal won't be met; here's what's Done so far and what the integration would take." Then get the Product Owner and the engineering manager in one conversation, the same day, so the India developers don't come in to a changed plan without the reason.
  4. What not to do: quietly start the integration "in parallel". That's the most common way a Sprint fails, and nobody decided to fail it.
How long is Sprint Planning for a two-week Sprint?

A colleague says "the Scrum Guide says Sprint Planning is four hours for a two-week Sprint". Right or wrong?

Show answer

Wrong, strictly. The Guide gives a maximum of eight hours for a one-month Sprint and adds that for shorter Sprints the event is usually shorter. It never gives a figure for two weeks.

What’s happening
  1. Halving to four hours is a reasonable rule of thumb that many teams use, and older material repeats it, but it isn't in the Guide's text.
  2. A two-week Sprint with a well-refined backlog often plans in one to two hours. A team that needs the full four probably has refinement problems (items arriving at Planning unclear or too big).
  3. Answer it that way in an interview: state the actual rule, call the halving a rule of thumb, and say what you'd look at if Planning ran long.

#Estimation and planning

Added

Estimates exist to support decisions, not to make promises. You estimate so the Product Owner can order the backlog (a small item with big value goes first), so the team can plan a Sprint it can actually finish, and so the client can get a forecast for a date. Each of those needs a different level of precision, and none of them needs a number to be "right". The Scrum Guide doesn't prescribe a method; it only says the Developers who'll do the work are responsible for sizing it.

A good mental model is a weather forecast. A useful forecast says "70% chance of rain tomorrow", not "it will rain at 14:05". It's made from past patterns, it's updated as new information arrives, and nobody blames the forecaster personally when it doesn't rain. Turning a forecast into a promise ("you said it would rain!") makes forecasters pad their numbers until they're useless. Estimates behave the same way: the moment they're used to judge people, they inflate.

The common units, and what each is good for:

UnitWhat it isGood forWatch out for
Story pointsrelative size (effort, complexity, uncertainty) on a scale like 1, 2, 3, 5, 8, 13quick relative sizing; a team's own Sprint planningconverted to hours, compared across teams, used as a performance metric
Ideal hours or daystime if nothing interruptssmall, well-understood tasks; the Sprint plan's taskseveryone hears "hours" as a deadline
T-shirt sizesS / M / L / XLearly roadmap sizing of epicsfalse precision if mapped straight to points
Count of itemsno sizing, just items split to a similar small sizeforecasting with throughput (below); Kanban teamsonly works if items really are split small

Ron Jeffries, one of the creators of Extreme Programming, wrote in May 2019 ("Story Points Revisited" on ronjeffries.com) that he may have invented story points and that, if he did, he's sorry now. As he tells it, XP stories were first estimated in "ideal days" of time, converted to calendar time with a load factor; stakeholders were confused by the gap between those "days" and real days, so the team started calling them "points". He deplores the way points have since been misused, and he now thinks estimation, in points or in time, is mostly worth avoiding. That's worth knowing because it's the honest position: points are a tool for the team's own planning conversation, not a currency.

The planning question a lead actually faces every two weeks is "how much can we take on this time?". Here it is for an illustrative team of two people in Austin and four in India, for the Sprint of 16 to 27 November 2026, which contains US Thanksgiving:

capacity.mjs
// Sprint capacity for an illustrative team: 2 people in Austin, 4 in India, Sprint of 16–27 November 2026.
const sprint = { start: "2026-11-16", calendarDays: 14 }; // Monday to the second Friday

// US Thanksgiving is the fourth Thursday of November; many US companies also close the Friday after.
const thanksgiving = (year) => {
  const firstWeekday = new Date(Date.UTC(year, 10, 1)).getUTCDay(); // 0 = Sunday
  return new Date(Date.UTC(year, 10, 1 + ((4 - firstWeekday + 7) % 7) + 21));
};
const iso = (d) => d.toISOString().slice(0, 10);
const tg = thanksgiving(2026);
const holidays = { us: [iso(tg), iso(new Date(tg.getTime() + 86_400_000))], in: [] };

const weekdays = [];
for (let i = 0; i < sprint.calendarDays; i++) {
  const d = new Date(Date.parse(`${sprint.start}T00:00:00Z`) + i * 86_400_000);
  if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6) weekdays.push(iso(d));
}
const EVENT_DAYS = 1.5; // Planning, Review, Retro, Daily Scrums and refinement per person in two weeks (rule of thumb)

// focus = share of the remaining time that goes to Sprint Backlog work. "usual" is a normal Sprint.
const people = [
  { name: "Lead (Austin)", site: "us", usual: 0.5, now: 0.4, leave: [] }, // reviews, unblocking, onboarding this time
  { name: "Dev 1 (Austin)", site: "us", usual: 0.8, now: 0.8, leave: [] },
  { name: "Dev 2 (India)", site: "in", usual: 0.8, now: 0.8, leave: [] },
  { name: "Dev 3 (India)", site: "in", usual: 0.8, now: 0.8, leave: ["2026-11-18", "2026-11-19", "2026-11-20"] },
  { name: "Dev 4 (India)", site: "in", usual: 0.8, now: 0.6, leave: [] }, // the new joiner's buddy
  { name: "New joiner (India)", site: "in", usual: 0, now: 0.4, leave: [] }, // wasn't here for past Sprints
];
const focused = (days, focus) => Math.max(0, days - EVENT_DAYS) * focus;

let usual = 0, now = 0;
for (const p of people) {
  const days = weekdays.filter((d) => !holidays[p.site].includes(d) && !p.leave.includes(d)).length;
  usual += focused(weekdays.length, p.usual);
  now += focused(days, p.now);
  console.log(`${p.name.padEnd(19)} ${String(days).padStart(2)} of ${weekdays.length} days → ${focused(days, p.now).toFixed(1)} focused days`);
}

const velocities = [21, 26, 18, 24, 23]; // points Done in the last five Sprints
const avg = velocities.reduce((a, b) => a + b, 0) / velocities.length;
const ratio = now / usual;
console.log(`Thanksgiving ${iso(tg)} and ${holidays.us[1]} are US holidays`);
console.log(`Focused days ${now.toFixed(1)} vs a usual ${usual.toFixed(1)} → ${Math.round(ratio * 100)}% of normal capacity`);
console.log(`Velocity last 5: ${velocities.join(", ")} (average ${avg.toFixed(1)}, lowest ${Math.min(...velocities)})`);
console.log(`Plan about ${Math.round(Math.min(...velocities) * ratio)}–${Math.round(avg * ratio)} points, not ${Math.round(avg)}`);
Result
Lead (Austin)        8 of 10 days → 2.6 focused days
Dev 1 (Austin)       8 of 10 days → 5.2 focused days
Dev 2 (India)       10 of 10 days → 6.8 focused days
Dev 3 (India)        7 of 10 days → 4.4 focused days
Dev 4 (India)       10 of 10 days → 5.1 focused days
New joiner (India)  10 of 10 days → 3.4 focused days
Thanksgiving 2026-11-26 and 2026-11-27 are US holidays
Focused days 27.5 vs a usual 31.5 → 87% of normal capacity
Velocity last 5: 21, 26, 18, 24, 23 (average 22.4, lowest 18)
Plan about 16–20 points, not 22
What’s happening
  1. The model finds the Sprint's ten weekdays and US Thanksgiving by rule (the fourth Thursday of November: 26 November in 2026), and treats the Friday after as a holiday too because many US companies close then. The two Austin people lose two days each; the India team works through, except Dev 3's three days of leave.
  2. Not every working day goes to Sprint Backlog work. EVENT_DAYS takes 1.5 days per person for Planning, Review, Retrospective, Daily Scrums and refinement, and focus is the share of what's left that goes to Sprint work (the rest is support, reviews, meetings). Both are rules of thumb for this team, not industry figures; the point is to write them down so they can be argued with.
  3. The lead's focus drops from 0.5 to 0.4 and the buddy's from 0.8 to 0.6 because a new joiner is starting. The new joiner contributes 3.4 focused days at 0.4, but they weren't in the past Sprints whose velocity we're about to use, which is exactly why onboarding Sprints are usually over-committed: people add a head and forget the time it takes from two others.
  4. The usual capacity (the same formula with no leave, no holidays, usual focus and no new joiner) is 31.5 focused days; this Sprint has 27.5, so 87% of normal.
  5. Velocity has been 21, 26, 18, 24 and 23 points. Scaling the average (22.4) and the lowest (18) by 87% gives a range of 16 to 20 points. Plan inside that range, choosing the Sprint Goal first and the items second, not "the usual 22".
  6. If you changed the Sprint to include a holiday on the India side instead (Diwali and Holi move every year with the lunar calendar, so get the India office's holiday list each January), the same model shows the drop on the other side. Capacity planning is mostly about not being surprised by calendars.
AdvancedPlanning poker and what the conversation is for

Planning poker (popularised by Mike Cohn's Agile Estimating and Planning, 2005) has every Developer reveal a size at the same time, so nobody anchors on the most senior person's number. Its real value isn't the number:

  • When one person says 2 and another says 13, one of them knows something the other doesn't: an existing library, a hidden dependency on the device firmware, a missing API. Ask the high and low voters to explain, then vote again. That discussion is the estimate's main output.
  • Size relative to a few reference items the whole team remembers ("the login rate limit was a 3"), not in hours.
  • If an item keeps coming out at 13 or more, don't argue about 13 vs 20: split it. Large estimates are mostly uncertainty.
  • On a distributed team, do it asynchronously for most items (each person enters a size in the ticket before refinement, and only the disagreements get discussed live in the overlap), so the India developers aren't sizing at 20:00 their time.
AdvancedSpikes: buying information with a timebox

When an item can't be sized because nobody knows how something works (an undocumented device API, a third-party SDK, a performance question), don't guess. Create a spike: a timeboxed investigation whose output is knowledge, not shippable code.

Spike ticket template
# SPIKE: Can the web UI read call statistics from the device's local API?
Question:     Which endpoint, if any, returns live call statistics, and at what rate can we poll it?
Why now:      UI-240 (call stats panel) can't be sized until we know.
Timebox:      1.5 days, Dev 2. Stop at the timebox even if unfinished; report what's known.
Output:       a short note in the ticket: endpoints found, sample responses, limits observed,
              a recommendation, and the size of UI-240 re-estimated by the team.
Not in scope: building the panel; production-quality code (throwaway scripts are fine).
What’s happening
  1. The question is specific enough that "done" is obvious. "Investigate the device API" has no end; "which endpoint returns call statistics, and how fast can we poll it" does.
  2. Why now links the spike to the item it unblocks, so the Product Owner can see the spike buys something and can order it.
  3. The timebox is the important line. A spike that runs over is a project nobody approved. At the timebox the developer stops and reports, and the team decides whether more investigation is worth it.
  4. The output is written in the ticket, which matters on a distributed team: the Austin lead reads it in the morning without a meeting, and the re-sized item can go into the next Sprint.
  5. "Throwaway scripts are fine" gives permission to learn fast. If the spike code is good enough to keep, it still goes through review and tests like any other code.
AdvancedSplitting items so they fit

Large items are mostly uncertainty, and they wreck both estimates and flow. Split them vertically (each piece goes through every layer and can be demoed), not horizontally (all the API, then all the UI). An illustrative example for a device-management web UI:

Splitting "Admins can manage device firmware"
Too big (estimated 20+ by everyone, nobody agrees why):
  "Admins can manage device firmware"

Split by workflow step, each one usable and demoable on its own:
  1. Admins can see each device's current firmware version in the device list         (3)
  2. Admins can filter the list by firmware version                                      (2)
  3. Admins can start an update on one device and see its progress                       (5)
  4. Admins can schedule an update for a group of devices at a chosen time               (5)
  5. Admins get a report of devices whose update failed, with the device's error         (3)
Split out as its own item, because nobody knew the answer:
  SPIKE: How does the device report update progress (poll, push, or neither)?   (timebox 1 day)
Deferred by the Product Owner (not needed for the first release):
  Rolling back to the previous firmware from the UI
What’s happening
  1. The original item got 20+ from everyone because it hides at least five behaviours and one unknown. Arguing about 20 vs 40 is pointless; the number is just saying "too big to see".
  2. Splitting by workflow step gives items that each deliver something an admin can use: seeing the version is useful on its own, before scheduling exists. Each one goes through UI, API and tests, so each can meet the Definition of Done in a few days.
  3. The unknown (how the device reports progress) becomes a timeboxed spike instead of being buried inside item 3's estimate. Item 3 is re-sized after the spike.
  4. Splitting exposes choices: rollback turned out to be separable, so the Product Owner could defer it. That's the hidden value of splitting: the PO gets to order the pieces instead of accepting the whole thing.
  5. Other split patterns worth naming in an interview: happy path first then edge cases, one data variation first (one device model) then others, read-only before edit, and manual first then automated.
AdvancedForecasting a date: ranges, not a number

"When will these 40 items be done?" Dividing by the average throughput gives one number and hides the spread. A Monte Carlo forecast replays the team's real past Sprints in random order thousands of times and reports how often each outcome happened. Seeded here (mulberry32 with seed 42) so the output is the same every run:

forecast.mjs
// "When will these 40 backlog items be done?" A Monte Carlo forecast from past throughput (seeded, so repeatable).
function mulberry32(seed) {
  return () => {
    seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}
const random = mulberry32(42);

const throughput = [5, 7, 4, 6, 6, 3, 7, 5]; // items Done per Sprint, last eight Sprints
const remaining = 40;
const trials = 10_000;

const sprintsNeeded = [];
for (let t = 0; t < trials; t++) {
  let done = 0, sprints = 0;
  while (done < remaining) {
    done += throughput[Math.floor(random() * throughput.length)]; // replay a random past Sprint
    sprints++;
  }
  sprintsNeeded.push(sprints);
}
sprintsNeeded.sort((a, b) => a - b);
const pct = (p) => sprintsNeeded[Math.ceil((p / 100) * trials) - 1];

const avg = throughput.reduce((a, b) => a + b, 0) / throughput.length;
console.log(`Average throughput ${avg} items/Sprint → "${Math.ceil(remaining / avg)} Sprints" if you divide`);
for (const p of [50, 85, 95]) console.log(`${p}% of simulated futures finish within ${pct(p)} Sprints`);
for (const n of [7, 8, 9]) {
  const share = sprintsNeeded.filter((s) => s <= n).length / trials;
  console.log(`Chance of finishing within ${n} Sprints: ${Math.round(share * 100)}%`);
}

const counts = {};
for (const s of sprintsNeeded) counts[s] = (counts[s] ?? 0) + 1;
for (const [s, n] of Object.entries(counts)) console.log(`${s.padStart(2)} Sprints ${"#".repeat(Math.round(n / 100))} ${n}`);
Result
Average throughput 5.375 items/Sprint → "8 Sprints" if you divide
50% of simulated futures finish within 8 Sprints
85% of simulated futures finish within 9 Sprints
95% of simulated futures finish within 9 Sprints
Chance of finishing within 7 Sprints: 31%
Chance of finishing within 8 Sprints: 83%
Chance of finishing within 9 Sprints: 99%
 6 Sprints # 74
 7 Sprints ############################## 3010
 8 Sprints #################################################### 5215
 9 Sprints ################ 1561
10 Sprints # 137
11 Sprints  3
What’s happening
  1. throughput is how many items the team finished in each of the last eight Sprints: 5, 7, 4, 6, 6, 3, 7, 5. It counts items, not points, which works when the team splits items to roughly similar size; it avoids estimating the remaining 40 at all.
  2. Each of the 10,000 trials simulates the future by picking a random past Sprint's throughput, again and again, until 40 items are done, and records how many Sprints that took. Good and bad Sprints come up in realistic proportions because they're the team's own.
  3. Dividing (40 ÷ 5.375 = 7.4, so 8 Sprints) happens to match the median here, but it says nothing about risk. The simulation does: 83% of futures finish within 8 Sprints, 99% within 9, and only 31% within 7.
  4. That's what to tell a client: "8 Sprints is likely (about 80%); 9 is almost certain; 7 is a one-in-three bet". If they need 7, the conversation becomes scope ("which 10 items can wait?"), not "work harder".
  5. The histogram shows the shape: most outcomes at 7 or 8 Sprints, a tail to 10 and 11. Re-run the forecast every Sprint with the new throughput; a forecast is a living number.
AdvancedVelocity as a target: what goes wrong

Velocity (points finished per Sprint) is a planning input for one team. Goodhart's law, in its common phrasing "when a measure becomes a target, it ceases to be a good measure", describes what happens when it's used to judge the team. Illustrative numbers:

velocity-target.mjs
// What happens when velocity becomes a target. Illustrative numbers: the team finishes the same six items
// every Sprint; only the estimates move, because "velocity must go up 10% a Sprint".
const items = [1, 2, 3, 3, 5, 8]; // honest sizes, in points, of the work Done each Sprint

console.log("Sprint  items  points  growth");
let inflation = 1;
let prev = null;
for (let sprint = 1; sprint <= 6; sprint++) {
  // Each Sprint every estimate gets rounded up a little more to hit the target.
  const points = items.reduce((sum, p) => sum + Math.ceil(p * inflation), 0);
  const growth = prev ? `+${Math.round(((points - prev) / prev) * 100)}%` : "";
  console.log(`${String(sprint).padStart(6)}  ${String(items.length).padStart(5)}  ${String(points).padStart(6)}  ${growth}`);
  prev = points;
  inflation *= 1.1;
}

// Two teams Done the same four items. Their point scales differ, so their velocities can't be compared.
const teams = { "Team A": [2, 3, 5, 8], "Team B": [5, 8, 13, 20] };
for (const [name, pts] of Object.entries(teams)) {
  console.log(`${name}: ${pts.length} items Done, velocity ${pts.reduce((a, b) => a + b, 0)}`);
}
Result
Sprint  items  points  growth
     1      6      22  
     2      6      28  +27%
     3      6      30  +7%
     4      6      31  +3%
     5      6      35  +13%
     6      6      38  +9%
Team A: 4 items Done, velocity 18
Team B: 4 items Done, velocity 46
What’s happening
  1. Every Sprint the team finishes exactly the same six items, sized honestly at 1, 2, 3, 3, 5 and 8 points (22 in total).
  2. Someone sets a target: velocity must grow 10% a Sprint. Nothing about the work changes, but each estimate is nudged up (inflation grows by 1.1 each Sprint and every size is rounded up). Velocity climbs 22 → 28 → 30 → 31 → 35 → 38, a 73% "improvement" with zero extra output.
  3. The rounding makes the growth jumpy (+27%, then +3%), which is a tell: real improvement shows up in throughput, cycle time and defects, not in points alone.
  4. The second part: two teams finish the same four items, but Team B's scale runs larger, so its velocity is 46 against Team A's 18. Comparing teams by velocity measures their scales, not their output.
  5. What to use instead when someone needs a productivity signal: items finished and cycle time (see Kanban and when it fits better), the Sprint Goal hit rate, escaped defects, and the DORA metrics for delivery (DORA metrics).
AdvancedTrap: points-to-hours conversion and per-person velocity
The team grew from 5 to 8. What's next Sprint's capacity?

A five-person team averages 25 points. Three new developers join in India on Monday. The manager asks you to commit to 40 points (25 × 8 ÷ 5) for the next Sprint. What do you say?

Show answer

Plan for less than 25, not 40, for the first Sprint or two, and say why.

What’s happening
  1. New joiners spend their first Sprint mostly learning: environment setup, the codebase, the domain, the review standards. Their focused contribution is small (the capacity model above uses 0.4 for one joiner).
  2. Their onboarding takes time from existing people: buddies, reviews of first PRs, pairing, answering questions across the time zone. With three joiners at once, that cost lands on the experienced people who generate most of the 25.
  3. So the first Sprint's realistic capacity is often below the old 25. Brooks's law (Fred Brooks, The Mythical Man-Month, 1975), "adding manpower to a late software project makes it later", is the classic statement of this effect.
  4. Velocity will recover and then grow over several Sprints. Say so with numbers: "about 20 next Sprint, back to 25 in two Sprints, then we'll see the new baseline from real data". Measure it rather than assuming 8 ÷ 5.

#Kanban and when it fits better

Added

Kanban is a strategy for optimising the flow of value through a process: make the work visible, limit how much is in progress, and improve the flow using measurements. It has no Sprints, no roles and no fixed events. The current reference is The Kanban Guide (kanbanguides.org, edition of May 2025, by John Coleman and Daniel Vacanti, with seven other contributors). It defines three practices: defining and visualising a workflow, actively managing items in a workflow, and improving a workflow. It requires four flow measures: WIP (items started but not finished), throughput (items finished per unit of time), Work Item Age (how long a started item has been in progress) and cycle time (how long a finished item took from start to finish).

The mental model is a motorway. Its capacity is fixed by its lanes. Below capacity, more cars means more cars arrive. Once it's full, pushing more cars on doesn't get more cars to their destination per hour; it just makes every journey slower. A team works the same way: starting more items than the team can work on at once doesn't raise throughput, it raises cycle time. Little's Law from queueing theory states it exactly for a stable system: average cycle time = average WIP ÷ average throughput. (The Kanban Guide doesn't name Little's Law, but its required measures are exactly the three in it, plus age.)

Ready In progress · WIP 3 In review · WIP 2 Done UI-214 API-320 UI-216 UI-207 · age 5d API-298 · age 8d API-305 · age 1d full: finish before starting API-311 · age 2d free slot UI-201 API-290 pull Little's Law: average cycle time = average WIP ÷ average throughput same throughput, half the WIP → half the waiting
A pull system: a column takes new work only when it's under its limit. When "In progress" is full, the next useful thing is to help finish something, not to start something.

Little's Law sounds abstract until you watch it. This model runs the same three-person team on the same backlog (item sizes from a seeded generator) with three different WIP limits, for 300 simulated days, and measures after a 100-day warm-up:

littles-law.mjs
// Same team, same work, different WIP limits. Seeded, so every run prints the same numbers.
function mulberry32(seed) {
  return () => {
    seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

function run(wipLimit) {
  const random = mulberry32(7); // the same backlog of item sizes for every WIP limit
  const capacity = 3; // three developers: 3 days of effort per day, shared across everything in progress
  const inProgress = [];
  const finished = [];
  let wipSum = 0, measuredDays = 0;
  for (let day = 1; day <= 300; day++) {
    while (inProgress.length < wipLimit) inProgress.push({ left: 1 + Math.floor(random() * 5), start: day });
    if (day > 100) { wipSum += inProgress.length; measuredDays++; } // skip the warm-up
    const share = capacity / inProgress.length; // no context-switching cost at all, which flatters high WIP
    for (const item of inProgress) item.left -= share;
    for (let i = inProgress.length - 1; i >= 0; i--) {
      if (inProgress[i].left <= 1e-9) {
        const [item] = inProgress.splice(i, 1);
        if (day > 100) finished.push(day - item.start + 1);
      }
    }
  }
  const throughput = finished.length / measuredDays;
  const cycle = finished.reduce((a, b) => a + b, 0) / finished.length;
  const wip = wipSum / measuredDays;
  console.log(
    `WIP limit ${String(wipLimit).padStart(2)}: throughput ${throughput.toFixed(2)}/day, ` +
      `avg cycle time ${cycle.toFixed(1).padStart(4)} days, WIP ÷ throughput = ${(wip / throughput).toFixed(1).padStart(4)}`,
  );
}

for (const limit of [3, 6, 12]) run(limit);
Result
WIP limit  3: throughput 0.94/day, avg cycle time  3.2 days, WIP ÷ throughput =  3.2
WIP limit  6: throughput 0.95/day, avg cycle time  6.3 days, WIP ÷ throughput =  6.3
WIP limit 12: throughput 0.95/day, avg cycle time 12.5 days, WIP ÷ throughput = 12.6
What’s happening
  1. capacity is three developer-days of effort per day. Every in-progress item gets an equal share of it (share = capacity / inProgress.length), so with a WIP limit of 12 each item gets a quarter of a person per day.
  2. Item sizes are 1 to 5 days of effort from mulberry32(7), re-seeded for every run so all three WIP limits see exactly the same backlog in the same order.
  3. Throughput is the same in all three runs, about 0.95 items a day. That's the motorway's lane count: the team's capacity divided by the average item size (3 ÷ about 3.1, the average of this seeded backlog's sizes, which is just under 1). Starting more work didn't produce more finished work.
  4. Average cycle time scales with WIP: 3.2 days at a limit of 3, 6.3 at 6, 12.5 at 12. And WIP ÷ throughput predicts it almost exactly (3.2, 6.3, 12.6), which is Little's Law.
  5. This model is kind to high WIP: it charges nothing for context switching, waiting for answers across time zones or merge conflicts, all of which make real high-WIP teams slower still. Even with no switching cost, a customer's bug waits four times as long at WIP 12 as at WIP 3.
  6. So the lever for "things take too long" is usually less WIP, not more effort: stop starting, start finishing.
AdvancedService level expectations and Work Item Age

The Kanban Guide requires a service level expectation (SLE): a forecast of how long an item should take from start to finish, with a probability, based on historical cycle time ("85% of items finish within 6 days"). Then Work Item Age tells you, during the work, which items are at risk before they become late:

sle.mjs
// A service level expectation from past cycle times, then the board checked against it by Work Item Age.
// Cycle time and age are counted in calendar days, inclusive (started and finished the same day = 1 day).
const pastCycleTimes = [2, 3, 3, 4, 2, 6, 5, 3, 9, 4, 3, 7, 2, 4, 5, 3, 12, 4, 6, 3]; // last 20 items Done

const sorted = [...pastCycleTimes].sort((a, b) => a - b);
const percentile = (p) => sorted[Math.ceil((p / 100) * sorted.length) - 1];
const sle = percentile(85);
console.log(`50th percentile ${percentile(50)} days, 85th ${sle} days, 95th ${percentile(95)} days`);
console.log(`SLE: 85% of items finish within ${sle} days of starting`);

const today = Date.parse("2026-10-27T00:00:00Z");
const board = [
  { id: "API-311", column: "In review", started: "2026-10-26" },
  { id: "UI-207", column: "In progress", started: "2026-10-23" },
  { id: "API-298", column: "In progress", started: "2026-10-20" },
  { id: "UI-190", column: "In review", started: "2026-10-14" },
];
for (const item of board) {
  const age = Math.round((today - Date.parse(`${item.started}T00:00:00Z`)) / 86_400_000) + 1;
  const status = age > sle ? "PAST the SLE: swarm or split it" : age >= sle - 1 ? "close to the SLE: talk about it today" : "fine";
  console.log(`${item.id.padEnd(8)} ${item.column.padEnd(12)} age ${String(age).padStart(2)} days  ${status}`);
}
Result
50th percentile 4 days, 85th 6 days, 95th 9 days
SLE: 85% of items finish within 6 days of starting
API-311  In review    age  2 days  fine
UI-207   In progress  age  5 days  close to the SLE: talk about it today
API-298  In progress  age  8 days  PAST the SLE: swarm or split it
UI-190   In review    age 14 days  PAST the SLE: swarm or split it
What’s happening
  1. Twenty past cycle times sort to give percentiles: half the items finished within 4 days, 85% within 6, 95% within 9. The 12-day item is the outlier that a "6-day SLE" deliberately doesn't promise to cover.
  2. The SLE is the 85th percentile, stated with its probability: "85% of items finish within 6 days of starting". It's a forecast to the client and a trigger for the team, not a deadline per item.
  3. Age is counted inclusively from the start date to today (27 October 2026): API-311 started yesterday, age 2, fine. UI-207 is at 5 days, one day short of the SLE, so it's raised at today's Daily Scrum before it's late.
  4. API-298 (8 days) and UI-190 (14 days, sitting "In review") are past the SLE. The action isn't "work harder"; it's to swarm (two people on it), split it, or chase whatever it's waiting on. An item aging in review on a US–India team usually means it's waiting for a reviewer in the other time zone; see Small pull requests and fast reviews.
  5. Age is the most useful number on a Kanban board because it's about items still in progress, which you can still do something about. Cycle time is only known once it's too late to change.
AdvancedReading a cumulative flow diagram

A cumulative flow diagram (CFD) stacks, day by day, how many items have entered each state. Interviewers ask what its shapes mean. The same reading, done with numbers on illustrative data:

cfd.mjs
// Reading a cumulative flow diagram (CFD) as numbers. Illustrative data for ten working days.
// Each array is the cumulative count of items that had ENTERED that state by the end of each day.
const started  = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]; // entered "In progress"
const inReview = [1, 3, 5, 7, 9, 11, 12, 13, 14, 15];   // entered "In review"
const done     = [0, 1, 3, 5, 7, 9, 10, 10, 11, 11];    // entered "Done"

const bar = (n, ch) => ch.repeat(n);
console.log("day  WIP  in progress | in review  (band widths = items in each state)");
for (let d = 0; d < started.length; d++) {
  const inProgress = started[d] - inReview[d];
  const reviewing = inReview[d] - done[d];
  console.log(
    `${String(d + 1).padStart(3)}  ${String(started[d] - done[d]).padStart(3)}  ` +
      `${bar(inProgress, "▒").padEnd(12)}|${bar(reviewing, "█")}`,
  );
}

// The horizontal gap between the "started" and "done" lines approximates cycle time:
// the Nth item started on day s(N) and the Nth item finished on day f(N) (first in, first out).
const dayReached = (series, n) => series.findIndex((c) => c >= n) + 1;
for (const n of [5, 10]) {
  const s = dayReached(started, n), f = dayReached(done, n);
  console.log(`item #${n}: started by day ${s}, done by day ${f} → about ${f - s} days`);
}
const throughput = (done.at(-1) - done[4]) / 5, arrivals = (started.at(-1) - started[4]) / 5;
console.log(`last 5 days: ${arrivals} started/day vs ${throughput} done/day → WIP grows by ${arrivals - throughput}/day`);
Result
day  WIP  in progress | in review  (band widths = items in each state)
  1    4  ▒▒▒         |█
  2    5  ▒▒▒         |██
  3    5  ▒▒▒         |██
  4    5  ▒▒▒         |██
  5    5  ▒▒▒         |██
  6    5  ▒▒▒         |██
  7    6  ▒▒▒▒        |██
  8    8  ▒▒▒▒▒       |███
  9    9  ▒▒▒▒▒▒      |███
 10   11  ▒▒▒▒▒▒▒     |████
item #5: started by day 2, done by day 4 → about 2 days
item #10: started by day 4, done by day 7 → about 3 days
last 5 days: 2 started/day vs 0.8 done/day → WIP grows by 1.2/day
What’s happening
  1. Each series is cumulative: by the end of day 10, 22 items had been started, 15 had reached review and 11 were Done. The vertical distance between the "started" and "done" lines on any day is the WIP (22 − 11 = 11 on day 10), and the bands are the items sitting in each state.
  2. Days 2 to 6 are a healthy shape: WIP holds at 5, both bands keep the same width, and work leaves as fast as it arrives.
  3. From day 7, Done goes almost flat (10, 10, 11, 11) while starting continues at 2 a day. Both bands widen and WIP more than doubles, from 5 to 11. On a chart this looks like the bands fanning out; in words, "we keep starting, we've stopped finishing".
  4. The horizontal distance between the lines approximates cycle time (assuming items finish roughly in the order they started): item 5 took about 2 days, item 10 about 3, and by Little's Law the items started on days 8 to 10 will take far longer if nothing changes.
  5. The fix follows from the shape: stop starting (the WIP limit), find what stalled finishing (Done barely moves from day 7 and the review band grows from day 8: are reviewers away, or is the other time zone's reviewer the only one who can approve?), and swarm on review until the bands narrow again.
AdvancedScrum or Kanban: how to choose
SituationBetter fitWhy
Product feature work, a stakeholder group that wants a regular reviewScrumthe Sprint Goal and the Review give a cadence of commitment and feedback
Support queue, production bugs, device issues reported by the fieldKanbanwork arrives unpredictably; a two-week plan would be broken by day 2
Platform or ops team serving many other teamsKanbanthe job is flow and response time; an SLE describes it
Team new to agile working, unclear prioritiesScrumthe events force regular decisions and inspection
Mature Scrum team with a lot of unplanned workScrum with flow practiceskeep Sprints, add WIP limits, age and an expedite lane

They also combine. A Scrum team can visualise its Sprint Backlog as a Kanban board with WIP limits, track Work Item Age at the Daily Scrum, and forecast with throughput; nothing in the Scrum Guide forbids that. "Scrumban" is an informal name for mixtures like this; it isn't defined by either guide, so define what you mean if you use the word.

A practical pattern for a feature team that also owns production issues: reserve a fixed share of capacity (say 20%, set from the last few Sprints' real interrupt load) as an interrupt lane with its own WIP limit of one or two, outside the Sprint Goal. Interrupts go there, not into the Sprint plan. If the lane overflows two Sprints running, the share was wrong, or the product has a quality problem worth its own Sprint Goal.

AdvancedTrap: WIP limits that are never enforced
Halving the cycle time

A team's board shows on average 12 items in progress, and it finishes about 3 items a week. Customers complain that requests take a month. Without adding people, how could the team get to about two weeks?

Show answer

Limit WIP to about 6. By Little's Law, cycle time = WIP ÷ throughput: 12 ÷ 3 = 4 weeks now, 6 ÷ 3 = 2 weeks with half the WIP.

What’s happening
  1. Average cycle time = 12 items ÷ 3 items per week = 4 weeks, which matches the "a month" complaint.
  2. Throughput is set by the team's capacity and the item sizes, so it won't change much just by rearranging work. The lever left is WIP.
  3. Halving WIP to 6 gives 6 ÷ 3 = 2 weeks, with the same people and the same throughput, as in the simulation above.
  4. In practice throughput often rises a little too when WIP falls, because there's less context switching and items waiting for answers get chased. Little's Law holds for a stable system on average, so measure for a few weeks after the change rather than expecting it overnight.

#Shifting priorities and incomplete documentation

Added

This role's description says it plainly: priorities will shift and documentation will be incomplete. That's normal on a client engagement and on a device platform where firmware, web UI and APIs move at different speeds. The skill being tested is not tolerating ambiguity; it's turning it into explicit, cheap-to-change decisions so the team keeps moving without building on guesses nobody wrote down.

A good mental model is exploring with a half-drawn map. You mark what you've confirmed in ink, what you're assuming in pencil, and you send a scout ahead before you march the whole group down an unknown path. The danger isn't the blank parts of the map; it's pencil marks that everyone has started treating as ink. When priorities shift, the pencil marks are what you rub out, and you can only do that cleanly if you know which marks they are.

In practice that's four habits:

  • Write assumptions down where the work is, with an owner and a date by which each must be confirmed. An assumption log is the pencil layer of the map.
  • Separate one-way from two-way doors. Jeff Bezos's letter to Amazon shareholders for 2015 (published April 2016) describes "Type 1" decisions that are hard to reverse (a public API's shape, a database choice, a contract) and "Type 2" decisions that are easy to reverse (an internal module boundary, a UI layout behind a flag). Make Type 2 decisions quickly on an assumption; slow down and confirm before Type 1.
  • Buy information with timeboxed spikes (see Estimation and planning) instead of debating what the undocumented system probably does.
  • Absorb priority changes through the Product Owner and the Sprint Goal, never by quietly adding work.

Here's an assumption log for an illustrative project, a web UI for managing device settings where the device API's documentation is partial. This is the main tool; it lives in the repository (or the wiki the client can see), not in someone's head:

docs/assumptions.md
# Assumptions log: device settings web UI

| # | Assumption | Why we believe it | Risk if wrong | Confirm by | Owner | Status |
|---|---|---|---|---|---|---|
| A1 | The device API accepts PATCH for partial settings updates | Two endpoints we tested do; no docs | Medium: switch to GET + PUT | 2026-11-04 | Dev 2 | ✅ confirmed (spike SP-12) |
| A2 | Settings changes apply without a device reboot | Client's PM said so in the kickoff | High: UI needs a "restart required" flow | 2026-11-06 | Lead | ❓ asked client's firmware team, Q-7 |
| A3 | Admins manage at most ~500 devices per site | Sales deck figure | High: list needs server paging | 2026-11-06 | Lead | ❓ |
| A4 | English-only UI for the first release | Not in the brief either way | Low: strings are already externalised | 2026-11-20 | PO | 📝 decided by PO 2026-10-29 |
| A5 | Old firmware returns the same error shape | Seen on one device only | Medium: per-version error mapping | 2026-11-10 | Dev 4 | 🔍 testing on 3 firmware versions |
What’s happening
  1. Every row is something the team is building on but hasn't confirmed. Writing it down turns "I thought you knew" into a visible list the client can correct, which is the cheapest way to fix a wrong assumption: before the code exists.
  2. Why we believe it separates evidence (A1: two endpoints tested) from hearsay (A2: someone said so once). Hearsay with a high risk is the row to chase first.
  3. Risk if wrong says what changes, not just "high". "The UI needs a restart-required flow" is something a Product Owner can weigh; "high" alone isn't. Rows with a high risk and weak evidence (A2, A3) get confirmed before the team builds on them.
  4. Confirm by and Owner stop rows from living forever. A date before the dependent work starts, and one named person (not "the team"), who'll chase the client's firmware team for A2.
  5. Status links to how it was settled: a spike (SP-12), a question in the client's queue (Q-7), a Product Owner decision with its date. When a priority shifts next month and someone asks "why does the UI assume no reboot?", the answer is one click away.
  6. The log is also a negotiation tool. When the client reprioritises, you can say exactly which assumptions the new work depends on and which confirmed facts carry over, which turns "everything changed" into a short list of real changes.
AdvancedWhen an urgent request lands mid-Sprint

Shifting priorities usually arrive as "can you just squeeze this in?". The Scrum-compatible answer is a trade-off, made visible, decided by the Product Owner. This model (illustrative backlog and numbers) does the arithmetic the lead should do before replying:

mid-sprint-request.mjs
// An urgent request arrives on day 4 of a 10-day Sprint. What has to come out, and is the Sprint Goal still safe?
// Illustrative backlog. "goal" marks the items the Sprint Goal depends on.
const sprintGoal = "Admins can schedule a firmware update from the web UI";
const backlog = [
  { id: "FW-1", pts: 5, goal: true, state: "done" },
  { id: "FW-2", pts: 3, goal: true, state: "in progress" },
  { id: "FW-3", pts: 5, goal: true, state: "to do" },
  { id: "UI-9", pts: 3, goal: false, state: "to do" },
  { id: "UI-12", pts: 2, goal: false, state: "to do" },
  { id: "DOC-4", pts: 1, goal: false, state: "to do" },
];
const remainingCapacity = 14; // points the team expects to finish in the 6 days left (from the capacity plan)

function absorb(request) {
  const notDone = backlog.filter((i) => i.state !== "done");
  let load = notDone.reduce((s, i) => s + i.pts, 0) + request.pts;
  const out = [];
  // Drop not-started, non-goal work first, largest first; never drop goal work silently.
  for (const item of notDone.filter((i) => !i.goal && i.state === "to do").sort((a, b) => b.pts - a.pts)) {
    if (load <= remainingCapacity) break;
    out.push(item.id);
    load -= item.pts;
  }
  const safe = load <= remainingCapacity;
  console.log(`Request ${request.id} (${request.pts} pts): ${safe ? "fits" : "does NOT fit"} after removing ${out.join(", ") || "nothing"}`);
  console.log(`  load ${load} of ${remainingCapacity} pts → ${safe ? "Sprint Goal still achievable" : "Sprint Goal at risk: Product Owner decides"}`);
}

console.log(`Sprint Goal: ${sprintGoal}`);
absorb({ id: "BUG-77 (customer-facing login bug)", pts: 3 });
absorb({ id: "FEAT-5 (new client demo feature)", pts: 8 });
Result
Sprint Goal: Admins can schedule a firmware update from the web UI
Request BUG-77 (customer-facing login bug) (3 pts): fits after removing UI-9
  load 14 of 14 pts → Sprint Goal still achievable
Request FEAT-5 (new client demo feature) (8 pts): does NOT fit after removing UI-9, UI-12, DOC-4
  load 16 of 14 pts → Sprint Goal at risk: Product Owner decides
What’s happening
  1. The Sprint Goal is the anchor: "Admins can schedule a firmware update from the web UI". Items FW-1 to FW-3 serve it (goal: true); UI-9, UI-12 and DOC-4 are other useful work that happened to fit.
  2. On day 4 the remaining capacity is 14 points (from the capacity plan). The not-done work is FW-2 3 + FW-3 5 + UI-9 3 + UI-12 2 + DOC-4 1 = 14, so the Sprint is exactly full.
  3. The 3-point customer-facing login bug makes the load 17. Removing not-started, non-goal work largest first, taking out UI-9 (3) brings it back to 14, so it fits and the Sprint Goal is safe. The lead can say yes, and say what was traded: "we'll take BUG-77; UI-9 moves to next Sprint".
  4. The 8-point demo feature makes it 22. Even after removing all three non-goal items the load is 16 of 14. It can't fit without dropping goal work, so the reply is not yes or no; it's a decision for the Product Owner: keep the goal and do the demo feature next Sprint, or change (or cancel) the goal.
  5. The model never drops goal work by itself, on purpose: that's exactly the decision the Guide reserves for the Product Owner, and on a distributed team it has to be made in writing so the India developers see it at the start of their day.
AdvancedWorking with an undocumented system

When the documentation is missing or wrong (common with device APIs that changed across firmware versions), the system itself is the specification. A sequence that works:

  1. Ask once, in writing, with a deadline. One consolidated question list to the client's engineering contact ("Q-7: do settings changes need a reboot? We'll assume no if we don't hear by Thursday"), not ten chat messages. An explicit default keeps the team moving.
  2. Observe the real behaviour. Record actual requests and responses against a real device or the vendor's simulator, for each firmware version you must support. Save them as fixtures.
  3. Pin the behaviour with tests. Turn the fixtures into contract or characterisation tests (see Adding tests to a legacy codebase), so the day a firmware update changes the API, a test fails instead of a customer.
  4. Write down what you learned, in the repository, as you go: a short docs/device-api-notes.md with the endpoint, what it really returns, and the date and firmware version it was observed on. The next person, maybe in India at 11:00 their time with nobody to ask, starts from there.
  5. Feed corrections back. Send the client's team the notes; often they'll fix their docs, and you've built trust by being precise.

The habit to show in an interview is that you don't stop and you don't guess silently: you make the assumption explicit, put a default and a date on it, and reduce the risk with observation and tests.

AdvancedA question log for the client's engineering team

When documentation is incomplete, the questions you send the client's team are a work queue of their own. Ten scattered chat messages get two answers; one maintained log, reviewed in a weekly call, gets them all. The default column is what keeps the team moving while it waits:

docs/questions-for-client.md
| ID  | Question | Asked | Needed by | Default if no answer | Blocks | Answer (who, when) |
|-----|----------|-------|-----------|----------------------|--------|--------------------|
| Q-7 | Do settings changes apply without a device reboot? | 2026-10-28 | 2026-11-06 | Assume reboot may be needed; show "restart required" when the API says so | API-298, UI-207 | |
| Q-8 | Max devices per site for the first customers? | 2026-10-28 | 2026-11-06 | Build server-side paging | UI-220 | |
| Q-9 | Which firmware versions must the web UI support? | 2026-10-29 | 2026-11-13 | The last three releases | test matrix | Client PM, 2026-10-30: 9.8, 10.1, 10.2 |
What’s happening
  1. Each question is specific and answerable in a sentence by the right person. "How does the settings API work?" goes unanswered; "do settings changes apply without a reboot?" gets a yes or no.
  2. Needed by comes from the plan: the date the dependent work starts. It tells the client which questions are urgent without anyone having to say "ASAP".
  3. Default if no answer is the key column. The team proceeds on the default when the date passes, and the client knows it: silence becomes a decision rather than a delay. Choose the default that's safer to be wrong about (paging when the device count is unknown).
  4. Blocks links each question to tickets, so when an answer arrives you know exactly what to revisit, and the assumption log row (A2, A3 above) can be closed.
  5. The answer column records who answered and when. Six months later, when someone asks why the UI supports exactly three firmware versions, the reason is there.
AdvancedWhat to say when the priority changes

A short script for the conversation with a client stakeholder or Product Owner when priorities move. It works because it neither refuses nor silently absorbs the change:

Reply template: priority change
Thanks, understood: <new item> is now more important than <current goal / item>.
Impact if we switch now:
- <current item> stops at <state>; picking it up later costs about <time to resume>.
- The Sprint Goal "<goal>" won't be met this Sprint / is still achievable if we drop <items>.
- Assumptions this depends on: <A2, A3 from the log>, not yet confirmed.
Options:
1. Switch now: we start <new item> today (India team picks it up tomorrow morning IST).
2. Switch at the Sprint boundary on <date>: current goal finishes, <new item> starts then.
Recommendation: <1 or 2, one sentence why>. Product Owner to decide by <time, with time zone>.
What’s happening
  1. The first line acknowledges the change without arguing. Pushing back on whether priorities may change is a losing position; the client owns priorities.
  2. "Impact if we switch now" makes the cost concrete: work in progress stops in a known state, resuming has a cost, and the Sprint Goal is either at risk or not. This is the information the person changing the priority usually doesn't have.
  3. Listing the unconfirmed assumptions the new work depends on stops a rushed switch from building on pencil marks.
  4. Two options with dates, including the time zone consequence ("India team picks it up tomorrow morning IST"), and a recommendation. Then a decision owner and a deadline, so the change is decided, not drifted into.
  5. Sent in writing (and linked from the ticket), it's also the record of why the plan changed, which matters at the Sprint Review and when someone later asks why the original goal slipped.
AdvancedTrap: the silent scope swap
Which assumption do you confirm first?

Your assumption log has three unconfirmed rows: (a) "the UI can be English-only for launch", low risk, PO can decide any time; (b) "an admin manages at most ~500 devices", high risk, source is a sales deck; (c) "the API's error format is the same across firmware versions", medium risk, seen on one device. The team starts the device list screen tomorrow. Which do you chase first?

Show answer

(b), the device count, because it's high-risk, weakly evidenced, and the work that depends on it starts tomorrow.

What’s happening
  1. Rank by risk × weakness of evidence × how soon work depends on it. (a) is low risk and has no deadline pressure; it can wait for the Product Owner.
  2. (b) decides the design of the screen starting tomorrow: 500 devices can load in one request and filter in the browser; 50,000 need server-side paging, search and maybe virtualised rendering. Building the first and discovering the second is a rewrite.
  3. (c) matters, but it's medium risk and it's testable without anyone's help: run the same failing requests against three firmware versions. Give it to a developer as a small spike.
  4. So: message the client's contact today with a specific question and a default ("we'll build server-side paging unless you confirm under 1,000 devices per site by Thursday"), and start (c)'s spike in parallel. Choosing the safer default for a one-way-ish decision costs a little extra work and removes the rewrite risk.

#Running useful standups, refinement and retrospectives

Added

Each recurring event exists to inspect one thing and adapt it. The Daily Scrum inspects the plan for reaching the Sprint Goal; refinement inspects whether upcoming work is understood well enough to plan; the retrospective inspects how the team works. When one of them turns into a ritual that inspects nothing (a status round for the manager, a backlog read-aloud, a retro whose actions never happen), people stop valuing it, and on a distributed team they resent it, because someone is attending at 20:00 their time for no benefit.

The mental model is a pit stop. A pit stop is short, every person has one job, and the car leaves in a different state than it arrived (new tyres, more fuel). If the car leaves exactly as it arrived, the stop was wasted time, however well it was run. Judge each event by what changed because of it: a re-plan, a split item, an action that was actually done.

The Daily Scrum is the one that goes wrong most often. The 2017 Guide offered "three questions" as an example (what did I do yesterday, what will I do today, do I see any impediment), and the 2020 Guide dropped them as part of being less prescriptive. In practice they tend to produce a round of individual status reports. The Guide's actual requirement is that the Daily Scrum focuses on progress toward the Sprint Goal and produces an actionable plan for the next day. A format that does that is to walk the board from right to left, the items closest to Done first. Here's an agenda for a distributed team that meets in the overlap window, plus the written update the India developers post before it:

Daily Scrum: walk the board (15 minutes, in the overlap)
Before the call (India developers, by 18:30 IST; Austin developers, end of their day):
  Post in the team channel, per item you touched:
    UI-207 (In progress, age 5d): pagination done, sorting left. Blocked? No.
    API-298 (In progress, age 8d): waiting on Q-7 (reboot behaviour). Blocked: yes, since Tuesday.

In the call (08:00–08:15 Austin = 19:30–19:45 IST in winter):
1. Sprint Goal, read aloud (10 s): "Admins can schedule a firmware update from the web UI."
2. Walk the board right to left: In review → In progress → Ready.
   For each item: what will it take to move it one column today? Who helps?
3. Items past the SLE or blocked (from the written posts): decide an action and an owner now.
4. Is the Sprint Goal still on track? If not: who tells the Product Owner, today.
5. Anything longer than one minute → "after the call", with only the people who need it.
What’s happening
  1. The written posts move status out of the meeting. Everyone can read them in their own time, the call doesn't spend twelve minutes on "yesterday I…", and an India developer whose question appears in the post gets an answer in the call instead of in tomorrow's.
  2. Reading the Sprint Goal first takes ten seconds and frames every later question: progress is measured against the goal, not against how busy people were.
  3. Walking right to left (closest to Done first) puts finishing before starting. The question for each item is what it needs to move one column today, which surfaces "it's waiting for a review from Austin" and gets a reviewer assigned on the spot.
  4. Aged and blocked items get an action and an owner in the call (see Kanban and when it fits better for Work Item Age). A blocker raised and left until tomorrow costs the India developer a whole day.
  5. The goal check is where the Daily Scrum adapts: if the goal is at risk, someone tells the Product Owner today, not at the Review. Long discussions leave the call, so it stays inside 15 minutes and the India side can log off at 19:45.
AdvancedRefinement that makes Sprint Planning short

The Guide defines Product Backlog refinement as breaking down and further defining items "into smaller more precise items", an ongoing activity, with the Developers who'll do the work responsible for sizing. It sets no timebox in 2020 (the 2017 Guide said it "usually consumes no more than 10% of the capacity"). Two working rules: keep about one to two Sprints of items refined ahead, and treat Sprint Planning running long as a symptom of refinement not happening.

A readiness checklist the team agrees on (often called a "Definition of Ready"; it isn't in the Scrum Guide, so keep it light and never use it as a gate to refuse work from the Product Owner):

Ticket ready for Sprint Planning
- [ ] Title says the outcome: "Admins can filter devices by firmware version", not "Device filter"
- [ ] Why: who needs it and what it's for, in two lines
- [ ] Acceptance criteria as checkable statements (Given / When / Then where behaviour matters)
- [ ] Sized by the Developers, small enough to finish in a few days (else split it)
- [ ] Dependencies named: API endpoint exists? Design ready? Client decision needed?
- [ ] Open questions either answered or listed in the assumptions log with a default
- [ ] How it will be tested is clear (unit, contract, e2e, device-in-the-loop?)
What’s happening
  1. An outcome title is the cheapest readability win: a reader in another time zone knows what "done" means before opening the ticket.
  2. Acceptance criteria as checkable statements are what the developer builds and the reviewer verifies. Given/When/Then (from behaviour-driven development) works well when there's behaviour to pin: "Given 600 devices, when an admin filters by firmware 10.2, then only matching devices are listed and the count shows 'n of 600'".
  3. Small enough to finish in a few days keeps WIP and cycle time down. A good split slices through every layer (UI + API + test) rather than doing "all the API, then all the UI": a thin slice is demoable and Done; a layer isn't.
  4. Dependencies and open questions are where distributed teams lose days. If an item depends on a client decision that hasn't happened, it isn't ready, however well written it is.
  5. The INVEST criteria (Bill Wake, 2003: Independent, Negotiable, Valuable, Estimable, Small, Testable) are the classic version of this list; interviewers often like to hear the acronym.
AdvancedRetrospectives that change something

The Guide's purpose for the Retrospective is "to plan ways to increase quality and effectiveness": the team inspects individuals, interactions, processes, tools and its Definition of Done, and the most helpful changes are addressed as soon as possible, and may even go into the next Sprint Backlog. Two classic sources shape how good retros run:

  • Norm Kerth's Prime Directive (Project Retrospectives, 2001): the retro starts from the belief that everyone did the best job they could, given what they knew, their skills and abilities, the resources available and the situation. It points the discussion at the system, not at people, which is what makes honesty safe.
  • Esther Derby and Diana Larsen's five stages (Agile Retrospectives, 2006): set the stage, gather data, generate insights, decide what to do, close. Skipping "gather data" is the common failure: the loudest person's memory becomes the data.

Formats (start/stop/continue, 4Ls, sailboat) matter less than the actions. A useful habit is to track them across Sprints and look at follow-through. Illustrative data:

retro-actions.mjs
// Are retrospectives changing anything? Track their actions across Sprints. Illustrative data.
const retros = [
  { sprint: 21, actions: [
    { what: "Post the API contract before UI work starts", owner: "Lead", theme: "handoffs", done: true },
    { what: "Fix the flaky device-simulator test", owner: "Dev 2", theme: "ci", done: false },
    { what: "Shorter Daily Scrum", owner: "Team", theme: "meetings", done: false } ] },
  { sprint: 22, actions: [
    { what: "Fix the flaky device-simulator test", owner: "Dev 2", theme: "ci", done: false },
    { what: "Write a handoff note at the end of the India day", owner: "Dev 3", theme: "handoffs", done: true } ] },
  { sprint: 23, actions: [
    { what: "Fix the flaky device-simulator test", owner: "Dev 4", theme: "ci", done: true },
    { what: "Agree review turnaround: first review within one working day", owner: "Lead", theme: "reviews", done: true },
    { what: "Improve communication", owner: "Team", theme: "handoffs", done: false } ] },
];

let total = 0, done = 0;
const seen = new Map();
for (const r of retros) {
  for (const a of r.actions) {
    total++;
    if (a.done) done++;
    seen.set(a.what, (seen.get(a.what) ?? 0) + 1);
  }
}
console.log(`Actions done: ${done} of ${total} (${Math.round((done / total) * 100)}%)`);
for (const [what, n] of seen) if (n > 1) console.log(`In ${n} retros in a row: "${what}"`);

// "Team" owns nothing: an action needs one named owner.
const unowned = retros.flatMap((r) => r.actions).filter((a) => a.owner === "Team" && !a.done);
for (const a of unowned) console.log(`No single owner, and never done: "${a.what}"`);

const themes = {};
for (const r of retros) for (const t of new Set(r.actions.map((a) => a.theme))) themes[t] = (themes[t] ?? 0) + 1;
for (const [t, n] of Object.entries(themes)) if (n === retros.length) console.log(`Theme in every retro: ${t}`);
Result
Actions done: 4 of 8 (50%)
In 3 retros in a row: "Fix the flaky device-simulator test"
No single owner, and never done: "Shorter Daily Scrum"
No single owner, and never done: "Improve communication"
Theme in every retro: handoffs
Theme in every retro: ci
What’s happening
  1. Three retros produced eight actions; four were done (50%). That number alone is a good retro topic: a team that does half its actions should choose fewer, smaller ones.
  2. "Fix the flaky device-simulator test" appeared in three retros in a row, owned by Dev 2 twice and then Dev 4, and was finally done in Sprint 23. A repeated action means it never got time in a Sprint; the fix is to put it in the Sprint Backlog as an item, which the Guide explicitly allows.
  3. "Shorter Daily Scrum" and "Improve communication" were owned by "Team" and never done. An action needs one named owner and an observable change: "the Daily Scrum ends at 08:15; Dev 1 keeps time" or "an end-of-day handoff note in the channel by 19:00 IST", which is exactly what Sprint 22's done action looked like.
  4. Two themes, handoffs and CI, came up in every retro. Persistent themes are where the lead should invest outside the retro too: a handoff template (see Async communication that works), or a Sprint where fixing CI flakiness is part of the goal.
  5. Start the next retro by reviewing these numbers. It takes two minutes and is the single best signal that retros are worth attending.
AdvancedRunning these events across Austin and India
  • Daily Scrum in the overlap, written updates before it, 15 minutes hard stop. In winter (US standard time) the overlap with an India 11:00–20:00 day can be only 30 minutes, so the Daily Scrum is most of it; protect the rest for questions.
  • Refinement mostly async: the Product Owner or lead writes up items two or three days ahead, developers add questions and sizes in the ticket, and only disagreements go to a short live session.
  • Rotate the inconvenient slot for the long events. If the Review and Retro always sit at 20:00–21:30 IST, the India developers carry the cost of every Sprint; alternate with an early-morning Austin slot (07:00 CT is 17:30 or 18:30 IST depending on the US clock change).
  • Retro data collected in writing first (a shared board open for two days) so people who are quieter on calls, or working in a second language, contribute equally. Then the live session is for insights and decisions.
  • Record the Review and post the recording and decisions in writing within the day.
AdvancedTrap: a retro with the boss in the room
A Daily Scrum that's always 40 minutes

Your distributed team's Daily Scrum takes 40 minutes. It's at 08:00 in Austin, 19:30 in India. Each of eight people speaks for about four minutes about yesterday and today, and two technical discussions break out most days. What three changes would bring it under 15 minutes, and what's the risk if you don't?

Show answer

Written updates before the call, walk the board instead of going round the people, and move discussions to after the call. The risk is that the India developers stop engaging, or start their day late to compensate, because they're spending 40 minutes at the end of their working day on something that rarely helps them.

What’s happening
  1. Eight people × 4 minutes is 32 minutes of individual status before any discussion. Moving status into written posts removes almost all of it; people read them before the call.
  2. Walking the board, items closest to Done first, means the call talks about the few items that need help, not every person's day. Items moving fine take seconds.
  3. The two technical discussions are valuable, but not for all eight people. "After the call, with Dev 2 and Dev 4" keeps them and frees the rest.
  4. The time zone makes it costly: a 40-minute call starting at 19:30 IST ends at 20:10, past the end of the India working day. A 15-minute call with a clear plan respects that, and the overlap time left over goes to the questions that actually unblock people.
  5. Check the result after two Sprints: call length, how many blockers were resolved the same day, and what the team says in the retro.

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.