Somewhere in your stack there is a rule that decides whether a human hears when payments start failing. It is probably not in your repo. It's a toggle in a dashboard, set months ago by someone who may not work there anymore, and the only way to find out what it does is to log in and look. Notifications as code is the argument that this rule is production config, and deserves to live where production config lives: in git.
Not a radical argument. Just one that stopped short of the place it matters most.
the config nobody can review
A dashboard change has no diff. Someone tightens a filter, mutes a channel, reroutes an alert, and there is no PR where a colleague could have said wait, that's the only alert we have on dunning. There's no blame when the postmortem asks why nobody was told, no revert when the change turns out wrong, no way to see what the rules were last March when the numbers looked strange. The change is applied the moment it's made, invisibly, to production.
And the knowledge rots. Why does #billing skip renewals? Why is the signup alert quiet on weekends? The answer lives in whoever clicked the toggle, which means the answer is archaeology: ask around, log in, squint at eight screens of settings, and hope staging was clicked the same way as prod. It never was.
infra already learned this lesson
Infrastructure went through this exact arc and came out the other side. Nobody serious edits production DNS in a web console anymore; it's Terraform, in a repo, behind review. Alerting on metrics made the same journey: Prometheus rules are files, monitors are code, and “alerts as code” is settled practice for CPU, latency, and error rates.
But it stopped at the metrics layer. Product events (the signup, the failed invoice, the job that died) got routed through clicky tools instead, because they didn't look like infrastructure. They are. The routing of those events decides what your team learns about production and when. That's as load-bearing as config gets, and it's the one kind still being edited live, by hand, with no witnesses.
notifications as code, concretely
The alternative is one file per pipeline, in the repo, stating the whole rule in one place: what comes in, what happens to it, where it lands, and under what conditions. Here's the one for failed payments, as the diagram or as the file. Flip between them.
representation
01source
02pipeline · 3 steps
- 01CTLdedupekey $event.payload.customer · within 24h
- 02ENRenrich.company$event.payload.customer_email → company
- 03CTLthrottlemax 5 per 1h · key billing-alerts
03destinations · 2
- toslackSlackchannel#billingwhen$event.payload.attempt_count >= 3
- towarehouse.pgPostgrestableevents.payment_failures
Everything a reviewer would want to interrogate is on the page. The dedupe means one story per customer, not one per retry. The throttle caps how hard the channel can be hit when a bad deploy fails everyone at once. The when keeps attempts one and two away from humans, because they usually recover on their own. And every attempt lands in Postgres, including the quiet ones. None of it is hiding in a modal.
the pr that turns down the noise
# PR #482 · "billing alerts: one page per hour is plenty"
- throttle:
key: billing-alerts
- max: 20
- per: 5m
+ max: 5
+ per: 1hThis is what changing an alert rule should look like. The reviewer can see precisely what goes quiet, and can object before the channel goes silent instead of in the postmortem after. The commit message holds the reasoning. git blame answers “why five an hour?” forever, and git revert un-mutes the channel in one command when the quiet turns out to be a mistake.
There are quieter wins too. The PR that adds a new event can carry its routing in the same diff, so nothing ships unheard. Staging and prod stop drifting, because they're the same file. And the new teammate learns what the team gets told about production by reading a directory, not by shadowing whoever holds the dashboard password.
how ingestlayer does this
In ingestlayer, every pipeline is exactly this file: YAML in your repo, checked in next to the code that emits the events. dedupe, throttle, classify, and the rest are typed actions, so the review is of a real schema, not free-form config. The noise moves and the fan-out you'd design in the file are the same ones the dashboard-shaped tools bury in settings; here they just have a diff.
Put your first pipeline in your repo. The quickstart is a YAML file and one webhook away.