The Sprint Cycle
Sprint ring with 5 phase boxes. Press Start Sprint to run the cycle — a dot travels around the ring and story cubes drop in. Press Show Velocity to see sprint-over-sprint improvement.
Code example
A sprint planning ticket structure and a simple Kanban board state machine.
// Sprint planning ticket — Linear/Jira compatible structure
const sprintTicket = {
id: 'ENG-347',
title: 'Salary filter on job listings',
type: 'story', // story | bug | task | spike
points: 3, // relative effort, not hours
priority: 'high',
sprint: {
name: 'Sprint 14',
start: '2026-07-07',
end: '2026-07-18',
goal: 'Improve job seeker filtering experience',
},
// Scrum ceremonies this ticket touches
planning: {
// Team committed to this in sprint planning
committed_by: ['@kai', '@maya'],
blockers: [],
},
// Updated in daily standup (15 min, same time, standing)
standup: {
yesterday: 'Implemented filter UI component',
today: 'Writing unit tests for edge cases',
blocked: false,
},
// Reviewed in sprint review (demo to stakeholders)
review: {
demoed: true,
accepted: true, // PO accepted against acceptance criteria
feedback: 'Add a max salary range in next sprint',
},
// Retro: what worked, what didn't, one change next sprint
retro_action: 'Add test coverage check to PR template',
};
// Velocity = story points completed per sprint
// Track over 4+ sprints to get a reliable average
const velocity = [18, 22, 21, 26, 28]; // improving over time
const avgVelocity = velocity.slice(-3).reduce((a,b)=>a+b,0)/3;
# Kanban board state machine
# Kanban = continuous flow; no sprints, no time-boxes
# Good for: support queues, ops work, bug triage
from enum import Enum, auto
class State(Enum):
BACKLOG = auto()
READY = auto() # refined, estimated, acceptance criteria written
IN_PROGRESS= auto()
IN_REVIEW = auto() # PR open, tests green
DONE = auto()
TRANSITIONS = {
State.BACKLOG: [State.READY],
State.READY: [State.IN_PROGRESS, State.BACKLOG],
State.IN_PROGRESS: [State.IN_REVIEW, State.READY],
State.IN_REVIEW: [State.DONE, State.IN_PROGRESS],
State.DONE: [],
}
# WIP (Work-In-Progress) limits prevent context switching
WIP_LIMITS = {
State.IN_PROGRESS: 3, # max 3 tickets in flight per person
State.IN_REVIEW: 2,
}
def move(ticket, new_state, board):
allowed = TRANSITIONS[ticket.state]
if new_state not in allowed:
raise ValueError(f"Invalid transition {ticket.state} ? {new_state}")
wip = board.count(new_state)
limit = WIP_LIMITS.get(new_state)
if limit and wip >= limit:
raise ValueError(f"WIP limit reached for {new_state.name}")
ticket.state = new_state
return ticket
# Scrum vs Kanban key difference:
# Scrum = time-boxed sprints, fixed team, planned commitment
# Kanban = continuous flow, WIP limits, measure cycle time
Key concepts
Sprint
A fixed-length iteration (usually 2 weeks) with a clear goal. The sprint goal should describe the business outcome — "improve onboarding" not "build feature X." At the end, something shippable must exist.
Backlog
A prioritized list of all planned work. The Product Owner owns it. "Grooming" (or refinement) is the ongoing process of adding detail, clarifying acceptance criteria, and estimating upcoming stories before sprint planning.
Retrospective
After every sprint, the team reflects. Three questions: What went well? What didn't? What's the one thing we'll change next sprint? The retro action item is a commitment, not a wish list.
Velocity
Story points completed per sprint. Don't compare velocity between teams — it's a relative measure calibrated to your team's own estimation scale. Use the 3-sprint rolling average to forecast how much fits in the next sprint.