<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samson Tanimawo</title>
    <description>The latest articles on DEV Community by Samson Tanimawo (@samson_tanimawo).</description>
    <link>https://dev.to/samson_tanimawo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3830227%2F02ea1ab7-513f-4426-b63d-9120142bc431.png</url>
      <title>DEV Community: Samson Tanimawo</title>
      <link>https://dev.to/samson_tanimawo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samson_tanimawo"/>
    <language>en</language>
    <item>
      <title>From DevOps to SRE: Making the Transition</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Wed, 22 Jul 2026 22:39:19 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/from-devops-to-sre-making-the-transition-4km9</link>
      <guid>https://dev.to/samson_tanimawo/from-devops-to-sre-making-the-transition-4km9</guid>
      <description>&lt;p&gt;I moved from a DevOps title to an SRE title about 6 years ago. On paper, they look similar. In practice, the mindset is different. Here's what actually changed for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevOps: 'how do we ship faster?'
&lt;/h2&gt;

&lt;h2&gt;
  
  
  SRE: 'how do we ship faster without breaking things?'
&lt;/h2&gt;

&lt;p&gt;DevOps is a culture and a set of practices for getting software from code to production quickly. SRE adds a discipline of reliability as a first-class concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed for me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I stopped being a YES person on deploys.&lt;/strong&gt; As a DevOps engineer, I was judged on deployment frequency. As an SRE, I was judged on reliability too. Sometimes the right answer to 'can we deploy this now?' is 'no, not until we have a rollback plan.'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I started thinking in error budgets.&lt;/strong&gt; Not 'is the site up?' but 'have we used too much of our error budget this quarter?' That frame changes everything about how you prioritize work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I stopped fixing things that weren't my job.&lt;/strong&gt; SREs are supposed to push work back to product teams when those teams' code is the problem. The first few times you say 'this is not an infrastructure issue, please fix your code,' it feels rude. Then you realize that fixing it for them is the trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skill gap
&lt;/h2&gt;

&lt;p&gt;If you're making this transition, the skills you probably need to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statistics (for SLO math and anomaly detection)&lt;/li&gt;
&lt;li&gt;Deep debugging of production systems (not dev)&lt;/li&gt;
&lt;li&gt;Writing post-mortems that actually change systems&lt;/li&gt;
&lt;li&gt;Saying no to feature work when the reliability budget is spent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hardest part isn't learning tools. It's accepting that reliability is an ongoing discipline, not a project that ends.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>career</category>
    </item>
    <item>
      <title>The SRE Interview: Questions I Actually Ask</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Wed, 22 Jul 2026 13:49:15 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/the-sre-interview-questions-i-actually-ask-5ffk</link>
      <guid>https://dev.to/samson_tanimawo/the-sre-interview-questions-i-actually-ask-5ffk</guid>
      <description>&lt;p&gt;I've interviewed maybe 300 SRE candidates over the years. Most interviews are bad. Leetcode for people who don't write production code. System design questions ripped off from a blog.&lt;/p&gt;

&lt;p&gt;Here are the questions I actually ask, and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  'Walk me through your last bad incident'
&lt;/h2&gt;

&lt;p&gt;The best SREs can tell you about incidents with specificity. They remember the graphs, the timeline, the moment they realized what was wrong, and what they'd do differently. Vague answers mean they weren't really there.&lt;/p&gt;

&lt;h2&gt;
  
  
  'Your on-call phone rings at 3 AM. Walk me through the first 10 minutes'
&lt;/h2&gt;

&lt;p&gt;Reveals process discipline. Good answers: acknowledge → assess impact → communicate → start diagnosis. Bad answers: jump straight to SSH-ing into a box.&lt;/p&gt;

&lt;h2&gt;
  
  
  'Tell me about a time you killed an alert'
&lt;/h2&gt;

&lt;p&gt;Not 'added an alert.' Killed one. Shows they understand alert hygiene as a discipline, not a chore.&lt;/p&gt;

&lt;h2&gt;
  
  
  'What's the difference between an SLI and an SLO?'
&lt;/h2&gt;

&lt;p&gt;Tests fundamentals without being a trick question. You'd be surprised how many senior candidates conflate these.&lt;/p&gt;

&lt;h2&gt;
  
  
  'What's the most useful tool you've built for yourself?'
&lt;/h2&gt;

&lt;p&gt;Good SREs are tool builders. The answer should be small, specific, and boring — a script that saves 5 minutes a day, not a grand platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I don't ask
&lt;/h2&gt;

&lt;p&gt;'Reverse a linked list on a whiteboard.' SRE is not a coding contest. I care whether you can debug a broken system at 3 AM, not whether you memorized CLRS.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>hiring</category>
      <category>career</category>
    </item>
    <item>
      <title>Incident Retrospectives Without Blame</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Tue, 21 Jul 2026 23:52:45 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/incident-retrospectives-without-blame-29e</link>
      <guid>https://dev.to/samson_tanimawo/incident-retrospectives-without-blame-29e</guid>
      <description>&lt;p&gt;I've run over 100 post-mortems. The worst ones end with 'Alice will be more careful.' The best ones end with 'we fixed the system.' Here's how you get from the first to the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The language rule
&lt;/h2&gt;

&lt;p&gt;Ban these phrases from retros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'Should have...'&lt;/li&gt;
&lt;li&gt;'Alice forgot to...'&lt;/li&gt;
&lt;li&gt;'If only...'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replace with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'The system let this happen because...'&lt;/li&gt;
&lt;li&gt;'The runbook didn't cover...'&lt;/li&gt;
&lt;li&gt;'The signal was missing...'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People make mistakes. Systems that allow those mistakes to reach production are the actual bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 whys that work
&lt;/h2&gt;

&lt;p&gt;Classic 5 whys often end at human error. Push through that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did the outage happen? &lt;em&gt;Alice deployed broken config.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Why did broken config deploy? &lt;em&gt;Our config validation didn't catch it.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Why didn't validation catch it? &lt;em&gt;It didn't cover this edge case.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Why didn't it cover it? &lt;em&gt;We didn't have a test for this case.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Why didn't we have a test? &lt;em&gt;Nobody owns the config validation pipeline.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Action item: assign an owner to config validation and add the missing test. That's a system fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The people thing
&lt;/h2&gt;

&lt;p&gt;Blameless doesn't mean consequence-free. If someone keeps making the same mistake after the system is fixed, that's a management issue, not a retro issue. Handle it privately, not in the retro.&lt;/p&gt;

&lt;p&gt;Retros are for learning. Everything else belongs elsewhere.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>postmortem</category>
      <category>culture</category>
    </item>
    <item>
      <title>Alert Fatigue: The Silent Productivity Killer</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Tue, 21 Jul 2026 13:39:56 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/alert-fatigue-the-silent-productivity-killer-105g</link>
      <guid>https://dev.to/samson_tanimawo/alert-fatigue-the-silent-productivity-killer-105g</guid>
      <description>&lt;p&gt;Alert fatigue isn't a wellness issue. It's a production risk. And it's cheaper to fix than most people think.&lt;/p&gt;

&lt;h2&gt;
  
  
  The math
&lt;/h2&gt;

&lt;p&gt;An engineer getting 30 alerts a night sleeps badly. Sleeps badly → cognitive load next day drops 20%. Drops 20% → higher chance of introducing a bug. That bug becomes the next incident. The next incident is another 30 alerts.&lt;/p&gt;

&lt;p&gt;You are in a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it actually happens
&lt;/h2&gt;

&lt;p&gt;Nobody sets out to create alert fatigue. It happens one alert at a time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'Let's add an alert for this' (reasonable)&lt;/li&gt;
&lt;li&gt;'The threshold is too sensitive, let's keep it but note it' (rationalizing)&lt;/li&gt;
&lt;li&gt;'It fires sometimes but we know what it means' (tolerating)&lt;/li&gt;
&lt;li&gt;'We should probably look at that' (normalizing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two years later you have 400 alerts and nobody remembers why half of them exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Delete every alert that hasn't caused action in 30 days.&lt;/strong&gt; If nobody acted on it, it's not an alert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raise the threshold on noisy alerts until they only fire for real problems.&lt;/strong&gt; Your boss is not going to fire you for missing noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group related alerts.&lt;/strong&gt; One incident should page once, not 40 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set a max alerts/day per engineer rule.&lt;/strong&gt; When you hit it, something gets cut.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Teams that aggressively prune alerts sleep better, ship more, and have fewer real incidents. It's not a trade-off. It's just good hygiene.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>oncall</category>
      <category>alerts</category>
    </item>
    <item>
      <title>Why SLIs Matter More Than SLOs</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Tue, 21 Jul 2026 01:54:45 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/why-slis-matter-more-than-slos-4n8</link>
      <guid>https://dev.to/samson_tanimawo/why-slis-matter-more-than-slos-4n8</guid>
      <description>&lt;p&gt;SLOs get all the attention. I want to argue that your SLIs are more important.&lt;/p&gt;

&lt;p&gt;Here's the thing: an SLO is a number you pick. 99.9% uptime. 300ms p95 latency. Whatever. It's a &lt;em&gt;decision&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An SLI is what you're actually measuring. Is it the right signal? Does it reflect user experience? Is it gamed by caching? Those questions matter more than the target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bad SLI, good SLO
&lt;/h2&gt;

&lt;p&gt;'99.9% of healthcheck requests return 200.' Looks great on paper. Means nothing to users. Your healthcheck endpoint can be up while your actual API is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good SLI, any SLO
&lt;/h2&gt;

&lt;p&gt;'99.x% of user-initiated checkout requests complete successfully within 5 seconds.' That signal tells you if the product works. Whatever target you pick, you're measuring the right thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;When your SLO is missed, does on-call get paged because a real user is suffering? If yes, your SLI is good. If no, you're measuring the wrong thing.&lt;/p&gt;

&lt;p&gt;Pick the SLI first. Pick the SLO second. Most teams do it backwards and wonder why their reliability work doesn't move the needle.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>slo</category>
      <category>reliability</category>
    </item>
    <item>
      <title>The PagerDuty Migration Playbook</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Mon, 20 Jul 2026 13:28:32 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/the-pagerduty-migration-playbook-3cen</link>
      <guid>https://dev.to/samson_tanimawo/the-pagerduty-migration-playbook-3cen</guid>
      <description>&lt;p&gt;Migrating from PagerDuty is not a weekend project. I learned this the hard way. Here's the playbook I wish I had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Audit every integration. Alerts, webhooks, Slack hooks, status page syncs. Ours had 47. I found 12 we had forgotten about.&lt;/li&gt;
&lt;li&gt;Export every schedule. Including overrides and holiday rules.&lt;/li&gt;
&lt;li&gt;Snapshot every escalation policy.&lt;/li&gt;
&lt;li&gt;Document your notification rules (who gets SMS vs push vs call).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The parallel period
&lt;/h2&gt;

&lt;p&gt;Do not cutover. Run both systems side-by-side for at least 2 weeks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route a small % of alerts to the new system first.&lt;/li&gt;
&lt;li&gt;Verify every alert fires in both.&lt;/li&gt;
&lt;li&gt;Let on-call engineers use the new app but keep PagerDuty as backup.&lt;/li&gt;
&lt;li&gt;Watch for silent failures (missing integrations usually hide here).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The cutover
&lt;/h2&gt;

&lt;p&gt;Pick a Monday morning, not a Friday night. Cutover in stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Non-critical services first&lt;/li&gt;
&lt;li&gt;Monitor for 24 hours&lt;/li&gt;
&lt;li&gt;Critical services next&lt;/li&gt;
&lt;li&gt;Keep PagerDuty subscribed but route-away for 1 more week in case of rollback&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The cleanup
&lt;/h2&gt;

&lt;p&gt;Don't forget the ghost integrations. Old CI jobs, dead services, third-party tools. They'll keep sending to PagerDuty silently until someone notices the bill.&lt;/p&gt;

&lt;p&gt;Total time for a 50-person team migration: 6 weeks, not 6 days. Plan accordingly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>pagerduty</category>
      <category>oncall</category>
    </item>
    <item>
      <title>How We Cut Datadog Bills by 60% Without Losing Observability</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Sun, 19 Jul 2026 23:50:57 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/how-we-cut-datadog-bills-by-60-without-losing-observability-2d0p</link>
      <guid>https://dev.to/samson_tanimawo/how-we-cut-datadog-bills-by-60-without-losing-observability-2d0p</guid>
      <description>&lt;p&gt;Last year our Datadog bill hit $38k/month. Leadership asked me to cut it in half. Here's how we got to $15k without losing a single useful signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 moves that worked
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Dropped custom metrics with zero dashboard references.&lt;/strong&gt; We had 2,400 custom metrics. Only 600 were actually graphed or alerted on. We stopped sending the other 1,800. Saved 30%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Aggressive log tier management.&lt;/strong&gt; Hot logs for 3 days, warm for 7, then cold. Most of our log cost came from full indexing of debug logs nobody queried after 24 hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lower-cardinality tags.&lt;/strong&gt; We were tagging metrics with &lt;code&gt;user_id&lt;/code&gt;. That's millions of series. We moved user-level stuff to traces and kept metrics aggregate-only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dropped synthetic monitors for dev environments.&lt;/strong&gt; We were running 200+ API checks on dev. We only need them on prod.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Consolidated APM sampling.&lt;/strong&gt; Instead of 100% sampling on everything, we did 10% sampling on healthy traces, 100% on errors and slow requests. Cut APM volume by 85% with no loss in signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;p&gt;Trying to negotiate a discount with sales. They'll give you 5-10% if you threaten to leave. Actually cutting volume gets you 60%.&lt;/p&gt;

&lt;p&gt;The lesson: your observability bill is almost always a data hygiene problem, not a pricing problem.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>datadog</category>
      <category>cost</category>
    </item>
    <item>
      <title>Building Your First Runbook: A Template That Actually Works</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Sun, 19 Jul 2026 13:17:20 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/building-your-first-runbook-a-template-that-actually-works-3fma</link>
      <guid>https://dev.to/samson_tanimawo/building-your-first-runbook-a-template-that-actually-works-3fma</guid>
      <description>&lt;p&gt;Most runbooks are useless. Either they're too abstract ('check the logs') or they're a 40-page Confluence doc that nobody reads at 3 AM.&lt;/p&gt;

&lt;p&gt;Here is the template I use. It fits on one page and works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The template
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Trigger.&lt;/strong&gt; The exact alert name and what it means.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Impact.&lt;/strong&gt; Who is affected? What are they seeing? Is this user-facing?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. First 5 minutes.&lt;/strong&gt; The single most useful command to run. One. Not five.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Common causes.&lt;/strong&gt; The 3 things that most often cause this alert, in order of likelihood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Fix per cause.&lt;/strong&gt; For each common cause, the exact fix. Copy-paste-ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Escalation.&lt;/strong&gt; Who to page if none of the above works. Include their timezone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Post-incident.&lt;/strong&gt; What to update after the incident is done (ticket, dashboard, doc).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;At 3 AM, your brain is running at 60%. You need a runbook that gives you the next action in under 30 seconds. A 40-page doc makes you think. A one-pager tells you what to do.&lt;/p&gt;

&lt;p&gt;Start with your noisiest alert. Write the runbook. Test it on a new team member. If they can follow it without you, it works.&lt;/p&gt;

&lt;p&gt;Repeat for your top 10 alerts. That's 90% of your on-call load handled.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>runbook</category>
      <category>ops</category>
    </item>
    <item>
      <title>AIOps vs Traditional Monitoring: What Actually Changed</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Sun, 19 Jul 2026 00:27:11 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/aiops-vs-traditional-monitoring-what-actually-changed-1044</link>
      <guid>https://dev.to/samson_tanimawo/aiops-vs-traditional-monitoring-what-actually-changed-1044</guid>
      <description>&lt;p&gt;Every vendor now slaps 'AIOps' on the box. Most of them just added a dashboard that says 'anomaly detected' and called it a day.&lt;/p&gt;

&lt;p&gt;I want to tell you what AIOps actually changes, and what it doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually changes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Correlation.&lt;/strong&gt; Traditional monitoring alerts on a symptom — CPU spike, 500 error, queue depth. AIOps correlates across signals and tells you &lt;em&gt;one&lt;/em&gt; story: 'deploy 14a23 on payments-api broke the checkout flow, here are the 7 alerts it triggered.'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Noise reduction.&lt;/strong&gt; On my old team we got 300 alerts/day. About 40 of them mattered. The rest were duplicates, known-flaky services, or transient spikes. A good AIOps layer suppresses 80% of that before a human sees it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause suggestions.&lt;/strong&gt; Not answers — suggestions. 'The top 3 likely causes based on historical incidents are...' Still needs a human to confirm. But it saves you 20 minutes of dashboard-hopping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't change
&lt;/h2&gt;

&lt;p&gt;You still need good instrumentation. You still need runbooks. You still need someone on-call who can make a call.&lt;/p&gt;

&lt;p&gt;AIOps is not a replacement for SRE. It is a force multiplier for SREs who already know what they're doing.&lt;/p&gt;

&lt;p&gt;If you're drowning in alerts, the answer isn't more dashboards. It's letting AI do the triage so your humans can do the thinking.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>ai</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Eventual Consistency: Debugging the Hardest Class of Bugs</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:08:10 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/eventual-consistency-debugging-the-hardest-class-of-bugs-37kl</link>
      <guid>https://dev.to/samson_tanimawo/eventual-consistency-debugging-the-hardest-class-of-bugs-37kl</guid>
      <description>&lt;h2&gt;
  
  
  The Bug That Only Happens Sometimes
&lt;/h2&gt;

&lt;p&gt;User reports: "I updated my profile but it still shows the old name."&lt;br&gt;
You refresh: new name appears.&lt;br&gt;
You ask them to refresh: still old name.&lt;br&gt;
You debug together: it's back to new name.&lt;br&gt;
The user: "This is clearly broken."&lt;br&gt;
You: "It's working for me..."&lt;/p&gt;

&lt;p&gt;Congratulations, you're debugging eventual consistency.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Eventual Consistency Actually Means
&lt;/h2&gt;

&lt;p&gt;In a distributed system, a write to one node takes time to propagate to others. During that window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some reads see the new value&lt;/li&gt;
&lt;li&gt;Some reads see the old value&lt;/li&gt;
&lt;li&gt;All reads will eventually see the new value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That "eventually" window can be milliseconds or seconds. Sometimes minutes if something is wrong.&lt;/p&gt;

&lt;p&gt;Users don't care about CAP theorem. They care that they updated their profile and it shows wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why It's So Hard to Debug
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Non-deterministic reproduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can't reliably reproduce it. Sometimes the bug fires, sometimes it doesn't. This makes unit tests useless and traditional debugging painful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Time-dependent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bug depends on when the read happens relative to the write. Microseconds matter. Your laptop is too fast to see it locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. State propagation is invisible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Logs show you the read and the write. They don't show you "this read hit replica 2 which hadn't received the replication event yet."&lt;/p&gt;
&lt;h2&gt;
  
  
  The Common Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Read your own writes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;User updates profile → write goes to primary → user reads → read hits a replica that hasn't replicated yet → user sees stale data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# WRONG
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;read_from_replica&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Might be stale!
&lt;/span&gt;
&lt;span class="c1"&gt;# RIGHT
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Guaranteed fresh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern 2: Cache staleness&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Write goes to database, but the cache still has the old value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fix: invalidate on write
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Force next read from DB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use a shorter TTL. Or use read-through caching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3: Async propagation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;User clicks "upgrade subscription" → API returns 200 → subscription_service emits event → billing_service processes → account_service updates → user's next page load still shows "trial"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fix: wait for propagation before returning
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;subscription_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Wait for downstream to process
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;wait_for_account_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern 4: Eventually consistent views&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Elasticsearch as a read replica of Postgres. Writes go to Postgres, then replicate to ES. If you write + read, you read the old value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fix 1: read from primary for recent writes
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recent_write&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;recent_write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;elasticsearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Fix 2: tracking epoch and waiting
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;min_epoch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;wait_for_replication_to_reach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_epoch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;elasticsearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Detection Strategy
&lt;/h2&gt;

&lt;p&gt;Since bugs are non-deterministic, detection has to be probabilistic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In your logging middleware:
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_inconsistency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consistency.mismatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;field:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Consistency mismatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor the mismatch rate. If it's &amp;gt; 0.01%, you have a real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Diagnostic Questions
&lt;/h2&gt;

&lt;p&gt;When debugging, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How many replicas are involved?&lt;/strong&gt; More replicas = longer propagation delay.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What's the normal replication lag?&lt;/strong&gt; Check monitoring. Normal is usually &amp;lt; 1 second.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is the lag elevated right now?&lt;/strong&gt; A spike from 100ms to 5s is a red flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Was the write synchronous or async?&lt;/strong&gt; Async writes have no guarantee of being visible on reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Does the client have a retry?&lt;/strong&gt; Retries at the wrong layer can produce duplicates or stale reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is there a cache in the path?&lt;/strong&gt; Caches are the most common source of "phantom staleness."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Debugging Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Replication lag metrics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pg_last_xact_replay_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;replication_lag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printSecondaryReplicationInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO replication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alert on lag &amp;gt; 1 second sustained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Request tracing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use OpenTelemetry to trace a single user action across services. You'll see where time is spent and which service is talking to which replica.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Log all reads with node ID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every read should log which node/replica served it. When debugging, you can see if the bug correlates with a specific replica.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Deterministic test environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build a test environment where you can introduce artificial replication delay. Helps reproduce bugs locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In test
&lt;/span&gt;&lt;span class="n"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;  &lt;span class="c1"&gt;# Force 500ms replication lag
# Run the flow, assert consistency
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Fix Strategies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Strategy 1: Strong consistency where it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not everything needs to be eventually consistent. For critical flows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-visible state changes → read from primary&lt;/li&gt;
&lt;li&gt;Financial operations → strong consistency (transactions)&lt;/li&gt;
&lt;li&gt;Security operations (auth, permissions) → strong consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accept the latency hit for correctness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy 2: Causal consistency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Track "you just wrote X" and ensure subsequent reads see X:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Client sends "min_epoch" based on last write
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min_epoch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;wait_for_replication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min_epoch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strategy 3: User-visible time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes the solution is UX, not infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Your changes are saved. They may take up to 30 seconds to appear."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set expectations. Users understand "saved but syncing" better than "works for you but broken for me."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy 4: Accept and monitor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some inconsistency is tolerable. Log it, alert if it exceeds a threshold, and fix the worst offenders.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardest Bugs
&lt;/h2&gt;

&lt;p&gt;The worst eventual consistency bugs happen at failure recovery time. A write that was in-flight during a network partition might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Succeed on primary but fail to replicate&lt;/li&gt;
&lt;li&gt;Fail on primary but succeed on one replica&lt;/li&gt;
&lt;li&gt;Be applied in a different order than another write&lt;/li&gt;
&lt;li&gt;Be lost entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are fundamental distributed systems problems. Defense: idempotent operations, client-side retries with unique IDs, careful read-after-write semantics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Eventual consistency isn't a bug. It's a tradeoff.&lt;/p&gt;

&lt;p&gt;The bug is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not understanding where consistency matters in your system&lt;/li&gt;
&lt;li&gt;Not having visibility into replication lag&lt;/li&gt;
&lt;li&gt;Not documenting consistency guarantees to users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these, and "eventually consistent" stops being a dirty phrase and starts being a feature.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>distributed</category>
      <category>consistency</category>
      <category>debugging</category>
      <category>microservices</category>
    </item>
    <item>
      <title>The Economics of Self-Hosting vs. Managed Monitoring</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Sat, 18 Jul 2026 00:06:28 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/the-economics-of-self-hosting-vs-managed-monitoring-5cc5</link>
      <guid>https://dev.to/samson_tanimawo/the-economics-of-self-hosting-vs-managed-monitoring-5cc5</guid>
      <description>&lt;h2&gt;
  
  
  The "Obvious" Math That's Wrong
&lt;/h2&gt;

&lt;p&gt;Engineer A: "Datadog is $15K/month. Prometheus is free. We should self-host."&lt;/p&gt;

&lt;p&gt;Engineer B: "But we'd need to pay an SRE to run it. That's $150K/year."&lt;/p&gt;

&lt;p&gt;Engineer A: "Prometheus doesn't need a full SRE. It's easy."&lt;/p&gt;

&lt;p&gt;Engineer B: "Famous last words."&lt;/p&gt;

&lt;p&gt;This conversation happens at every company. Both sides have points. The real math is more complex.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Total Cost Breakdown
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Managed (Datadog, New Relic, Dynatrace)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Licensing: $X/month (scales with hosts, events, logs)
Integration time: 1-2 weeks per service
Training: 1 day per new hire
Ongoing: minimal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Self-hosted (Prometheus + Grafana + Loki + Alertmanager)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Infrastructure: hosting costs (~$500-$5000/month depending on scale)
Initial setup: 2-4 weeks of engineering time
Ongoing maintenance: 10-20% of 1 FTE
Upgrade costs: quarterly, each upgrade ~1 week
Storage growth: ~20% per year
Expertise: junior → senior SRE hire required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The honest answer: &lt;strong&gt;managed is cheaper for teams under 50 engineers. Self-hosted becomes cheaper around 200+ engineers if you can run it well&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Variables
&lt;/h2&gt;

&lt;p&gt;It's not just licensing cost vs. hosting cost. These factors matter more:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Data volume growth&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Managed tools charge per GB ingested or per metric. If your logs 10x, your bill 10x's.&lt;/p&gt;

&lt;p&gt;Self-hosted scales linearly with compute. You control the growth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Retention requirements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Managed tools often charge extra for long retention. Self-hosted you store as much as your disk allows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cardinality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prometheus dies at high cardinality. Datadog handles it but charges more. High-cardinality metrics are where self-hosted breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Incident rate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Heavy incident load means heavy query load on your monitoring tools. Self-hosted needs bigger compute for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Team expertise&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your team has never run Prometheus, you'll spend 6 months in the pit learning cardinality mistakes, retention tuning, and HA setups. That's not free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Break-Even Calculation
&lt;/h2&gt;

&lt;p&gt;Rough calculation for a 50-engineer startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Managed (Datadog)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Licensing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$10K/month = $120K/year&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Maintenance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~10 hours/month of engineering time&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Total&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~$125K/year&lt;/span&gt;

&lt;span class="na"&gt;Self-Hosted (Prometheus + Grafana + Loki)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Infrastructure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$2K/month = $24K/year&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Maintenance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~30% of 1 SRE = $45K/year (loaded)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Initial setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$30K one-time&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Total year 1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$99K&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Total year 2+&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$69K/year&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At 50 engineers, self-hosted saves ~$60K/year once you're past the setup phase.&lt;/p&gt;

&lt;p&gt;BUT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the SRE quits, you're in trouble&lt;/li&gt;
&lt;li&gt;If the cardinality explodes, you're in trouble&lt;/li&gt;
&lt;li&gt;If an upgrade fails, you're in trouble&lt;/li&gt;
&lt;li&gt;If you need 24/7 reliability of monitoring itself, add another $50K/year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Factor these in honestly before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hybrid Approach
&lt;/h2&gt;

&lt;p&gt;Most teams we see end up hybrid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Self-hosted Prometheus (cheap at scale)&lt;/span&gt;
&lt;span class="na"&gt;Logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Managed service (complex to self-host at scale)&lt;/span&gt;
&lt;span class="na"&gt;Traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Managed service (requires specialized knowledge)&lt;/span&gt;
&lt;span class="na"&gt;Alerting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Self-hosted Alertmanager (simple, stable)&lt;/span&gt;
&lt;span class="na"&gt;Dashboards&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Self-hosted Grafana&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This captures the best of both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cheap for commodity metrics&lt;/li&gt;
&lt;li&gt;Paid for complex/specialized (logs, traces)&lt;/li&gt;
&lt;li&gt;Predictable ongoing costs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Managed Is Obviously Right
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Team has &amp;lt; 20 engineers&lt;/li&gt;
&lt;li&gt;No one has ops expertise&lt;/li&gt;
&lt;li&gt;Growing fast, need to ship features&lt;/li&gt;
&lt;li&gt;Compliance requires certified tools (SOC2, HIPAA specifically)&lt;/li&gt;
&lt;li&gt;Need it running in 2 weeks, not 3 months&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Self-Hosted Is Obviously Right
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Team has 100+ engineers&lt;/li&gt;
&lt;li&gt;Strong ops culture already in place&lt;/li&gt;
&lt;li&gt;Budget pressure from managed costs&lt;/li&gt;
&lt;li&gt;Very high data volume (10TB+/day logs)&lt;/li&gt;
&lt;li&gt;Want full control over data (privacy, residency)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Gray Zone (50-200 engineers)
&lt;/h2&gt;

&lt;p&gt;Most startups land here. The calculation is close. Three factors break the tie:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 1: Engineering time availability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your SRE team is underwater with incidents, adding "maintain Prometheus" to their plate is a disaster. Pay for managed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 2: Growth rate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're doubling engineers every 6 months, managed costs explode. Build self-hosted capacity now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 3: Data sovereignty&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some customers won't accept their data leaving your infrastructure. This forces self-hosted regardless of cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Costs of Managed
&lt;/h2&gt;

&lt;p&gt;Managed isn't just the invoice. Also consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integration debt&lt;/strong&gt;: every service you add to Datadog is a service you're locked into Datadog&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom metric costs&lt;/strong&gt;: 10M custom metrics in Datadog is expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term pricing risk&lt;/strong&gt;: vendors raise prices once you're locked in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export restrictions&lt;/strong&gt;: getting your data out is often painful&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support quality&lt;/strong&gt;: P1 support response times vary wildly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Factor these into your TCO.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Costs of Self-Hosted
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On-call for monitoring&lt;/strong&gt;: when Prometheus dies, someone has to fix it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade risk&lt;/strong&gt;: each major version can break dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling anxiety&lt;/strong&gt;: "will it handle the next 2x growth?" is a question you ask yourself weekly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge concentration&lt;/strong&gt;: if the one expert leaves, you're stranded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration maintenance&lt;/strong&gt;: every new service needs exporters/agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't line items on an invoice. They're real costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision Framework
&lt;/h2&gt;

&lt;p&gt;Answer these questions honestly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How many engineers can dedicate time to monitoring maintenance?&lt;/li&gt;
&lt;li&gt;How fast is your data volume growing?&lt;/li&gt;
&lt;li&gt;What's your current monthly bill for managed tools?&lt;/li&gt;
&lt;li&gt;How much pain is there from vendor lock-in?&lt;/li&gt;
&lt;li&gt;What's the opportunity cost of engineering time spent on monitoring?&lt;/li&gt;
&lt;li&gt;Can you recruit or train the expertise?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer #1-#6 confidently, stay managed until you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Recommendation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Years 0-2&lt;/strong&gt;: Managed. Ship features, don't fight your monitoring stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Years 3-5&lt;/strong&gt;: Start evaluating. Maybe hybrid. Hire an SRE with deep ops experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Years 5+&lt;/strong&gt;: Likely hybrid or mostly self-hosted. Costs justify the complexity.&lt;/p&gt;

&lt;p&gt;The best monitoring stack is the one your team can actually operate. If that's managed, pay the bill. If that's self-hosted, invest in the expertise. Don't try to do both poorly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>costs</category>
      <category>devops</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Building an Incident Response Playbook Library</title>
      <dc:creator>Samson Tanimawo</dc:creator>
      <pubDate>Fri, 17 Jul 2026 13:46:27 +0000</pubDate>
      <link>https://dev.to/samson_tanimawo/building-an-incident-response-playbook-library-2oc4</link>
      <guid>https://dev.to/samson_tanimawo/building-an-incident-response-playbook-library-2oc4</guid>
      <description>&lt;h2&gt;
  
  
  The Folder Full of Stale Runbooks
&lt;/h2&gt;

&lt;p&gt;Every engineering org has a Confluence folder of incident runbooks. Every runbook was written during or after an incident. Each is a snapshot of how to fix one specific thing.&lt;/p&gt;

&lt;p&gt;After 2 years, the folder has 400 runbooks. Nobody knows which are current. During the next incident, nobody opens any of them.&lt;/p&gt;

&lt;p&gt;A playbook library is supposed to help. Usually it just generates debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Playbooks Should Actually Do
&lt;/h2&gt;

&lt;p&gt;A good playbook library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces cognitive load during incidents&lt;/li&gt;
&lt;li&gt;Provides confidence to junior responders&lt;/li&gt;
&lt;li&gt;Captures institutional knowledge&lt;/li&gt;
&lt;li&gt;Trains new hires&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bad playbook library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is out of date&lt;/li&gt;
&lt;li&gt;Is impossible to search&lt;/li&gt;
&lt;li&gt;Has no ownership&lt;/li&gt;
&lt;li&gt;Contradicts itself across runbooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is process, not content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structure
&lt;/h2&gt;

&lt;p&gt;Every runbook follows the same template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Title: [Specific Problem Being Fixed]&lt;/span&gt;

&lt;span class="gu"&gt;## When to Use This&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Alert that fires: [exact alert name]
&lt;span class="p"&gt;-&lt;/span&gt; Symptoms: [what the user sees]
&lt;span class="p"&gt;-&lt;/span&gt; Impact level: [SEV-1/2/3/4]

&lt;span class="gu"&gt;## Quick Fix (90 seconds)&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Command to run
&lt;span class="p"&gt;2.&lt;/span&gt; Command to run
&lt;span class="p"&gt;3.&lt;/span&gt; Verification step

&lt;span class="gu"&gt;## Deeper Investigation (if quick fix fails)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Check this
&lt;span class="p"&gt;-&lt;/span&gt; Look at this dashboard
&lt;span class="p"&gt;-&lt;/span&gt; Tail these logs

&lt;span class="gu"&gt;## Root Cause Category&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Known cause 1: [link to deeper doc]
&lt;span class="p"&gt;-&lt;/span&gt; Known cause 2: [link to deeper doc]

&lt;span class="gu"&gt;## Escalation&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Primary: [role]
&lt;span class="p"&gt;-&lt;/span&gt; Secondary: [role]
&lt;span class="p"&gt;-&lt;/span&gt; SME: [specific person]

&lt;span class="gu"&gt;## Related Runbooks&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Similar problem A]
&lt;span class="p"&gt;-&lt;/span&gt; [Similar problem B]

&lt;span class="gu"&gt;## Metadata&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Owner: [team]
&lt;span class="p"&gt;-&lt;/span&gt; Last Verified: [date]
&lt;span class="p"&gt;-&lt;/span&gt; Expires: [date + 3 months]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every field is required. Missing fields fail CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ownership Rule
&lt;/h2&gt;

&lt;p&gt;Every runbook has an owner. The owner is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeping it current&lt;/li&gt;
&lt;li&gt;Re-verifying it quarterly&lt;/li&gt;
&lt;li&gt;Updating when the underlying system changes&lt;/li&gt;
&lt;li&gt;Deleting it when no longer relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a runbook has no owner, delete it. A stale runbook is worse than no runbook — it wastes time during incidents and may contain wrong instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Expiration Date
&lt;/h2&gt;

&lt;p&gt;Every runbook has a 90-day expiration. After 90 days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI warns the owner&lt;/li&gt;
&lt;li&gt;After 30 more days, CI fails builds that reference it&lt;/li&gt;
&lt;li&gt;After 60 more days, the runbook is auto-moved to archive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The owner must re-verify and reset the expiration date. Re-verification means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read it end to end&lt;/li&gt;
&lt;li&gt;Try the commands in staging&lt;/li&gt;
&lt;li&gt;Update anything that's changed&lt;/li&gt;
&lt;li&gt;Set a new expiration date&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This forces continuous maintenance. It's painful. It also means the runbooks are trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Discovery Problem
&lt;/h2&gt;

&lt;p&gt;A library of 400 runbooks is useless if you can't find the right one during an incident.&lt;/p&gt;

&lt;p&gt;Three techniques:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Alert-to-runbook mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every alert includes a runbook link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HighErrorRate&lt;/span&gt;
&lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;above&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;5%"&lt;/span&gt;
  &lt;span class="na"&gt;runbook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://runbooks.internal/api/high-error-rate"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the alert fires, the runbook is one click away. No searching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Symptom-based search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We tag runbooks by symptom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symptoms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slow&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;time"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;queries&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;timing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;out"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connection&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pool&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exhausted"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During an incident, you search by symptom, not by service name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AI-assisted search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Slack bot that takes a natural-language description and returns the top 3 relevant runbooks. Only works if runbooks are well-structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Writing Rule: Before You Have the Outage
&lt;/h2&gt;

&lt;p&gt;The worst time to write a runbook is during an incident. You're stressed, you're in a hurry, you'll write something incomplete.&lt;/p&gt;

&lt;p&gt;The best time to write a runbook is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;During a change: new service? write the runbook before launch&lt;/li&gt;
&lt;li&gt;During a quiet week: pick a service you're familiar with, write the runbook&lt;/li&gt;
&lt;li&gt;During a post-mortem: document what you just learned while it's fresh&lt;/li&gt;
&lt;li&gt;During a drill: tabletop exercises expose gaps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Companies that write runbooks proactively have better incident response than those who write reactively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Post-Incident Rule
&lt;/h2&gt;

&lt;p&gt;Every incident with a post-mortem generates at least one runbook update. Either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a new runbook&lt;/strong&gt; if this was a novel problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update an existing runbook&lt;/strong&gt; if the old instructions didn't work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete a runbook&lt;/strong&gt; if it was wrong or misleading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No post-mortem is complete without a runbook change. We enforce this in the template.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of Playbooks
&lt;/h2&gt;

&lt;p&gt;Maintaining a library of 100 current runbooks takes real time. Approximately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write new runbook: 2 hours
Quarterly verification: 30 min/runbook/quarter
Post-incident update: 1 hour/incident
Search index maintenance: 2 hours/month

Total: ~10 hours/week for a mature library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Budget this time. If you don't, your library will rot.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Write a Runbook
&lt;/h2&gt;

&lt;p&gt;Counter-intuitive: sometimes the right answer is not to write a runbook.&lt;/p&gt;

&lt;p&gt;If the fix is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Obvious from the error message → no runbook needed&lt;/li&gt;
&lt;li&gt;Already automated → delete the manual path&lt;/li&gt;
&lt;li&gt;Only applicable once (one-time migration) → write a ticket, not a runbook&lt;/li&gt;
&lt;li&gt;Constantly changing → document the general approach, not specific commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Runbooks are for recurring, stable, manual procedures. Everything else belongs elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Death Star" Runbook
&lt;/h2&gt;

&lt;p&gt;The biggest runbook anti-pattern: one giant runbook that covers "incident response" generically. 50 pages long. No specific triggers. No specific steps.&lt;/p&gt;

&lt;p&gt;These exist because nobody wanted to write specific ones, so they wrote one massive generic one.&lt;/p&gt;

&lt;p&gt;Nobody reads them during incidents. They're doorstops.&lt;/p&gt;

&lt;p&gt;Delete them. Replace with specific, focused runbooks tied to specific alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Starter Kit
&lt;/h2&gt;

&lt;p&gt;If you're starting a runbook library from scratch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;List your top 20 most-fired alerts in the last 6 months&lt;/li&gt;
&lt;li&gt;Write a runbook for each of them&lt;/li&gt;
&lt;li&gt;Link each alert to its runbook&lt;/li&gt;
&lt;li&gt;Set quarterly verification cycles&lt;/li&gt;
&lt;li&gt;Assign owners&lt;/li&gt;
&lt;li&gt;Put them in version control&lt;/li&gt;
&lt;li&gt;Review monthly with your team&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After 6 months, you'll have 30-50 high-quality runbooks that are actually used. This is infinitely more valuable than 400 stale ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ultimate Test
&lt;/h2&gt;

&lt;p&gt;During your next incident, watch the responder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did they open a runbook? (good)&lt;/li&gt;
&lt;li&gt;Was the runbook current? (better)&lt;/li&gt;
&lt;li&gt;Did it contain the right answer? (best)&lt;/li&gt;
&lt;li&gt;Did it resolve the incident faster than winging it would have? (mission accomplished)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the answer to any of these is "no," your library has work to do.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by Dr. Samson Tanimawo&lt;/strong&gt;&lt;br&gt;
BSc · MSc · MBA · PhD&lt;br&gt;
Founder &amp;amp; CEO, Nova AI Ops. &lt;a href="https://novaaiops.com" rel="noopener noreferrer"&gt;https://novaaiops.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>incidents</category>
      <category>sre</category>
      <category>runbooks</category>
      <category>oncall</category>
    </item>
  </channel>
</rss>
