Gantt Guild

INC-2219: anatomy of a production incident, minute by minute

A feature flag reached 100% of a large SaaS platform at 13:40 UTC. Forty-eight minutes later, 12% of epic board views were failing. What happened, why four layers of defense lined up, and what a project manager should do in the room and after it.

A wall covered in colored sticky notes arranged as a plan

On Tuesday 3 August 2027, a change to the epic boards query reached every group on GitLab.com. It had done everything right on paper. It sat behind a feature flag. It had rolled out to 1% of groups on Monday and to 10% on Tuesday morning with no errors. At 13:40 UTC the flag went to 100%.

By 14:12 an alert fired. By 14:26 the incident was severity 2 and the public status page said degraded performance. By 14:40 the service was mitigated. By 15:20 it was resolved. Then the real work started: understanding why four separate defences had failed at once, and fixing every one of them.

INC-2219 is a case from Projects, For Real, and it's one of the scenarios our students run live in week 14. It's worth studying closely because almost every software incident looks like it: a reasonable change, a reasonable rollout, and a combination of small gaps that nobody had lined up before.

The setup

The change belonged to the Run the Engine team, a product group that keeps a large product area healthy while also building a new feature (Beacon) with the same people. The work, labeled E2, moved epic boards onto a new work-items API. Like most changes on a continuously deployed service, it shipped dark behind a feature flag and was switched on in steps.

INC-2219: Epic boards timing out for large groups
production tracker · severity::2 · declared 2027-08-03 14:18 UTC · mitigated 14:40 · resolved 15:20 · incident lead: Marta Kowalczyk (EOC) · responders: Fatima Zahra, Alex Kovalenko · comms lead: Tunde Bakare (CMOC)
severity2ServiceWebgroupproject managementincidentRootCauseFeature-Flag

Summary. Epic boards returned errors or timed out for groups with large numbers of epics and issues. About 12% of epic board page views failed during the incident. Cause: a new query path, enabled by feature flag at 13:40, performed a sequential scan on large namespaces because a supporting index was missing. Mitigated by disabling the flag.

The timeline

ExhibitINC-2219 timeline (UTC)

From the incident issue in the production tracker

TimeEvent
13:40Flag epic_boards_work_items_query enabled for 100% of groups (rollout issue #45070)
13:52Error rate on the epic boards endpoint begins rising as US morning traffic grows
14:12Alert: web apdex below SLO for the boards feature category (sustained 5 min)
14:18Engineer on call declares an incident, severity 3; incident channel and call open
14:21Support reports 4 tickets from large customers: epic boards unusable
14:26Severity raised to 2; communications lead engaged; status page: degraded performance, epic boards
14:31Fatima (pulled in by group escalation) correlates the start of errors with the flag change in the feature-flag log
14:33State → identified. Mitigation agreed: disable the flag
14:40Flag disabled via ChatOps. State → monitoring
14:46Error rate and apdex back to baseline
15:20State → resolved. Status page: operational. Review required (severity 2)

102 minutes from first errors to resolution; 48 minutes from first errors to mitigation, 20 of them before the alert fired. No data loss. Four support tickets; two customers asked for a root-cause analysis through their account teams.

32 mintime to detect, from the change
22 mintime to mitigate, from declaration
62 mintime to resolve, from declaration
12%of epic board views failed

What went right, and why it matters

A good review starts with what worked, because those are the things you must not lose when you fix the rest.

  • The change was behind a flag. Mitigation was one ChatOps command, not a revert and a redeploy. That single design choice probably saved an hour.
  • The feature-flag log existed. Fatima correlated the error curve with the flag change in under ten minutes of joining the call.
  • Severity moved when the facts moved. It was declared at 3, then raised to 2 as soon as customer impact was clear, which pulled in the communications lead and the status page.
  • The escalation path worked. The owning development team was paged in and on the call within 10 minutes.

Roles: who does what when the clock is running

The classic incident failure is that everyone investigates and nobody coordinates or communicates. GitLab's handbook describes three roles that prevent it. The incident lead owns the incident and drives it to resolution; every incident has one, set deliberately. Responders do the technical investigation and mitigation. A communications lead handles stakeholders, customers and the status page, required for severity 1 and 2. These are filled from on-call rotations: the engineer on call for first response and an incident manager on call for larger incidents.

Severity runs from 1 (critical) to 4 (low, an inconvenience with a workaround), and all severity 1 and 2 incidents need a review. The incident's state moves through investigating, active, identified, monitoring and resolved. Anything lasting more than five minutes is communicated publicly.

The blameless review

Incident reviews at GitLab are blameless: they focus on why and how the incident happened, never on who was at fault, so people share what they actually saw. Each review has a review initiator (usually the incident lead), a DRI from the team that owns the affected service (here Beacon's group, with the engineering manager Sam Adeyemi accountable), and a bar raiser: a peer from a panel of engineering managers, product managers and staff engineers who asks the hard questions and must approve the review before it closes. The handbook expects the review within five working days of resolution.

The bar raiser's questions are the useful part. Is the root cause clearly identified? Do the corrective actions match the type of root cause? Did we have the right observability? Did the change go through the right control flow? Did we miss it in testing? Could we have detected and mitigated faster? Where else could this happen? And one rule that cuts through a lot of theatre: if a code change caused the incident and none of the corrective actions make a similar defect less likely to escape again, the review isn't finished.

ExhibitINC-2219 review: five whys (excerpt)

Incident review issue · DRI: Sam Adeyemi · bar raiser: Henrik Dahl (Staff Engineer, another stage) · opened 3 Aug · closed 9 Aug

  1. Why did epic boards time out? The new query did a sequential scan on the work-items hierarchy table for namespaces with more than about 50,000 items.
  2. Why did it do a sequential scan? A composite index the new query needed didn't exist; the old query used a different access path.
  3. Why wasn't the missing index caught in review? The database review checked the query plan against a mid-sized group, where the planner chose an acceptable plan. Large namespaces weren't tested.
  4. Why didn't the 1% and 10% steps reveal it? The flag rolled out by percentage of groups, sampled at random. Fewer than 0.5% of groups are large, and none of the very largest landed in the 10% sample. Then 10% → 100% happened the same day.
  5. Why did that pattern seem safe? The rollout checklist was written for features whose load is spread evenly. Nobody had considered risk concentrated in a few very large namespaces.

Contributing factors: alerting on the aggregate boards category hid the epic board endpoint's degradation for 20 minutes; the 10% → 100% step left no time for the US morning peak to test the change.

Notice that every answer is about the system, not a person. Not “the reviewer missed it” but “the review checked a mid-sized group.” That isn't politeness. It's accuracy: the reviewer did what the process asked.

Four slices of Swiss cheese

The five whys implies one neat chain of causes. Real incidents usually have several. The Swiss cheese model explains INC-2219 better: four layers of defense existed, each had a hole, and on 3 August the holes lined up.

Code reviewNo plan checked for large namespaces
Database reviewQuery plan checked on a mid-sized group
Staged rolloutRandom sampling; 10% → 100% on the same day
AlertingAggregate alert hid one endpoint's spike

Corrective actions that actually prevent the next one

GitLab labels corrective actions, links them to the incident and gives them severity and priority labels matching the incident, which puts them under the same resolution targets as bugs of that severity. The handbook asks for SMART actions: not “investigate monitoring for this scenario” but “add alerting for all cases where this service returns more than 1% errors.”

ExhibitINC-2219 corrective actions

Issues labeled corrective action · severity::2, priority::2 · tracked in the group's milestone planning

#Corrective actionAddressesOwnerStatus
CA-1Add composite index for work-items hierarchy lookups by namespaceProximate causeFatima✓ Merged 9 Aug
CA-2Rollout template for flags on board and list finders: enable for the 20 largest namespaces first; monitor 24 hRollout samplingAlex✓ 18 Aug
CA-3Database review guideline: include a plan from the largest namespaceReview gapAlex + DB maintainers✓ 27 Aug
CA-4Endpoint-level apdex alert for epic and issue boards, with runbookDetection timeSam + Observability✓ 13 Sep
CA-5At least 24 h between rollout steps above 10%; one step must span a US and an EU peakSame-day jumpSam✓ 16 Aug
CA-6Check other finders migrated under E2 for the same patternBeyond this incidentFatima✓ 1 found, fixed

The set has one property worth copying: every slice of cheese gets its own action. CA-1 fixes this incident. CA-2, CA-3 and CA-5 fix the holes that let a whole class of defects through. CA-4 fixes detection. CA-6 goes looking for the same hole elsewhere, and finds one. That's what the bar raiser looked for before approving the review on 9 August.

The flag came back through a production change request (classified C3) on 10 to 13 August: the 20 largest namespaces first, then 10%, 50% and 100% over three days, with the metrics to watch and the rollback command written down. It reached 100% with p95 latency 40% lower than before the original change, thanks to the index.

What it cost the project

This is the part project managers miss. For the team, INC-2219 wasn't 102 minutes of degraded service. The review and corrective actions took one engineer most of a week and another several days, on top of a July security fix. Together, unplanned work took about 30% of the group's capacity for three weeks. The month's error budget was 96% spent. And the Beacon feature, due for general availability in release 20.4, had 34 weight of remaining scope against about 22 available.

The team held the date and cut scope from the bottom of the story map, through a formal commitment change request that took two days and no meetings. Three customers who had asked for the deferred capability heard about it from their account managers in time to plan.

The same discipline outside software

On 14 October, during the hypercare of a new expense system (Ledger), the first reimbursement file to payroll was rejected for employees in three countries. About 60 people would miss the payroll run unless it was fixed within two days. The PM treated it as a severity 2 hypercare incident: an incident issue, an owner, a workaround (accounts payable processed the payments manually by the deadline) and a root-cause analysis within five business days.

ExhibitLedger hypercare: fishbone analysis, reimbursement file rejected

Whiteboard session · effect: Workday rejected 61 reimbursement lines (JP, KR, CL)

CategoryPossible cause raisedVerified?
DataAmounts in zero-decimal currencies (JPY, KRW, CLP) sent with two decimals✓ Root cause
ProcessIntegration test cases used only USD, EUR, GBP, CAD✓ Contributing
TechnologyExport uses one global currency format✓ Contributing
RequirementsBRD said “amounts in local currency” without precision rules✓ Contributing
PeopleNew payroll analyst unfamiliar with the file✗ Not a factor
SuppliersVendor release changed the export✗ No changes

The first explanation offered in the room was the new payroll analyst. The fishbone's categories forced the group to check everything else, and the real causes were a data rule, a testing gap and an ambiguous requirement: three holes lining up again. Two of the three corrective actions changed templates that future projects will use. The incident became an improvement to the organization, not just a fix.

A checklist for project managers

  1. Know your project's severity definitions and who declares an incident.
  2. During an incident, don't investigate. Make sure there's an incident lead, help with communication, and protect the responders from interruptions.
  3. Afterwards, insist on a blameless review with a corrective action for each layer of defense that failed.
  4. Put the corrective actions into the plan with owners, dates and capacity.
  5. Ask: where else could this happen?

How students practice this

In week 14 every Gantt Guild student runs a 30-minute live incident simulation as incident lead, then writes the blameless review: timeline, five whys, Swiss cheese and SMART corrective actions. They also rebuild the incident's timing metrics from raw event logs in SQL.

Adapted from Projects, For Real, chapters 38 and 39. The projects, people, vendors and numbers are the book's fictional case studies, set against GitLab's publicly documented practices. Gantt Guild is not affiliated with GitLab Inc.

Operator Program · Cohort 1

Do this for free, for real

Every article here is a week of the Operator Program in miniature. In the program you write the documents yourself, in real tools, and get reviewed live.

The whole program is free. No tuition, no deposit, no income-share agreement, nothing owed if you leave.

Apply, it's free