Kanban After 12 Months Of Interrupt-Driven Work
One year after replacing sprints, our platform team kept the limits and lost the theatre.
On 19 August 2025, we deleted the remaining two-week sprint from our platform board while seven tickets were still marked “in progress.” Two were waiting on product teams, one was a certificate renewal nobody had planned, and one was a fairly ordinary Terraform change that had somehow acquired four assignees.
We had spent six months pretending this was a planning problem. It was an interruption problem.
Our nine-person platform team supports 36 production services, a shared Kubernetes cluster, identity, CI runners, and the collection of internal tools that only become visible when they fail at 03:00. Sprints made the work look orderly for roughly a day and a half. Then an incident, an access request, or a release deadline arrived and the board became historical fiction.
We moved to kanban with a hard WIP limit, an expedite lane, and a promise to stop calling unfinished work “carryover” as if it had merely missed a train. One year later, we’d grade the decision a B. The board changed how we start work and how we argue. It also created chores we had not put on anybody’s capacity plan.
We Expected The Board To Expose Hidden Work
The expected gain was embarrassingly simple: show the work we were already doing.
Before kanban, an engineer could have a sprint ticket, two Slack requests, a deployment rollback, and a “quick look” at a customer’s IAM policy. Only the sprint ticket appeared in the report sent to leadership on Friday. We were measuring planned work against a week that contained a great deal of unplanned work, then acting surprised when the ratio looked bad.
We expected WIP limits to make people finish things before picking up more things. Our original rule was eight active standard tickets across the team, with one separate expedite slot. Eight felt generous. It was based on the number of engineers minus the people we assumed would be in meetings, which is not a calculation we’d recommend preserving in a museum.
We also expected the classes of service to stop being decorative labels. “Expedite” meant production impact or a security deadline with fewer than five days left. “Fixed date” meant a date imposed by something outside our team: a contract renewal, audit evidence, a vendor certificate expiry. Everything else entered the standard queue.
The Kanban Guide is admirably plain about starting with the current way of working. We took that seriously, perhaps because we did not have enough spare energy to redesign the department and install a new board in the same quarter.
Our old sprint plan behaved like a railway timetable after a signal failure: technically still present, increasingly irrelevant.
The first version had three columns: Ready, Doing, Done. We added Blocked on the second day because “Doing” was hiding work that no one could move. That should have told us something about the first version’s intellectual depth.
Tuesday Replenishment Changed More Than Stand-Up
What actually happened was quieter than we expected. Nobody stood up in a meeting and declared a new era of flow. The useful shift came on the first Tuesday when the Ready column held 19 tickets and Doing had reached its limit.
We had 11 active tickets. More precisely, we had 11 tickets marked active; three were waiting for a reply, two had not been touched since the previous week, and one was a task to “investigate service mesh options,” which is a sentence that should trigger an automatic timeout.
The team started asking a different question: what can we finish before taking this? That sounds obvious when written down. In practice, it changed the order of conversations. An engineer working on an OpenTofu module asked the service owner for the missing variable list before beginning a new request. Another closed a stale upgrade card after confirming the vendor had withdrawn the affected release. We stopped treating context switching as evidence of responsiveness.
Our daily check-in dropped from 25 minutes to around 11. We walk the board right to left, starting with blocked items and then work closest to done. We do not go person by person. The person-by-person version had become a polite recital of activity, and everyone knew it.
Throughput settled around 14 completed standard tickets per fortnight, though the number is lumpy enough that we do not use it for individual targets. In October, we completed 23 because several old requests were finally closed. In January, we completed six while rebuilding the GitHub Actions runner group after an image change broke our ARM jobs.
Little’s Law gave us a useful way to discuss that variation: more work in progress generally means longer time to finish, assuming the system is reasonably stable. The formula is short; the hard part is refusing to add “just one more” item when the board is full. The explanation of Little’s Law is more useful than most delivery dashboards we’ve purchased.
This only held because our engineering managers agreed not to insert work directly into Doing. They can request an expedite item. They cannot assign it a person and declare the WIP limit an inconvenience.
The Blocked Column Made Dependencies Public
The surprise nobody predicted was that blocked work would become mildly political.
A ticket marked Blocked tells a different story from a ticket left in Doing. It tells us the platform team is waiting on a security approval, a product decision, or a vendor reply. That visibility is useful, but it also names the place where work has stopped. Some teams welcomed it. One product group asked whether we could use a softer status because “blocked sounds accusatory.”
We did not.
We did change the policy. Every blocked ticket now needs a short reason, the name of the waiting party, and a next follow-up date. “Waiting on product” is not enough. “Waiting on Checkout to choose whether webhook retries should be at-least-once or deduplicated; follow up Thursday” is enough.
By November, the board showed an uncomfortable pattern. Access work was not slow because creating roles in Okta took long. It was slow because requesters submitted a ticket before deciding which people needed access and why. We had built a form that asked for an application name and environment, then left the actual approval chain to comments.
That led to a small but worthwhile change: access requests now require an owner and an expiry date before entering Ready. The number of access tickets did not fall. Their median elapsed time fell from 9.4 business days to 3.1, mostly because we stopped accepting half-formed requests.
The blocked data also gave us evidence during the February incident review. We could show that six “urgent” observability requests had waited on service owners to define an SLO. The Google SRE workbook’s alerting guidance says alerts should connect to a user-facing objective. We had been asking teams for exactly that, then allowing the cards to enter the queue without it.
There is still a social cost. People dislike seeing their team named in a blocked reason, even when the wording is factual. We have not found a better answer than being precise and not turning the board into a blame register.
The Board Needed Someone To Weed It
Nobody budgeted for board maintenance.
Not administration in the abstract. Actual maintenance: closing dead cards, correcting issue types, checking automations, chasing owners, and making sure a ticket moved to Done reflects a deployed change rather than a pull request waiting for approval.
For the first three months, GitHub Projects automation handled enough of this that we thought we had escaped the usual board janitor role. A pull request merge moved the linked issue to Done. New issues with the platform label entered Triage. An incident label put work into Expedite.
Then reality arrived. A merged pull request could deploy to staging and sit there for nine days. Incident tickets kept the label after the incident was closed. A request from Finance inherited platform because someone copied an old issue template. The board was accurate in the same way a kitchen drawer full of old cables is organised: there was a system, once.
Here is how the setup drifted over the year:
# 19 August 2025
workflow:
columns: [Ready, Doing, Done]
doing_wip_limit: 8
expedite_wip_limit: 1
done_when: "pull request merged"
automation:
label_platform: "add to Ready"
label_incident: "move to Doing"
# 19 August 2026
workflow:
columns: [Triage, Ready, Doing, Blocked, Verify, Done]
doing_wip_limit: 6
expedite_wip_limit: 1
blocked_requires: [reason, waiting_on, follow_up_date]
done_when: "production verification recorded"
policies:
triage_owner_rotation: weekly
stale_ready_after_days: 30
expedite_requires: "active customer impact or security deadline under 5 days"
automation:
label_platform: "add to Triage"
label_incident: "move to Expedite"
merged_pull_request: "move to Verify"
verified_production: "move to Done"
We now rotate a weekly triage owner. It takes about 35 minutes on a normal week and longer after an incident. The owner checks new work, pings stalled cards, and closes tickets that have become archaeology. Nobody loves the rota, but it is far less annoying than discovering during replenishment that Ready contains a request for Ubuntu 20.04 package support from last winter.
Our on-call rota deserves a brief mention here. We stopped making the on-call engineer triage owner after one week in September, when the same person had a noisy Kafka page at 02:17 and a backlog of 14 intake tickets by breakfast. The board can wait. A tired engineer should not have to decide whether a request is “standard” or “fixed date” before coffee.
We Kept Limits And Replenishment, Not Velocity Charts
The pieces we kept are the ones that force a decision.
The Doing limit is now six, not eight. We lowered it after measuring elapsed time and noticing that work got stuck at six anyway; the extra two slots were simply permission for two more partially started tasks. Six feels restrictive on Monday morning. By Thursday, it usually feels sensible.
We also kept twice-weekly replenishment. Tuesday and Friday, 20 minutes each, with the triage owner and whichever engineers are free. We pull enough Ready work to keep the board supplied, not enough to make everyone feel booked. We do not estimate every card. If a task is clearly larger than about five working days, we split it or write a short discovery card with a specific decision as its exit condition.
Our preference is blunt: we dislike story points for platform work. Giving an S3 lifecycle policy a five and a Kubernetes minor upgrade a thirteen produced elaborate certainty theatre. Elapsed time, blocked age, and a short list of current work tell us more.
We retained a monthly service-delivery review, although we cut the slide deck from 18 slides to four screenshots and a list of the oldest blocked items. We look at completion times by work type, not individual engineer. The point is to see that fixed-date work is clogging the queue or that access requests are incomplete, not to invent a ranking system for people who happened to draw the ugly tickets.
We have not tried this past 40 nodes or across multiple platform teams. Our cluster has 17 worker nodes and one team owns the board. At a larger scale, shared services and dependency boards may require a different arrangement. We are not claiming a magic ceiling; we simply do not have evidence beyond our own rather manageable mess.
We Ripped Out Swimlanes And Deadline Colours
We removed the swimlanes.
At one point the board had lanes for Kubernetes, CI, cloud accounts, observability, security, and “team enablement.” Cards duplicated across them, work vanished below the fold, and nobody could tell whether the WIP limit applied to the lane or the column. A board that needs a legend has already lost an argument with its users.
We also removed due-date colours. Red cards encouraged people to work on the nearest date even when the date was invented by a requester who wanted attention. Fixed-date work now has a documented external deadline and a reason. A request for “this sprint if possible” is standard work, even if the ticket title contains three exclamation marks.
The final thing we ripped out was the rule that every engineer must always have a card assigned. Pairing on a thorny rollout, reviewing a production verification, or helping the on-call engineer is work. It does not become more real because a board has somebody’s avatar attached.
One argument remains unresolved. Security remediation gets its own expedite slot when there is an active exploit, but we still disagree about high-severity findings with no known exploit. Half the team wants a second reserved slot. The rest of us think that would quietly become a permanent bypass around the limit. The next critical CVE will probably settle it for us.



