Sprint burn-down chart
Agile — story points remaining vs. ideal
Example from the compendium of canonical charts
Python Code
"""Agile — Sprint burn-down chart (story points remaining vs. ideal)."""
import numpy as np
import plotly.graph_objects as go
def generate():
print("building sprint burn-down chart …")
rng = np.random.default_rng(133)
sprint_days = 10
total_points = 80
# Ideal burn: linear from total_points to 0
days = np.arange(0, sprint_days + 1)
ideal = total_points - total_points * days / sprint_days
# Actual burn: realistic — slow start, accelerates, then levels off
actual = [total_points]
remaining = float(total_points)
for d in range(1, sprint_days + 1):
# Burn rate increases in middle of sprint
if d <= 2:
burn = rng.uniform(3, 6)
elif d <= 7:
burn = rng.uniform(8, 14)
else:
burn = rng.uniform(4, 8)
remaining = max(0, remaining - burn)
actual.append(remaining)
actual = np.array(actual)
# Add scope change on day 4 (new stories added)
scope_day = 4
scope_added = 12
scope_remaining = actual.copy()
scope_remaining[scope_day:] += scope_added
fig = go.Figure()
# Ideal line
fig.add_trace(go.Scatter(
x=days, y=ideal,
mode="lines",
name="Ideal",
line=dict(color=MUTED, width=2, dash="dash"),
hovertemplate="Day %{x}: %{y:.0f} pts ideal<extra></extra>",
))
# Scope change area
fig.add_trace(go.Scatter(
x=days[scope_day:], y=scope_remaining[scope_day:],
mode="lines",
name="After scope change",
line=dict(color=TEAL, width=2, dash="dot"),
hovertemplate="Day %{x}: %{y:.0f} pts (post-scope)<extra></extra>",
))
# Actual remaining
fig.add_trace(go.Scatter(
x=days, y=actual,
mode="lines+markers",
name="Actual remaining",
line=dict(color=VIOLET, width=2.5),
marker=dict(size=8, color=VIOLET, line=dict(color="white", width=1.5)),
hovertemplate="Day %{x}: %{y:.0f} pts remaining<extra></extra>",
))
# Scope change annotation
fig.add_annotation(
x=scope_day, y=actual[scope_day] + scope_added / 2,
text=f"+{scope_added} pts added",
showarrow=True, arrowhead=2,
ax=50, ay=0,
font=dict(size=11, color=TEAL),
arrowcolor=TEAL,
bgcolor="rgba(255,255,255,0.85)",
bordercolor=TEAL,
)
# Sprint completion marker
done_pts = int(total_points - actual[-1])
fig.add_annotation(
x=sprint_days, y=actual[-1],
text=f"{done_pts} pts done",
showarrow=True, arrowhead=2,
ax=-50, ay=-30,
font=dict(size=11, color=GREEN),
arrowcolor=GREEN,
bgcolor="rgba(255,255,255,0.85)",
bordercolor=GREEN,
)
fig.update_layout(
xaxis=dict(
title="Sprint Day",
tickvals=list(range(sprint_days + 1)),
range=[-0.3, sprint_days + 0.5],
showgrid=True,
),
yaxis=dict(
title="Story Points Remaining",
range=[-5, total_points + scope_added + 5],
),
legend=dict(orientation="h", y=1.08),
margin=dict(t=50, b=50, l=70, r=40),
height=420,
)
return fig
if __name__ == "__main__":
generate()
Made with Plotly