Canberk Şenyüz

Quiet Hours

Client
SaaS platform team
Year
2023
Role
Software engineer
Stack
Go, PostgreSQL, Slack API
The project summary as it is stored: 101 bytes of UTF-8.

A rules engine that routes production alerts to whoever’s on call and holds the rest until morning.

Context

Every production alert paged the whole on-call rotation, including warnings nobody could act on at night. People started muting the channel, which is the one thing an alerting system cannot survive.

Idea

Routing rules became data owned by each team, with a quiet window per rule. Anything below critical inside that window is held and delivered in the morning. Before a team switches a rule on, a replay mode runs it against the last thirty days of real alerts.

RowTimeSeverityRuleDecision
000023:12warningpaymentshold until 08:00
001023:40criticalpaymentspage owner
002002:05infosearchhold until 08:00
003006:48warningsearchhold until 08:00
004008:15warningsearchpage owner
Replay output for one night on the payments and search rules. Only the selected alert would have woken anyone.

Details

  1. Rules as data

    Teams edit their own rules in a reviewed file. The engine loads them at startup and refuses to start on an invalid rule instead of guessing.

  2. Replay before rollout

    The same routing function runs over historical alerts and prints who would have been woken up, and when. Most rule changes were settled by reading that output.

  3. Critical always pages

    No quiet window can hold a critical alert. That rule lives in code, not configuration, and it has its own test.

func Route(a Alert, rules []Rule, now time.Time) Decision {	for _, r := range rules {		if !r.Matches(a) {			continue		}		if r.Quiet.Contains(now) && a.Severity < SeverityCritical {			return Hold(r.Owner, r.Quiet.End(now))		}		return Page(r.Owner)	}	return Page(DefaultOwner)}
Go The routing decision. Replay mode calls the same function.

Outcome

Rolled out one team at a time. Because every team saw its own replay first, nobody switched a rule on blind.