{"id":40,"date":"2025-12-09T11:04:41","date_gmt":"2025-12-09T11:04:41","guid":{"rendered":"https:\/\/steadyrabbit.in\/blogs\/?p=40"},"modified":"2025-12-09T11:05:59","modified_gmt":"2025-12-09T11:05:59","slug":"six-leading-indicators-that-predict-sprint-failure-two-weeks-before-it-happens","status":"publish","type":"post","link":"https:\/\/steadyrabbit.in\/blogs\/six-leading-indicators-that-predict-sprint-failure-two-weeks-before-it-happens\/","title":{"rendered":"Six Leading Indicators That Predict Sprint Failure\u2014Two Weeks Before It Happens"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Velocity\u201d tells you last sprint\u2019s weather when you need tomorrow\u2019s forecast. We share the six <strong>leading indicators<\/strong> our Micro-GCC squads feed into <strong>SteadCAST<\/strong> to flag scope creep, burnout, tech-debt drift, and pipeline drag up to <strong>two sprints<\/strong> before they explode. Copy-paste JQL \/ SQL queries included, plus a Grafana dashboard JSON you can import in five minutes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Velocity Is a Rear-View Mirror\u00a0<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Velocity answers one question: <em>\u201cHow many story-points did we ship?\u201d<\/em><em><br><\/em> Great for history, useless for the future\u2014because it can be high <strong>and<\/strong> unhealthy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>40 SP velocity hides that half the backlog was defect re-work.<br><\/li>\n\n\n\n<li>Surge of story completions could be burnout peaking.<br><\/li>\n\n\n\n<li>New dependencies balloon build times, masked by \u201csame\u201d velocity.<br><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Predictability demands <strong>forward-looking signals<\/strong>\u2014leading indicators that move <strong>before<\/strong> deadlines slip.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Six Indicators (Overview Table, \u2248 220 w)<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>#<\/strong><\/td><td><strong>Indicator<\/strong><\/td><td><strong>Why It Predicts Failure<\/strong><\/td><td><strong>Target<\/strong><\/td><\/tr><tr><td>1<\/td><td><strong>Risk-High WIP %<\/strong><\/td><td>Too many high-risk stories \u2192 re-work spike<\/td><td>\u2264 25 % of sprint cards<\/td><\/tr><tr><td>2<\/td><td><strong>Time-to-First-Review (TTFR)<\/strong><\/td><td>Review wait \u27f9 merge pile-up, QA squeeze<\/td><td>&lt; 2 h median<\/td><\/tr><tr><td>3<\/td><td><strong>Build Time Delta<\/strong><\/td><td>CI drifting \u27f9 less iteration, dev frustration<\/td><td>\u2264 10 % MoM increase<\/td><\/tr><tr><td>4<\/td><td><strong>SBOM Size Delta<\/strong><\/td><td>Growing dep tree \u2192 bigger attack surface, perf hit<\/td><td>\u2264 5 % per sprint<\/td><\/tr><tr><td>5<\/td><td><strong>Code Re-open Rate<\/strong><\/td><td>Stories re-opened in sprint \u27f9 spec or quality gap<\/td><td>&lt; 2 re-opens\/sprint<\/td><\/tr><tr><td>6<\/td><td><strong>Unplanned Work %<\/strong><\/td><td>Prod bugs &amp; urgent asks hijack sprint<\/td><td>\u2264 10 % of capacity<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">SteadCAST ingests these via Jira API, GitHub Actions, and CI logs\u2014alerting amber\/red in Slack every Friday.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Capture Each Metric\u00a0<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 Risk-High WIP %<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Process<\/em><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Add \u201crisk-high\u201d label during Plan-Left risk-matrix.<br><\/li>\n\n\n\n<li>Daily cron queries Jira:<br><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">JQL<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">project = ABC AND sprint in openSprints() AND labels = risk-high<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Formula<\/em> = (# risk-high cards in sprint) \u00f7 (total cards).<br>SteadCAST threshold: amber at 25 %, red at 35 %.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 Time-to-First-Review (TTFR)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>GitHub GraphQL<\/em>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">graphql<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">{<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;repository(name:&#8221;app&#8221;, owner:&#8221;org&#8221;) {<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;pullRequests(last:20){<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;edges{<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node{<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createdAt<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reviews(first:1){ edges{ node{ createdAt } } }<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Compute median \u2206 between PR create and first review.<br>Pulse every hour; ping #dev-ops if &gt; 2 h.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3.3 Build Time Delta<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">CI logs upload to S3. Athena query compares week-over-week average:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">sql<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WITH w1 AS (SELECT avg(duration) d FROM builds WHERE week = current_week-1),<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;w2 AS (SELECT avg(duration) d FROM builds WHERE week = current_week)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SELECT (w2.d &#8211; w1.d)\/w1.d AS delta<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Amber at 10 %, red at 20 %.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3.4 SBOM Size Delta<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">CycloneDX JSON keyed by hash. Lambda compares component count versus last green build; pushes metric to CloudWatch.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3.5 Code Re-open Rate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Jira workflow places \u201cRe-opened\u201d status. Query last sprint:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">JQL<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">status CHANGED TO &#8220;Re-opened&#8221; DURING (startOfSprint(), endOfSprint())<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3.6 Unplanned Work %<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Tag hot-fixes &amp; BAU as \u201cunplanned.\u201d Capacity = SP committed.<br>SteadCAST shows bar chart: planned vs. unplanned burndown.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Grafana Dashboard JSON (copy block, \u2248 90 w)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>(Truncated for brevity\u2014provide file download in resource center)<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">json<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">{<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&#8220;panels&#8221;:[<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;{&#8220;title&#8221;:&#8221;Risk High WIP %&#8221;,&#8221;type&#8221;:&#8221;gauge&#8221;,&#8221;targets&#8221;:[{&#8220;expr&#8221;:&#8221;risk_high_wip_percent&#8221;}]},<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;{&#8220;title&#8221;:&#8221;TTFR Median (h)&#8221;,&#8221;type&#8221;:&#8221;timeseries&#8221;,&#8221;targets&#8221;:[{&#8220;expr&#8221;:&#8221;ttfr_seconds\/3600&#8243;}]},<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;{&#8220;title&#8221;:&#8221;Build Time \u0394&#8221;,&#8221;type&#8221;:&#8221;stat&#8221;,&#8221;targets&#8221;:[{&#8220;expr&#8221;:&#8221;build_time_delta&#8221;}]}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Import \u2192 set Prometheus data-source \u2192 watch live metrics.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Real-World Case Study (Retail Client)\u00a0<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Before indicators<\/em>: sprint rollover 34 %, two hot-fix Fridays per month.<br><em>After 3 sprints<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Metric<\/strong><\/td><td><strong>Baseline<\/strong><\/td><td><strong>3 Sprints Later<\/strong><\/td><\/tr><tr><td>Risk-High WIP %<\/td><td>40 %<\/td><td>18 %<\/td><\/tr><tr><td>TTFR<\/td><td>5.3 h<\/td><td>1.7 h<\/td><\/tr><tr><td>Build Time<\/td><td>+18 % MoM<\/td><td>\u20132 %<\/td><\/tr><tr><td>Hot-fixes<\/td><td>2 \/ mo<\/td><td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Velocity went <strong>down<\/strong> 5 % (fewer \u201ceasy points\u201d), yet predictability soared\u2014zero missed releases in next six months.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u00a0Pitfalls &amp; Pro Tips\u00a0<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Pitfall<\/strong><\/td><td><strong>Fix<\/strong><\/td><\/tr><tr><td>Noisy alerts<\/td><td>Use amber \u2192 Slack, red \u2192 PR block; silence during retro.<\/td><\/tr><tr><td>Label drift (\u201crisk-high\u201d missing)<\/td><td>Jira automation: flag \u2265 8 SP stories as risk-high by default.<\/td><\/tr><tr><td>Developer gaming metrics<\/td><td>Make dashboard public; add dev-voted metric of the month.<\/td><\/tr><tr><td>Over-focusing on one indicator<\/td><td>Dashboard shows composite \u201cPredictability Score\u201d (weighted).<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Adoption Roadmap\u00a0<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Sprint<\/strong><\/td><td><strong>Add Indicator<\/strong><\/td><\/tr><tr><td>1<\/td><td>TTFR &amp; Build Time \u0394<\/td><\/tr><tr><td>2<\/td><td>Risk-High WIP % &amp; Code Re-open<\/td><\/tr><tr><td>3<\/td><td>SBOM Size \u0394<\/td><\/tr><tr><td>4<\/td><td>Unplanned Work % &amp; composite score<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Hold \u201cPredictability Retro\u201d month-end; kill one amber metric per retro.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Take-Home Checklist\u00a0<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Add risk-high labels &amp; DoR.<br><\/li>\n\n\n\n<li>Query TTFR via GitHub GraphQL.<br><\/li>\n\n\n\n<li>Track build \u0394 using CI logs \u2192 Prometheus.<br><\/li>\n\n\n\n<li>Diff SBOM component counts.<br><\/li>\n\n\n\n<li>Surface metrics in Grafana &amp; Slack.<br><\/li>\n\n\n\n<li>Retro monthly\u2014focus on amber trend.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Velocity\u201d tells you last sprint\u2019s weather when you need tomorrow\u2019s forecast. We share the six leading indicators our Micro-GCC squads feed into SteadCAST to flag scope creep, burnout, tech-debt drift, and pipeline drag up to two sprints before they explode. Copy-paste JQL \/ SQL queries included, plus a Grafana dashboard JSON you can import in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":15,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-40","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-shift-left-engineering"],"_links":{"self":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/40","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/comments?post=40"}],"version-history":[{"count":2,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/40\/revisions"}],"predecessor-version":[{"id":42,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/40\/revisions\/42"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/media\/15"}],"wp:attachment":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/media?parent=40"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/categories?post=40"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/tags?post=40"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}