Files
nexus/sreweekly/articles/381/01-the-pyramid-of-alerting.html
2026-09-12 17:23:01 +08:00

1674 lines
102 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The Pyramid of Alerting - DEV Community</title>
<link rel="preload" href="/reactions?article_id=1425662" as="fetch" crossorigin="same-origin">
<link rel="canonical" href="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48" />
<meta name="description" content="We all came across it before. Your company is processing thousands of data points. All goes well on... Tagged with monitoring, dataengineering.">
<meta name="keywords" content="monitoring, dataengineering, software, coding, development, engineering, inclusive, community">
<meta property="og:type" content="article" />
<meta property="og:url" content="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48" />
<meta property="og:title" content="The Pyramid of Alerting" />
<meta property="og:description" content="We all came across it before. Your company is processing thousands of data points. All goes well on..." />
<meta property="og:site_name" content="DEV Community" />
<meta name="twitter:site" content="@thepracticaldev">
<meta name="twitter:creator" content="@">
<meta name="author-trust" content="0">
<meta name="twitter:title" content="The Pyramid of Alerting">
<meta name="twitter:description" content="We all came across it before. Your company is processing thousands of data points. All goes well on...">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:widgets:new-embed-design" content="on">
<meta name="robots" content="max-snippet:-1, max-image-preview:large, max-video-preview:-1">
<meta property="og:image" content="https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88ajamndcnidzsp4n8jv.png" />
<meta name="twitter:image:src" content="https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88ajamndcnidzsp4n8jv.png">
<meta name="last-updated" content="2026-09-11 00:03:43 UTC">
<meta name="user-signed-in" content="false">
<meta name="head-cached-at" content="1789085023">
<meta name="environment" content="production">
<link rel="stylesheet" href="https://assets.dev.to/assets/minimal-3a438d8d138f95f478f01cbe53363cacf36bc6c47172aeb2e6c9172eebe29aa6.css" media="all" id="main-minimal-stylesheet" />
<link rel="stylesheet" href="https://assets.dev.to/assets/views-ca4b6f6e9ec63c7deefbdd4067e585d752cd4b06c12da1a49c7b9644defa2758.css" media="all" id="main-views-stylesheet" />
<link rel="stylesheet" href="https://assets.dev.to/assets/crayons-537acd1cbd95b8d8d524dab489c8923b7316dd045ead39e7a2dbaa42cdc208b2.css" media="all" id="main-crayons-stylesheet" />
<script src="https://assets.dev.to/assets/base-2e294c52c5fe6b771fda1a5f38d2f10756d0b4d8359c6f9825fe47df0f54e6ae.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/application-9ee88e198a0d2b64238c8557b5225d20e2c18ff5b877f1159ee5a31f6e3174f9.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/baseInitializers-624554190e72cf52de9498fd225c2e71ab334dfc4919535dcc5a26a3a0ae2359.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/baseTracking-cb478131fb1b41bc25e80ebcee996318777e081c9aa87367fabc1c118461a2d3.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/followButtons-e3595664847e9f43945a9b8a0445b32cda9a149f1f169f78b8fcc7714ccab45a.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/eventSignupButtons-a2b5da712a1b8eeb0eda4d63fafc54973138a70b88503a2163806c27fc1e4c39.js" defer="defer"></script>
<meta name="search-script" content="https://assets.dev.to/assets/Search-a570c3428c9b6cb070d3f18817c957f80d0dbdf36a0f4a1d6e23a990305fbc12.js">
<meta name="mermaid-script" content="https://assets.dev.to/assets/mermaidRenderer-b9ba305a9767f9203ac04b8043493fb0542090e9a7981428cecf8c7d2ccaf177.js">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<link rel="icon" type="image/x-icon" href="https://media2.dev.to/dynamic/image/width=32,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" />
<link rel="apple-touch-icon" href="https://media2.dev.to/dynamic/image/width=180,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://media2.dev.to/dynamic/image/width=152,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://media2.dev.to/dynamic/image/width=180,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png">
<link rel="apple-touch-icon" sizes="167x167" href="https://media2.dev.to/dynamic/image/width=167,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png">
<link href="https://media2.dev.to/dynamic/image/width=192,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" rel="icon" sizes="192x192" />
<link href="https://media2.dev.to/dynamic/image/width=128,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" rel="icon" sizes="128x128" />
<meta name="apple-mobile-web-app-title" content="dev.to">
<meta name="application-name" content="dev.to">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
<link rel="search" href="https://dev.to/open-search.xml" type="application/opensearchdescription+xml" title="DEV Community" />
<link rel="describedby" href="https://dev.to/llms.txt" type="text/plain" title="Guidance for AI agents" />
<meta property="forem:name" content="DEV Community" />
<meta property="forem:logo" content="https://media2.dev.to/dynamic/image/width=512,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" />
<meta property="forem:domain" content="dev.to" />
<style>
body:has(.pageslug-aie) #topbar {
display: none !important;
}
@media screen and (max-width: 768px) and (pointer: coarse) {
/* Target WebKit/Blink specific behaviors */
@supports (-webkit-app-region: none) {
body:has(.pageslug-aie) .dc-page {
padding-top: 56px !important;
}
}
}
body:not(.hidden-shell) .pageslug-aie {
margin-top: -56px !important;
}
/* ==========================================================================
THE 418 CHALLENGE: THEME-AWARE RETRO EDITION
========================================================================== */
.articletag-418challenge {
font-family: "Comic Sans MS", "Chalkboard SE", "Marker Felt", sans-serif !important;
cursor: crosshair !important;
}
/* Destroy modern rounded corners and add brutal borders */
.articletag-418challenge .crayons-card,
.articletag-418challenge .crayons-article__main,
.articletag-418challenge .crayons-article__header {
border-radius: 0 !important;
margin-bottom: 2rem !important;
}
/* Typography specifics */
.articletag-418challenge h1,
.articletag-418challenge h2,
.articletag-418challenge h3 {
text-transform: uppercase !important;
}
/* Fake <marquee> effect for the main article title */
.articletag-418challenge h1.fs-3xl {
animation: retro-slide 5s linear infinite alternate;
overflow: visible;
}
/* Spin those avatars */
.articletag-418challenge .crayons-article__header__meta img {
border-radius: 0 !important;
animation: retro-spin 5s linear infinite;
}
/* Obnoxious tags */
.articletag-418challenge .crayons-tag {
border-radius: 0 !important;
font-weight: 900 !important;
transform: rotate(-3deg);
display: inline-block;
}
.articletag-418challenge .crayons-tag:nth-child(even) {
transform: rotate(3deg);
}
/* --------------------------------------------------------------------------
2. LIGHT THEME (Windows 95 / Office 97 Vibes)
-------------------------------------------------------------------------- */
body:not(.dark-theme) .articletag-418challenge {
background-color: #008080 !important; /* Win95 Teal Desktop */
}
body:not(.dark-theme) .articletag-418challenge .crayons-card,
body:not(.dark-theme) .articletag-418challenge .crayons-article__main,
body:not(.dark-theme) .articletag-418challenge .crayons-article__header {
background-color: #c0c0c0 !important; /* Classic dialog gray */
color: #000000 !important;
border: 4px outset #ffffff !important;
box-shadow: 8px 8px 0px #000000 !important;
}
body:not(.dark-theme) .articletag-418challenge h1,
body:not(.dark-theme) .articletag-418challenge h2,
body:not(.dark-theme) .articletag-418challenge h3 {
color: #ff0000 !important;
text-shadow: 2px 2px 0px #ffff00 !important;
border-bottom: 3px dashed #0000ff !important;
}
body:not(.dark-theme) .articletag-418challenge a,
body:not(.dark-theme) .articletag-418challenge .crayons-link {
color: #0000ff !important; /* Standard unvisited blue */
text-decoration: underline !important;
font-weight: bold !important;
}
body:not(.dark-theme) .articletag-418challenge a:hover,
body:not(.dark-theme) .articletag-418challenge .crayons-link:hover {
background-color: #ffff00 !important;
color: #ff0000 !important;
}
body:not(.dark-theme) .articletag-418challenge .crayons-tag {
background: #ffff00 !important;
color: #ff0000 !important;
border: 2px dotted #0000ff !important;
}
/* --------------------------------------------------------------------------
3. DARK THEME (1999 Hacker / Deep GeoCities Vibes)
-------------------------------------------------------------------------- */
body.dark-theme .articletag-418challenge {
background-color: #000000 !important;
/* Dumb CSS: A scrolling, eye-bleeding neon green hacker grid with CRT scanlines */
background-image:
linear-gradient(transparent 50%, rgba(0, 255, 0, 0.25) 50%),
linear-gradient(90deg, rgba(0, 255, 0, 0.5) 1px, transparent 1px),
linear-gradient(rgba(0, 255, 0, 0.5) 1px, transparent 1px) !important;
background-size: 100% 4px, 20px 20px, 20px 20px !important;
animation: retro-pan 60s linear infinite !important;
}
body.dark-theme .articletag-418challenge .crayons-card,
body.dark-theme .articletag-418challenge .crayons-article__main,
body.dark-theme .articletag-418challenge .crayons-article__header {
background-color: #111111 !important;
color: #00ff00 !important; /* Terminal Green */
border: 4px outset #555555 !important;
box-shadow: 8px 8px 0px #ff00ff !important; /* Hot pink shadow */
}
body.dark-theme .articletag-418challenge h1,
body.dark-theme .articletag-418challenge h2,
body.dark-theme .articletag-418challenge h3 {
color: #ff00ff !important; /* Magenta headers */
text-shadow: 2px 2px 0px #00ffff !important; /* Cyan shadow */
border-bottom: 3px dashed #00ff00 !important;
}
body.dark-theme .articletag-418challenge a,
body.dark-theme .articletag-418challenge .crayons-link {
color: #00ffff !important; /* Cyan links */
text-decoration: underline !important;
font-weight: bold !important;
}
body.dark-theme .articletag-418challenge a:hover,
body.dark-theme .articletag-418challenge .crayons-link:hover {
background-color: #ff00ff !important;
color: #ffffff !important;
}
body.dark-theme .articletag-418challenge .crayons-tag {
background: #000000 !important;
color: #00ff00 !important;
border: 2px dotted #ff00ff !important;
}
/* --------------------------------------------------------------------------
4. ANIMATIONS
-------------------------------------------------------------------------- */
@keyframes retro-spin {
100% { transform: rotate(360deg); }
}
@keyframes retro-slide {
0% { transform: translateX(-2%); }
100% { transform: translateX(2%); }
}
@keyframes retro-pan {
0% { background-position: 0 0, 0 0, 0 0; }
/* Numbers must be multiples of the background-size (4px and 20px) to loop perfectly */
100% { background-position: 0 100px, 40px 40px, 40px 40px; }
}
/* --------------------------------------------------------------------------
5. BONUS: CAUTION TAPE REACTION BAR
-------------------------------------------------------------------------- */
.articletag-418challenge .crayons-article-actions {
background: repeating-linear-gradient(
45deg,
#ffff00,
#ffff00 10px,
#000000 10px,
#000000 20px
) !important;
border: 4px solid #ff0000 !important;
border-radius: 0 !important;
}
.articletag-418challenge .crayons-article-actions button {
background:black;
color: #ff00ff !important;
}
.articletag-418challenge .crayons-article-actions .crayons-reaction__count {
color: #ff00ff !important;
font-weight: bold !important;
}
.articletag-418challenge .crayons-icon:not(.crayons-icon--default) * {
fill: #ffff00 !important;
}
.articletag-418challenge .c-embed .crayons-btn--primary {
color: #00ff00 !important;
}
.articletag-418challenge .popover-billboard {
margin-bottom: 0 !important;
}
.crayons-story__contentpreview .ltag__link--embedded {
margin-top: 0 !important;
}
.subscription-icon {
max-height: 16px !important;
display: inline-block !important;
}
@media screen and (min-width: 950px) {
.event-show-layout {
margin-top: 24px !important;
}
}
@media screen and (max-width: 949px) {
.event-show-layout {
padding: 11px !important;
}
}
</style>
</head>
<body
class="sans-serif-article-body default-header"
data-user-status="logged-out"
data-is-root-subforem="false"
data-subforem-id="1"
data-side-nav-visible="true"
data-community-name="DEV Community"
data-subscription-icon="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"
data-community-leader-icon="https://assets.dev.to/assets/community-leader-icon-b72c9e74eff54916e5c46c962f47ba40c9f611a71f8b157511f9613f69c0001b.svg"
data-locale="en"
data-honeybadger-key="hbp_nqu4Y66HuEKlD6YRGssZuRQnPOjDm50J8Zkr"
data-deployed-at="2026-09-10T21:55:18Z"
data-latest-commit-id="ac54b3b29af145ea947eb7f5d6acba8a86c9d109"
data-ga-tracking="UA-71991109-1"
data-cookie-banner-user-context="logged_out_only"
data-cookie-banner-platform-context="off"
data-algolia-id="PRSOBFP46H"
data-algolia-search-key="9aa7d31610cba78851c9b1f63776a9dd"
data-algolia-display="true"
data-dynamic-url-component="bmar11"
data-ga4-tracking-id="G-TYEM8Y3JN3"
data-global-feature-flags-enabled="data_update_scripts onboarding_newsletter_content schedule_articles moderator_role display_ad_tags replace_unicorn_with_jump_to_comments article_id_adjusted_weight cloudflare_preferred_for_hosted_images multiple_reactions subscriber_icon ab_experiment_feed_strategy use_stories_endpoint digest_subject_testing digest_results_count_testing consistent_rendering discover_and_following_tabs hero_billboard onboarding_drip_emails algolia_frontend gemini_onboarding optimize_article_tag_query dev_core_user_sync personalized_email_digests community_favorites"
data-style-fingerprint="c75bfc6a82">
<script>
if (navigator.userAgent === 'ForemWebView/1' || window.frameElement) {
document.body.classList.add("hidden-shell");
}
if (new Date() > new Date("2026-02-04T09:00:00-05:00")) {
document.body.dataset.sideNavVisible = "false";
}
</script>
<link rel="stylesheet" href="https://assets.dev.to/assets/minimal-3a438d8d138f95f478f01cbe53363cacf36bc6c47172aeb2e6c9172eebe29aa6.css" media="all" id="secondary-minimal-stylesheet" />
<link rel="stylesheet" href="https://assets.dev.to/assets/views-ca4b6f6e9ec63c7deefbdd4067e585d752cd4b06c12da1a49c7b9644defa2758.css" media="all" id="secondary-views-stylesheet" />
<link rel="stylesheet" href="https://assets.dev.to/assets/crayons-537acd1cbd95b8d8d524dab489c8923b7316dd045ead39e7a2dbaa42cdc208b2.css" media="all" id="secondary-crayons-stylesheet" />
<div id="body-styles">
<style>
:root {
--accent-brand-lighter-rgb: 86, 105, 285;
--accent-brand-rgb: 64, 78, 211;
--accent-brand-darker-rgb: 51, 62, 169;
}
</style>
</div>
<div id="audiocontent" data-podcast="">
</div>
<div class="navigation-progress" id="navigation-progress"></div>
<header id="topbar" class="crayons-header topbar print-hidden">
<span id="route-change-target" tabindex="-1"></span>
<a href="#main-content" class="skip-content-link">Skip to content</a>
<div class="crayons-header__container">
<span class="inline-block m:hidden ">
<button class="c-btn c-btn--icon-alone js-hamburger-trigger mx-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="adbrgmgpmoloplelehqtv1t6mtp8232p" class="crayons-icon"><title id="adbrgmgpmoloplelehqtv1t6mtp8232p">Navigation menu</title>
<path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"></path>
</svg>
</button>
</span>
<a href="/" class="site-logo" aria-label="DEV Community Home" >
<img class="site-logo__img"
src="https://media2.dev.to/dynamic/image/quality=100/https://dev-to-uploads.s3.amazonaws.com/uploads/logos/resized_logo_UQww2soKuUsjaOGNB38o.png"
style="aspect-ratio: 10 / 8"
alt="DEV Community">
</a>
<div class="crayons-header--search js-search-form" id="header-search">
<form accept-charset="UTF-8" method="get" action="/search" role="search">
<div class="crayons-fields crayons-fields--horizontal">
<div class="crayons-field flex-1 relative">
<input id="search-input" class="crayons-header--search-input crayons-textfield js-search-input" type="text" id="nav-search" name="q" placeholder="Find related posts..." autocomplete="off" />
<button type="submit" aria-label="Search" class="c-btn c-btn--icon-alone absolute inset-px right-auto mt-0 py-0">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="aly4aviirjsewlw70fof71wi6zoo3aei" aria-hidden="true" class="crayons-icon"><title id="aly4aviirjsewlw70fof71wi6zoo3aei">Search</title>
<path d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0111 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 01-1.969 5.617zm-2.006-.742A6.977 6.977 0 0018 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 004.875-1.975l.15-.15z"></path>
</svg>
</button>
<a class="crayons-header--search-brand-indicator" href="https://www.algolia.com/developers/?utm_source=devto&utm_medium=referral" target="_blank" rel="noopener noreferrer">
Powered by Algolia
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" width="24" height="24" viewBox="0 0 500 500.34" role="img" aria-labelledby="a7epo2h50ncrbql9hh9pvy85utgrnjy4" aria-hidden="true" class="crayons-icon"><title id="a7epo2h50ncrbql9hh9pvy85utgrnjy4">Search</title>
<defs></defs><path class="cls-1" d="M250,0C113.38,0,2,110.16,.03,246.32c-2,138.29,110.19,252.87,248.49,253.67,42.71,.25,83.85-10.2,120.38-30.05,3.56-1.93,4.11-6.83,1.08-9.52l-23.39-20.74c-4.75-4.22-11.52-5.41-17.37-2.92-25.5,10.85-53.21,16.39-81.76,16.04-111.75-1.37-202.04-94.35-200.26-206.1,1.76-110.33,92.06-199.55,202.8-199.55h202.83V407.68l-115.08-102.25c-3.72-3.31-9.43-2.66-12.43,1.31-18.47,24.46-48.56,39.67-81.98,37.36-46.36-3.2-83.92-40.52-87.4-86.86-4.15-55.28,39.65-101.58,94.07-101.58,49.21,0,89.74,37.88,93.97,86.01,.38,4.28,2.31,8.28,5.53,11.13l29.97,26.57c3.4,3.01,8.8,1.17,9.63-3.3,2.16-11.55,2.92-23.6,2.07-35.95-4.83-70.39-61.84-127.01-132.26-131.35-80.73-4.98-148.23,58.18-150.37,137.35-2.09,77.15,61.12,143.66,138.28,145.36,32.21,.71,62.07-9.42,86.2-26.97l150.36,133.29c6.45,5.71,16.62,1.14,16.62-7.48V9.49C500,4.25,495.75,0,490.51,0H250Z"></path>
</svg>
</a>
</div>
</div>
</form>
</div>
<div class="flex items-center h-100 ml-auto">
<div class="flex" id="authentication-top-nav-actions">
<span class="hidden m:block">
<a href="https://dev.to/enter?signup_subforem=1" class="c-link c-link--block mr-2 whitespace-nowrap ml-auto" data-no-instant>
Log in
</a>
</span>
<a href="https://dev.to/enter?signup_subforem=1&amp;state=new-user" data-tracking-id="ca_top_nav" data-tracking-source="top_navbar" class="c-cta c-cta--branded whitespace-nowrap mr-2" data-no-instant>
Create account
</a>
</div>
</div>
</div>
</header>
<div class="hamburger">
<div class="hamburger__content">
<header class="hamburger__content__header">
<h2 class="fs-l fw-bold flex-1 break-word lh-tight">DEV Community</h2>
<button class="c-btn c-btn--icon-alone js-hamburger-trigger shrink-0" aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="ack7fhmanansdxco6fg9m95mbynalbpi" aria-hidden="true" class="crayons-icon c-btn__icon"><title id="ack7fhmanansdxco6fg9m95mbynalbpi">Close</title><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636l4.95 4.95z"></path></svg>
</button>
</header>
<div class="p-2 js-navigation-links-container" id="authentication-hamburger-actions">
</div>
</div>
<div class="hamburger__overlay js-hamburger-trigger"></div>
</div>
<div id="active-broadcast" class="broadcast-wrapper"></div>
<div id="page-content" class="wrapper stories stories-show articletag-monitoring articletag-dataengineering articleuser-841884" data-current-page="stories-show">
<div id="page-content-inner" data-internal-nav="false" data-viewable-id="1425662" data-viewable-type="Article">
<div id="page-route-change" class="screen-reader-only" aria-live="polite" aria-atomic="true"></div>
<style>
.html-variant-wrapper { display: none}
</style>
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.2.10/webcomponents-loader.js"
integrity="sha384-3HK5hxQbkFqOIxMbpROlRmRtYl2LBZ52t+tqcjzsmr9NJuOWQxl8RgQSyFvq2lhy"
crossorigin="anonymous" defer></script>
<script src="https://assets.dev.to/assets/webShare-0686f0b9ac40589694ef6ae6a6202c44119bc781c254f6cf6d52d8a008461156.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/articlePage-7386c745667022e7147503d288454cfce459f2019ee90ce454e641a116844837.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/commentDropdowns-7a28d130e5b78d38b30a9495a964003a66bd64fa455fc70b766d69cf06b9ba24.js" defer="defer"></script>
<script type="application/ld+json">
{"@context":"http://schema.org","@type":"Article","mainEntityOfPage":{"@type":"WebPage","@id":"https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48"},"url":"https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48","image":["https://media2.dev.to/dynamic/image/width=1080,height=1080,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88ajamndcnidzsp4n8jv.png","https://media2.dev.to/dynamic/image/width=1280,height=720,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88ajamndcnidzsp4n8jv.png","https://media2.dev.to/dynamic/image/width=1600,height=900,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88ajamndcnidzsp4n8jv.png"],"publisher":{"@context":"http://schema.org","@type":"Organization","name":"DEV Community","logo":{"@context":"http://schema.org","@type":"ImageObject","url":"https://media2.dev.to/dynamic/image/width=192,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png","width":"192","height":"192"}},"headline":"The Pyramid of Alerting","author":{"@context":"http://schema.org","@type":"Person","url":"https://dev.to/karelvandenbussche","name":"Karel Vanden Bussche"},"datePublished":"2023-04-04T10:56:57Z","dateModified":"2023-04-04T10:56:57Z","mainEntity":{"@type":"DiscussionForumPosting","@id":"#article-discussion-1425662","headline":"The Pyramid of Alerting","text":"\u003cp\u003eWe all came across it before. Your company is processing thousands of data points. All goes well on most days, but today, the pipelines are saying \"not today\". You start digging and find out that the issue is due to a single invalid or malformed message. You check the message for issues, identify the breaking issue (it's a new date format, who would have guessed) and implement a fail-safe for this specific issue.\u003c/p\u003e\n\n\u003cp\u003eSome people might say \"Well, you lost quite a bit of time digging for the root cause, why didn't you program the fail-safe in the first place?\". As data-engineers, we are well-trained in the art of balance our efforts to stay efficient \u0026amp; provide business value on the other end. There are hundreds of integrations to be made, but only little time and competitors don't wait. Secondly, data is so chaotic, that it is impossible to create guards against each mutation. Lastly, each hour of programming, has a business cost. As such, the new integrations have priority over a few edge-cases, such as faulty files or wrong assumptions. That is completely fine if you weave your net around the weakest points. It's only when certain assumptions weren't validated that the issues start.\u003c/p\u003e\n\n\u003cp\u003eThis balance between efficiency and robustness is an important equilibrium. The fact that postponing certain complex fail-safe developments brings more efficiency to the team even if the issue bites back later. The 2 cards that keeps everything from falling down in this precarious card stack, are monitoring and alerting. It sounds boring to most people, but monitoring can really help identify silently failing processes in your infrastructure. These failures might grind your pipeline to a halt or could silently destroy data integrity.\u003c/p\u003e\n\n\u003cp\u003eAt OTA Insight, we try to actively work on moving issues from the silent category to the alerting category. Over the years, we have had both types of impact arise, though quickly worked to resolve them permanently.\u003cbr\u003e\nSometimes our messages fail to transform and due to our aggressive retry-strategy, we keep on retrying these invalid or corrupt messages. When this issue is not resolved, the pipeline has significantly lower throughput and we lose precious CPU-time.\u003cbr\u003e\nOther times, our integrity checks fail, meaning we did not know we are not loading any data for multiple days.\u003cbr\u003e\nThinking about what impact a new flow might have and brainstorming alerting at scope-time might help resolve the biggest issues that are low-hanging fruit.\u003c/p\u003e\n\n\u003ch2\u003e\n \u003ca name=\"the-levels-of-monitoring-amp-alerts\" href=\"#the-levels-of-monitoring-amp-alerts\"\u003e\n \u003c/a\u003e\n The levels of monitoring \u0026amp; alerts\n\u003c/h2\u003e\n\n\u003cp\u003eOver the years, our team has learned valuable lessons on monitoring. When we now implement a new solution, we have a few key levels of monitoring we plan for. The image below shows the hierarchy of alerts we currently look at for new flows or business logic.\u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjd8w4mx7n8s5wdmpwa.png\" class=\"article-body-image-wrapper\"\u003e\u003cimg src=\"https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjd8w4mx7n8s5wdmpwa.png\" alt=\"Image description\" loading=\"lazy\" width=\"800\" height=\"339\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n\u003cp\u003eIn the following sections, we'll go over each of them, bottom to top, and show what they entail and how they help us recover from certain disasters.\u003c/p\u003e\n\n\u003ch3\u003e\n \u003ca name=\"operational-alerts\" href=\"#operational-alerts\"\u003e\n \u003c/a\u003e\n Operational alerts\n\u003c/h3\u003e\n\n\u003cp\u003eThe first type of alerts are the operation alerts. This category focusses on making sure that everything keeps running smoothly at the service/bare-metal level. These alerts monitor for example how many messages are in a certain queue and what the age of the oldest message is. This gives us a good idea if our processes are healthy.\u003c/p\u003e\n\n\u003cp\u003eOperational alerting should have an internal hierarchy. Each operation will encounter spikes at multiple points during their lifetime. This means that certain alerts might be raised falsely or their importance might be inflated. As such, we deem it worthwhile to define multiple steps of thresholds, each with its own importance. High importance alerts should be triggered as soon as possible, while low importance alerts should be looked into, but not necessarily now. In most cases, it is possible that a low importance alert evolves into a high importance alert. Due to this, it's also valuable to look into low importance alerts before they evolve and cripple the rest of the data pipelines.\u003c/p\u003e\n\n\u003cp\u003eSay we did not have these alerts. That would mean that when our processes wouldn't be healthy, no alarm bells would start ringing. This would be a disaster, as an invisible issue will not be actioned rapidly and might go on for days or weeks without anyone knowing.\u003c/p\u003e\n\n\u003ch3\u003e\n \u003ca name=\"data-validation\" href=\"#data-validation\"\u003e\n \u003c/a\u003e\n Data validation\n\u003c/h3\u003e\n\n\u003cp\u003eData validation has a lot of definitions, so let me explain what I mean with this first. The previous alert gave an overview of operational load \u0026amp; throughput. What it did not show was if certain values were received and if the amount of data was (more or less) correct. Other metrics that fall under data validation are:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eDid we receive (enough) files for a given date?\u003c/li\u003e\n\u003cli\u003eWas the amount of data relatively close to the average amount of ingested rows of previous days?\u003c/li\u003e\n\u003cli\u003eCan the incoming files be unzipped/loaded correctly?\u003c/li\u003e\n\u003cli\u003eDid we receive the file at the correct timestamp?\u003c/li\u003e\n\u003cli\u003e...\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eThe validations are slightly more complex, as the implementer should have some prior knowledge over the dataset. In the case of operational alerts, no prior knowledge was needed. The knowledge is quite superficial, as we're only looking at the \u003cstrong\u003eamount\u003c/strong\u003e of data and not the semantics of the data itself.\u003c/p\u003e\n\n\u003cp\u003eData validations might save your skin in cases where there is an issue with the ingestion or messages are silently dropped in your infrastructure. As such, these validation are very valuable, because, what is a data business without data?\u003c/p\u003e\n\n\u003ch3\u003e\n \u003ca name=\"business-assumptions\" href=\"#business-assumptions\"\u003e\n \u003c/a\u003e\n Business Assumptions\n\u003c/h3\u003e\n\n\u003cp\u003eAs we're now fairly certain that we have the data in our pipeline and it is flowing through correctly, we can start posing the question if our data is correct. As this requires a more in-depth knowledge of semantics of the data, this piece of alerting should be owned by the team that owns the data. At OTA Insight, that's the team that ingests it in most cases, but sometimes it's the consuming team that knows much better how it should be handled.\u003c/p\u003e\n\n\u003cp\u003eBusiness assumptions come in all flavours. To give an idea, I'll give a few examples:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eWe assume the header of the file always has a certain date format, but we're not sure if this will transfer to newly received files.\u003c/li\u003e\n\u003cli\u003eWe assume 2 values always add up to a third value. If this is incorrect, our next calculations cascade the faultiness.\u003c/li\u003e\n\u003cli\u003eAre the values in a certain row correct according to our initial research?\u003c/li\u003e\n\u003cli\u003eAre values within a certain range without too many outliers?\u003c/li\u003e\n\u003cli\u003e...\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eThese alerts are a bit harder to implement, as they can happen at different levels of your processing. This means that each data source should have a way of surfacing these invalidated assumptions.\u003c/p\u003e\n\n\u003cp\u003ePreferably, these fallbacks start very broad, warning at any assumptions that are broken. As time goes by, certain assumptions can be (in)validated and removed from our net, or added as hard-fails if the assumption signals an actual error. After a while, our assumptions will converge to a realistic mirror of the actual underlying semantics.\u003c/p\u003e\n\n\u003cp\u003eImplementing this is sometimes hard, as the decision needs to be made to use either a warning or an error. Throwing too many or not enough errors might skew the data or impact data quality. On the other hand, having too many warnings makes it hard to filter between truth and error. As such, this should be implemented by someone who has both a good understanding of the underlying data, but also the data source.\u003c/p\u003e\n\n\u003cp\u003eWhen implemented, these alerts give your pipelines a safety net against invalid data. They also provide a looking glass into your assumptions, which can help you root out the invalid assumptions and reinforce the valid ones.\u003c/p\u003e\n\n\u003ch2\u003e\n \u003ca name=\"conclusion\" href=\"#conclusion\"\u003e\n \u003c/a\u003e\n Conclusion\n\u003c/h2\u003e\n\n\u003cp\u003ePeople that work with Big Data know that it contains chaos. Without some kind of insights, finding \u0026amp; lowering edge cases is practically impossible. Adding different kinds of alerts helps us engineers cluster this chaos into smaller packets that can be solved independently. The result is more manageable errors and targeted failsafes, which ultimately increases the robustness of both processors and data.\u003c/p\u003e\n\n\u003cp\u003eThe hierarchy in alerts makes it easy to delegate certain responsibilities to different teams. It also gives an idea on what knowledge is required for certain levels of fallbacks. Lastly, it gives an idea of the implementation complexity at each depth. Depending on your data source, you might want to weigh the costs against the boons of adding a certain validation.\u003c/p\u003e\n\n","author":{"@type":"Person","name":"Karel Vanden Bussche","url":"https://dev.to/karelvandenbussche"},"datePublished":"2023-04-04T10:56:57Z","dateModified":"2023-04-04T10:56:57Z","url":"https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48","interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":7}],"comment":[{"@type":"Comment","@id":"#comment-1057677","text":"\u003cp\u003eAwesome, thanks for sharing!!\u003c/p\u003e\n\n","author":{"@type":"Person","name":"Jhonny Pong","url":"https://dev.to/jthan24"},"datePublished":"2023-07-22T18:16:29Z","dateModified":"2023-07-22T18:16:29Z","url":"https://dev.to/jthan24/comment/284fn","interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":1}]}]}}
</script>
<div class="crayons-layout crayons-layout--3-cols crayons-layout--article">
<aside class="crayons-layout__sidebar-left" aria-label="Article actions">
<script>
if (/Twitter for (iPhone|iPad|Android)/i.test(navigator.userAgent)) {
document.documentElement.classList.add('is-twitter-in-app');
}
</script>
<style>
.community-leader-visible-block {
display: none !important;
}
.trusted-visible-block {
display: none !important;
}
.only-sidebar-menu-item {
display: none !important;
}
@media (min-width: 640px) {
body.community-leader-status-true .only-sidebar-menu-item.community-leader-visible-block {
display: block !important;
}
body.trusted-status-true .only-sidebar-menu-item.trusted-visible-block {
display: block !important;
}
}
.only-mobile-menu-item {
display: none !important;
}
body.community-leader-status-true .only-mobile-menu-item.community-leader-visible-block {
display: block !important;
}
body.trusted-status-true .only-mobile-menu-item.trusted-visible-block {
display: block !important;
}
@media (min-width: 640px) {
body.community-leader-status-true .only-mobile-menu-item.community-leader-visible-block {
display: none !important;
}
body.trusted-status-true .only-mobile-menu-item.trusted-visible-block {
display: none !important;
}
}
.crayons-reaction__count {
line-height: 18px !important;
height: 18px !important;
min-height: 18px !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
@media (min-width: 768px) {
.crayons-reaction__count {
display: flex !important;
}
}
.crayons-reaction__count > span {
height: 16px !important;
width: 16px !important;
padding: 0 !important;
display: inline-block !important;
}
</style>
<div class="crayons-article-actions print-hidden">
<div class="crayons-article-actions__inner">
<div class="reaction-drawer__outer hoverdown" style="">
<button
id="reaction-drawer-trigger"
aria-label="reaction-drawer-trigger"
aria-pressed="false"
class="hoverdown-trigger crayons-reaction pseudo-reaction crayons-tooltip__activator relative">
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction--like crayons-reaction__icon--inactive" style="width: 40px; height: 40px">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" role="img" aria-hidden="true" class="crayons-icon">
<g clip-path="url(#clip0_988_3276)">
<path d="M19 14V17H22V19H18.999L19 22H17L16.999 19H14V17H17V14H19ZM20.243 4.75698C22.505 7.02498 22.583 10.637 20.479 12.992L19.059 11.574C20.39 10.05 20.32 7.65998 18.827 6.16998C17.324 4.67098 14.907 4.60698 13.337 6.01698L12.002 7.21498L10.666 6.01798C9.09103 4.60598 6.67503 4.66798 5.17203 6.17198C3.68203 7.66198 3.60703 10.047 4.98003 11.623L13.412 20.069L12 21.485L3.52003 12.993C1.41603 10.637 1.49503 7.01898 3.75603 4.75698C6.02103 2.49298 9.64403 2.41698 12 4.52898C14.349 2.41998 17.979 2.48998 20.242 4.75698H20.243Z" fill="#525252"></path>
</g>
<defs>
<clipPath id="clip0_988_3276">
<rect width="24" height="24" fill="white"></rect>
</clipPath>
</defs>
</svg>
</span>
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction__icon--active" style="width: 40px; height: 40px">
<img aria_hidden="true" height="24" width="24" src="https://assets.dev.to/assets/heart-plus-active-9ea3b22f2bc311281db911d416166c5f430636e76b15cd5df6b3b841d830eefa.svg" />
</span>
<span class="crayons-reaction__count" id="reaction_total_count">
<span class="bg-base-40 opacity-25 inline-block radius-default" style="height: 16px; width: 16px;"></span>
</span>
<span class="crayons-tooltip__content">
Add reaction
</span>
</button>
<div class="reaction-drawer" aria-expanded="false">
<div class="reaction-drawer__container">
<button
id="reaction-butt-like"
name="Like"
aria-label="Like"
aria-pressed="false"
class="crayons-reaction crayons-tooltip__activator relative pt-2 pr-2 pb-1 pl-2"
data-category="like">
<span class="crayons-reaction__icon crayons-reaction__icon--inactive p-0">
<img aria_hidden="true" height="32" width="32" src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" />
</span>
<span class="crayons-reaction__count" id="reaction-number-like"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Like
</span>
</button>
<button
id="reaction-butt-unicorn"
name="Unicorn"
aria-label="Unicorn"
aria-pressed="false"
class="crayons-reaction crayons-tooltip__activator relative pt-2 pr-2 pb-1 pl-2"
data-category="unicorn">
<span class="crayons-reaction__icon crayons-reaction__icon--inactive p-0">
<img aria_hidden="true" height="32" width="32" src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" />
</span>
<span class="crayons-reaction__count" id="reaction-number-unicorn"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Unicorn
</span>
</button>
<button
id="reaction-butt-exploding_head"
name="Exploding Head"
aria-label="Exploding Head"
aria-pressed="false"
class="crayons-reaction crayons-tooltip__activator relative pt-2 pr-2 pb-1 pl-2"
data-category="exploding_head">
<span class="crayons-reaction__icon crayons-reaction__icon--inactive p-0">
<img aria_hidden="true" height="32" width="32" src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" />
</span>
<span class="crayons-reaction__count" id="reaction-number-exploding_head"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Exploding Head
</span>
</button>
<button
id="reaction-butt-raised_hands"
name="Raised Hands"
aria-label="Raised Hands"
aria-pressed="false"
class="crayons-reaction crayons-tooltip__activator relative pt-2 pr-2 pb-1 pl-2"
data-category="raised_hands">
<span class="crayons-reaction__icon crayons-reaction__icon--inactive p-0">
<img aria_hidden="true" height="32" width="32" src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" />
</span>
<span class="crayons-reaction__count" id="reaction-number-raised_hands"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Raised Hands
</span>
</button>
<button
id="reaction-butt-fire"
name="Fire"
aria-label="Fire"
aria-pressed="false"
class="crayons-reaction crayons-tooltip__activator relative pt-2 pr-2 pb-1 pl-2"
data-category="fire">
<span class="crayons-reaction__icon crayons-reaction__icon--inactive p-0">
<img aria_hidden="true" height="32" width="32" src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" />
</span>
<span class="crayons-reaction__count" id="reaction-number-fire"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Fire
</span>
</button>
</div>
</div>
</div>
<button
id="reaction-butt-comment"
aria-label="Jump to Comments"
aria-pressed="false"
class="crayons-reaction crayons-reaction--comment crayons-tooltip__activator relative"
data-category="comment">
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction__icon--inactive">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-hidden="true" class="crayons-icon">
<path d="M10 3h4a8 8 0 010 16v3.5c-5-2-12-5-12-11.5a8 8 0 018-8zm2 14h2a6 6 0 000-12h-4a6 6 0 00-6 6c0 3.61 2.462 5.966 8 8.48V17z"></path>
</svg>
</span>
<span class="crayons-reaction__count" id="reaction-number-comment" data-count="1">
<span class="bg-base-40 opacity-25 inline-block radius-default" style="height: 16px; width: 16px;"></span>
</span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Jump to Comments
</span>
</button>
<button
id="reaction-butt-readinglist"
aria-label="Add to reading list"
aria-pressed="false"
class="crayons-reaction crayons-reaction--readinglist crayons-tooltip__activator relative"
data-category="readinglist">
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction__icon--inactive">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-hidden="true" class="crayons-icon">
<path d="M5 2h14a1 1 0 011 1v19.143a.5.5 0 01-.766.424L12 18.03l-7.234 4.536A.5.5 0 014 22.143V3a1 1 0 011-1zm13 2H6v15.432l6-3.761 6 3.761V4z"></path>
</svg>
</span>
<span class="crayons-reaction__count" id="reaction-number-readinglist"><span class="bg-base-40 opacity-25 inline-block radius-default" style="height: 16px; width: 16px;"></span></span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Save
</span>
</button>
<button
id="reaction-butt-boost"
aria-label="Boost"
aria-pressed="false"
class="crayons-reaction crayons-reaction--boost crayons-tooltip__activator relative">
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction__icon--inactive">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" role="img" aria-hidden="true" class="crayons-icon" width="24" height="24">
<path transform="translate(24,0) scale(-1,1)" d="M6 4H21C21.5523 4 22 4.44772 22 5V12H20V6H6V9L1 5L6 1V4ZM18 20H3C2.44772 20 2 19.5523 2 19V12H4V18H18V15L23 19L18 23V20Z"></path>
</svg>
</span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Boost
</span>
</button>
<div class="only-sidebar-menu-item community-leader-visible-block align-center">
<span class="favorite-control-root" data-favorite-control="true" data-variant="article" data-favoritable-type="Article" data-favoritable-id="1425662" data-favoritable-user-id="841884" data-favorited="false" data-label-favorite="Pick as gem" data-label-favorited="Picked as gem" data-label-favorited-by-you="Picked as gem by you" data-modal-title="Gem Picked!" data-modal-body="You&#39;ve picked this as a community gem!" data-modal-remaining-zero="You have no more gems left to give out today." data-modal-remaining-one="You have 1 more gem left to give out today." data-modal-remaining-other="You have %{count} more gems left to give out today." data-modal-remaining-unlimited="You have unlimited gems to give out." data-modal-close="Got it" data-modal-exhausted-title="Out of Gems" data-modal-exhausted-body="You have no more gems left to give out today. Your allowance will refresh soon!" data-modal-exhausted-close="Got it"><button type="button" class="favorite-reaction favorite-control crayons-tooltip__activator relative" aria-label="Pick as gem"><span class="crayons-reaction__icon crayons-reaction__icon--borderless"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" class="crayons-icon crayons-icon--default"><path d="M5.95 3.5h12.1L23.5 9 12 20.5.5 9 5.95 3.5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</span><span data-testid="tooltip" class="crayons-tooltip__content">Pick as gem</span></button></span>
</div>
<div id="mod-actions-menu-btn-area" class="only-sidebar-menu-item trusted-visible-block align-center">
</div>
<div class="align-center m:relative">
<button id="article-show-more-button" aria-controls="article-show-more-dropdown" aria-expanded="false" aria-haspopup="true" class="dropbtn crayons-btn crayons-btn--ghost-dimmed crayons-btn--icon-rounded" aria-label="Share post options">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" role="img" aria-labelledby="aoo41aklheeazzbfgnnx3zlruqx9whgp" aria-hidden="true" class="crayons-icon dropdown-icon"><title id="aoo41aklheeazzbfgnnx3zlruqx9whgp">More...</title><path fill-rule="evenodd" clip-rule="evenodd" d="M7 12a2 2 0 11-4 0 2 2 0 014 0zm7 0a2 2 0 11-4 0 2 2 0 014 0zm5 2a2 2 0 100-4 2 2 0 000 4z"></path></svg>
</button>
<div id="article-show-more-dropdown" class="crayons-dropdown side-bar left-2 right-2 m:right-auto m:left-100 s:left-auto mb-1 m:mb-0 top-unset bottom-100 m:top-0 m:bottom-unset">
<div>
<button
id="copy-post-url-button"
class="flex justify-between crayons-link crayons-link--block w-100 bg-transparent border-0"
data-postUrl="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48">
<span class="fw-bold">Copy link</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="article-copy-icon" role="img" aria-labelledby="afxtydacpi3smr6ilew4h2yqojcw60t3" aria-hidden="true" class="crayons-icon mx-2 shrink-0"><title id="afxtydacpi3smr6ilew4h2yqojcw60t3">Copy link</title>
<path d="M7 6V3a1 1 0 011-1h12a1 1 0 011 1v14a1 1 0 01-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1 1 0 013 21l.003-14c0-.552.45-1 1.007-1H7zm2 0h8v10h2V4H9v2zm-2 5v2h6v-2H7zm0 4v2h6v-2H7z"></path>
</svg>
</button>
<div id="article-copy-link-announcer" aria-live="polite" class="crayons-notice crayons-notice--success my-2 p-1" aria-live="polite" hidden>Copied to Clipboard</div>
</div>
<div class="Desktop-only">
<a
target="_blank"
class="crayons-link crayons-link--block"
rel="noopener"
href='https://twitter.com/intent/tweet?text=%22The%20Pyramid%20of%20Alerting%22%20by%20Karel%20Vanden%20Bussche%20%23DEVCommunity%20https%3A%2F%2Fdev.to%2Fkarelvandenbussche%2Fthe-pyramid-of-alerting-1g48'>
Share to X
</a>
<a
target="_blank"
class="crayons-link crayons-link--block"
rel="noopener"
href="https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fdev.to%2Fkarelvandenbussche%2Fthe-pyramid-of-alerting-1g48&title=The%20Pyramid%20of%20Alerting&summary=We%20all%20came%20across%20it%20before.%20Your%20company%20is%20processing%20thousands%20of%20data%20points.%20All%20goes%20well%20on...&source=DEV%20Community">
Share to LinkedIn
</a>
<a
target="_blank"
class="crayons-link crayons-link--block"
rel="noopener"
href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Fdev.to%2Fkarelvandenbussche%2Fthe-pyramid-of-alerting-1g48">
Share to Facebook
</a>
<a
target="_blank"
class="crayons-link crayons-link--block"
rel="noopener"
href="https://s2f.kytta.dev/?text=https%3A%2F%2Fdev.to%2Fkarelvandenbussche%2Fthe-pyramid-of-alerting-1g48">
Share to Mastodon
</a>
</div>
<web-share-wrapper shareurl="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48" sharetitle="The Pyramid of Alerting" sharetext="We all came across it before. Your company is processing thousands of data points. All goes well on..." template="web-share-button">
</web-share-wrapper>
<template id="web-share-button">
<a href="#" class="dropdown-link-row crayons-link crayons-link--block">Share Post via...</a>
</template>
<a href="/report-abuse" class="crayons-link crayons-link--block" id="ReportButtonAuthor">Report Abuse</a>
</div>
</div>
</div>
</div>
</aside>
<main id="main-content" class="crayons-layout__content grid gap-4">
<div class="article-wrapper">
<article class="crayons-card crayons-article mb-4"
id="article-show-container"
data-article-id="1425662"
data-article-slug="the-pyramid-of-alerting-1g48"
data-author-id="841884"
data-author-name="Karel Vanden Bussche"
data-author-username="karelvandenbussche"
data-co-author-ids=""
data-path="/karelvandenbussche/the-pyramid-of-alerting-1g48"
data-pin-path="/stories/feed/pinned_article"
data-pinned-article-id=""
data-published="true"
data-scheduled="false"
>
<script>
try {
if(localStorage) {
let currentUser = localStorage.getItem('current_user');
if (currentUser) {
currentUser = JSON.parse(currentUser);
if (currentUser.id === 841884) {
document.getElementById('article-show-container').classList.add('current-user-is-article-author');
}
}
}
} catch (e) {
console.error(e);
}
</script>
<header class="crayons-article__header" id="main-title">
<div class="crayons-article__header__meta">
<div class="flex s:items-start flex-col s:flex-row">
<div id="action-space" class="crayons-article__actions mb-4 s:mb-0 s:order-last"></div>
<div class="flex flex-1 mb-5 items-start">
<div class="relative">
<a href="/karelvandenbussche"><img class="radius-full align-middle" src="https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F841884%2F011c5edc-2de3-4d83-ae8a-cdc1dbb15739.jpeg" width="40" height="40" alt="Karel Vanden Bussche" /></a>
</div>
<div class="pl-3 flex-1">
<a href="/karelvandenbussche" class="crayons-link fw-bold">Karel Vanden Bussche</a>
<p class="fs-xs color-base-60 flex flex-wrap items-center gap-1">
<span>Posted on <time datetime="2023-04-04T10:56:57Z" class="date">Apr 4, 2023</time></span>
</p>
</div>
</div>
</div>
<div class="multiple_reactions_engagement">
<button
class="reaction_engagement_like hidden crayons-reaction crayons-reaction--like"
data-category="like"
aria-label="Like"
type="button"
><span class="crayons-reaction__icon crayons-reaction__icon--inactive p-1"><img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24" /></span>
<span class="crayons-reaction__count" id="reaction_engagement_like_count">&nbsp;</span>
</button> <button
class="reaction_engagement_unicorn hidden crayons-reaction crayons-reaction--unicorn"
data-category="unicorn"
aria-label="Unicorn"
type="button"
><span class="crayons-reaction__icon crayons-reaction__icon--inactive p-1"><img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24" /></span>
<span class="crayons-reaction__count" id="reaction_engagement_unicorn_count">&nbsp;</span>
</button> <button
class="reaction_engagement_exploding_head hidden crayons-reaction crayons-reaction--exploding_head"
data-category="exploding_head"
aria-label="Exploding Head"
type="button"
><span class="crayons-reaction__icon crayons-reaction__icon--inactive p-1"><img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24" /></span>
<span class="crayons-reaction__count" id="reaction_engagement_exploding_head_count">&nbsp;</span>
</button> <button
class="reaction_engagement_raised_hands hidden crayons-reaction crayons-reaction--raised_hands"
data-category="raised_hands"
aria-label="Raised Hands"
type="button"
><span class="crayons-reaction__icon crayons-reaction__icon--inactive p-1"><img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24" /></span>
<span class="crayons-reaction__count" id="reaction_engagement_raised_hands_count">&nbsp;</span>
</button> <button
class="reaction_engagement_fire hidden crayons-reaction crayons-reaction--fire"
data-category="fire"
aria-label="Fire"
type="button"
><span class="crayons-reaction__icon crayons-reaction__icon--inactive p-1"><img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24" /></span>
<span class="crayons-reaction__count" id="reaction_engagement_fire_count">&nbsp;</span>
</button>
</div>
<h1 class=" fs-3xl m:fs-4xl l:fs-5xl fw-bold s:fw-heavy lh-tight mb-2 medium">
The Pyramid of Alerting
</h1>
<div class="spec__tags flex flex-wrap">
<a class="crayons-tag " style="
--tag-bg: rgba(255, 163, 100, 0.10);
--tag-prefix: #ffa364;
--tag-bg-hover: rgba(255, 163, 100, 0.10);
--tag-prefix-hover: #ffa364;
" href="/t/monitoring"><span class="crayons-tag__prefix">#</span>monitoring</a>
<a class="crayons-tag " style="
--tag-bg: rgba(64, 78, 211, 0.10);
--tag-prefix: #404ED3;
--tag-bg-hover: rgba(64, 78, 211, 0.10);
--tag-prefix-hover: #404ED3;
" href="/t/dataengineering"><span class="crayons-tag__prefix">#</span>dataengineering</a>
</div>
</div>
</header>
<div class="crayons-article__main ">
<div class="crayons-article__body text-styles spec__body" data-article-id="1425662"
id="article-body">
<p>We all came across it before. Your company is processing thousands of data points. All goes well on most days, but today, the pipelines are saying "not today". You start digging and find out that the issue is due to a single invalid or malformed message. You check the message for issues, identify the breaking issue (it's a new date format, who would have guessed) and implement a fail-safe for this specific issue.</p>
<p>Some people might say "Well, you lost quite a bit of time digging for the root cause, why didn't you program the fail-safe in the first place?". As data-engineers, we are well-trained in the art of balance our efforts to stay efficient &amp; provide business value on the other end. There are hundreds of integrations to be made, but only little time and competitors don't wait. Secondly, data is so chaotic, that it is impossible to create guards against each mutation. Lastly, each hour of programming, has a business cost. As such, the new integrations have priority over a few edge-cases, such as faulty files or wrong assumptions. That is completely fine if you weave your net around the weakest points. It's only when certain assumptions weren't validated that the issues start.</p>
<p>This balance between efficiency and robustness is an important equilibrium. The fact that postponing certain complex fail-safe developments brings more efficiency to the team even if the issue bites back later. The 2 cards that keeps everything from falling down in this precarious card stack, are monitoring and alerting. It sounds boring to most people, but monitoring can really help identify silently failing processes in your infrastructure. These failures might grind your pipeline to a halt or could silently destroy data integrity.</p>
<p>At OTA Insight, we try to actively work on moving issues from the silent category to the alerting category. Over the years, we have had both types of impact arise, though quickly worked to resolve them permanently.<br>
Sometimes our messages fail to transform and due to our aggressive retry-strategy, we keep on retrying these invalid or corrupt messages. When this issue is not resolved, the pipeline has significantly lower throughput and we lose precious CPU-time.<br>
Other times, our integrity checks fail, meaning we did not know we are not loading any data for multiple days.<br>
Thinking about what impact a new flow might have and brainstorming alerting at scope-time might help resolve the biggest issues that are low-hanging fruit.</p>
<h2>
<a name="the-levels-of-monitoring-amp-alerts" href="#the-levels-of-monitoring-amp-alerts">
</a>
The levels of monitoring &amp; alerts
</h2>
<p>Over the years, our team has learned valuable lessons on monitoring. When we now implement a new solution, we have a few key levels of monitoring we plan for. The image below shows the hierarchy of alerts we currently look at for new flows or business logic.</p>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjd8w4mx7n8s5wdmpwa.png" class="article-body-image-wrapper"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjd8w4mx7n8s5wdmpwa.png" alt="Image description" loading="lazy" width="800" height="339"></a></p>
<p>In the following sections, we'll go over each of them, bottom to top, and show what they entail and how they help us recover from certain disasters.</p>
<h3>
<a name="operational-alerts" href="#operational-alerts">
</a>
Operational alerts
</h3>
<p>The first type of alerts are the operation alerts. This category focusses on making sure that everything keeps running smoothly at the service/bare-metal level. These alerts monitor for example how many messages are in a certain queue and what the age of the oldest message is. This gives us a good idea if our processes are healthy.</p>
<p>Operational alerting should have an internal hierarchy. Each operation will encounter spikes at multiple points during their lifetime. This means that certain alerts might be raised falsely or their importance might be inflated. As such, we deem it worthwhile to define multiple steps of thresholds, each with its own importance. High importance alerts should be triggered as soon as possible, while low importance alerts should be looked into, but not necessarily now. In most cases, it is possible that a low importance alert evolves into a high importance alert. Due to this, it's also valuable to look into low importance alerts before they evolve and cripple the rest of the data pipelines.</p>
<p>Say we did not have these alerts. That would mean that when our processes wouldn't be healthy, no alarm bells would start ringing. This would be a disaster, as an invisible issue will not be actioned rapidly and might go on for days or weeks without anyone knowing.</p>
<h3>
<a name="data-validation" href="#data-validation">
</a>
Data validation
</h3>
<p>Data validation has a lot of definitions, so let me explain what I mean with this first. The previous alert gave an overview of operational load &amp; throughput. What it did not show was if certain values were received and if the amount of data was (more or less) correct. Other metrics that fall under data validation are:</p>
<ul>
<li>Did we receive (enough) files for a given date?</li>
<li>Was the amount of data relatively close to the average amount of ingested rows of previous days?</li>
<li>Can the incoming files be unzipped/loaded correctly?</li>
<li>Did we receive the file at the correct timestamp?</li>
<li>...</li>
</ul>
<p>The validations are slightly more complex, as the implementer should have some prior knowledge over the dataset. In the case of operational alerts, no prior knowledge was needed. The knowledge is quite superficial, as we're only looking at the <strong>amount</strong> of data and not the semantics of the data itself.</p>
<p>Data validations might save your skin in cases where there is an issue with the ingestion or messages are silently dropped in your infrastructure. As such, these validation are very valuable, because, what is a data business without data?</p>
<h3>
<a name="business-assumptions" href="#business-assumptions">
</a>
Business Assumptions
</h3>
<p>As we're now fairly certain that we have the data in our pipeline and it is flowing through correctly, we can start posing the question if our data is correct. As this requires a more in-depth knowledge of semantics of the data, this piece of alerting should be owned by the team that owns the data. At OTA Insight, that's the team that ingests it in most cases, but sometimes it's the consuming team that knows much better how it should be handled.</p>
<p>Business assumptions come in all flavours. To give an idea, I'll give a few examples:</p>
<ul>
<li>We assume the header of the file always has a certain date format, but we're not sure if this will transfer to newly received files.</li>
<li>We assume 2 values always add up to a third value. If this is incorrect, our next calculations cascade the faultiness.</li>
<li>Are the values in a certain row correct according to our initial research?</li>
<li>Are values within a certain range without too many outliers?</li>
<li>...</li>
</ul>
<p>These alerts are a bit harder to implement, as they can happen at different levels of your processing. This means that each data source should have a way of surfacing these invalidated assumptions.</p>
<p>Preferably, these fallbacks start very broad, warning at any assumptions that are broken. As time goes by, certain assumptions can be (in)validated and removed from our net, or added as hard-fails if the assumption signals an actual error. After a while, our assumptions will converge to a realistic mirror of the actual underlying semantics.</p>
<p>Implementing this is sometimes hard, as the decision needs to be made to use either a warning or an error. Throwing too many or not enough errors might skew the data or impact data quality. On the other hand, having too many warnings makes it hard to filter between truth and error. As such, this should be implemented by someone who has both a good understanding of the underlying data, but also the data source.</p>
<p>When implemented, these alerts give your pipelines a safety net against invalid data. They also provide a looking glass into your assumptions, which can help you root out the invalid assumptions and reinforce the valid ones.</p>
<h2>
<a name="conclusion" href="#conclusion">
</a>
Conclusion
</h2>
<p>People that work with Big Data know that it contains chaos. Without some kind of insights, finding &amp; lowering edge cases is practically impossible. Adding different kinds of alerts helps us engineers cluster this chaos into smaller packets that can be solved independently. The result is more manageable errors and targeted failsafes, which ultimately increases the robustness of both processors and data.</p>
<p>The hierarchy in alerts makes it easy to delegate certain responsibilities to different teams. It also gives an idea on what knowledge is required for certain levels of fallbacks. Lastly, it gives an idea of the implementation complexity at each depth. Depending on your data source, you might want to weigh the costs against the boons of adding a certain validation.</p>
</div>
<div class="below-post-bb body-billboard-container" data-async-url="/karelvandenbussche/the-pyramid-of-alerting-1g48/bmar11/post_body_bottom"></div>
</div>
<section id="comments" data-follow-button-container="true" data-updated-at="2026-09-11 00:03:43 UTC" class="text-padding mb-4 border-t-1 border-0 border-solid border-base-10">
<header class="relative flex justify-between items-center mb-6">
<div class="flex items-center">
<h2 class="crayons-subtitle-1">
Top comments <span class="js-comments-count" data-comments-count="1">(1)</span>
</h2>
</div>
<div id="comment-subscription" class="print-hidden">
<div class="crayons-btn-group">
<span class="crayons-btn crayons-btn--outlined">Subscribe</span>
</div>
</div>
</header>
<div id="billboard_delay_trigger"></div>
<div
id="comments-container"
data-testid="comments-container"
data-commentable-id="1425662"
data-commentable-type="Article"
data-has-recent-comment-activity="false">
<div id="response-templates-data" class="hidden"></div>
<form class="comment-form print-hidden" id="new_comment" action="/comments" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="NOTHING" id="new_comment_authenticity_token">
<input value="1425662" autocomplete="off" type="hidden" name="comment[commentable_id]" id="comment_commentable_id" />
<input value="Article" autocomplete="off" type="hidden" name="comment[commentable_type]" id="comment_commentable_type" />
<span class="crayons-avatar m:crayons-avatar--l mr-2 shrink-0">
<img src="https://media2.dev.to/dynamic/image/width=256,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" width="32" height="32" alt="pic" class="crayons-avatar__image overflow-hidden" id="comment-primary-user-profile--avatar" loading="lazy" />
</span>
<div class="comment-form__inner">
<div class="comment-form__field" data-tracking-name="comment_form_textfield">
<textarea placeholder="Add to the discussion" onfocus="handleFocus(event)" onkeyup="handleKeyUp(event)" onkeydown="handleKeyDown(event)" oninput="handleChange(event)" id="text-area" required="required" class="crayons-textfield comment-textarea crayons-textfield--ghost" aria-label="Add a comment to the discussion" name="comment[body_markdown]">
</textarea>
</div>
<div class="response-templates-container crayons-card crayons-card--secondary p-4 mb-4 comment-form__templates fs-base hidden">
<header class="mb-3">
<button type="button" class="crayons-btn personal-template-button active" data-target-type="personal" data-form-id="new_comment">Personal</button>
<button type="button" class="crayons-btn moderator-template-button hidden" data-target-type="moderator" data-form-id="new_comment">Trusted User</button>
</header>
<div class="personal-responses-container">
</div>
<div class="moderator-responses-container hidden">
</div>
<a target="_blank" rel="noopener nofollow" href="/settings/response-templates">
Create template
</a>
<p>Templates let you quickly answer FAQs or store snippets for re-use.</p>
</div>
<div class="comment-form__preview text-styles text-styles--secondary" id="preview-div"></div>
<div class="comment-form__buttons mb-4 flex items-center flex-wrap gap-2">
<button type="submit" class="crayons-btn mr-2 js-btn-enable" onclick="validateField(event)" data-tracking-name="comment_submit_button" disabled>Submit</button>
<button type="button" class="preview-toggle crayons-btn crayons-btn--secondary comment-action-preview js-btn-enable mr-2" data-tracking-name="comment_preview_button" disabled>Preview</button>
<a href="/404.html" class="dismiss-edit-comment crayons-btn crayons-btn--ghost js-btn-dismiss hidden">Dismiss</a>
</div>
</div>
<div class="code-of-conduct" id="toggle-code-of-conduct-checkbox"></div>
</form>
<div class="comments" id="comment-trees-container">
<details class="comment-wrapper js-comment-wrapper comment-wrapper--deep-0
root
" open>
<summary aria-label="Toggle this comment (and replies)" data-tracking-name="expand_comment_toggle">
<span class="m:mx-1 inline-block align-middle">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="argzz7s7wawix72t0sh9zchyvobi0mnz" class="crayons-icon expanded"><title id="argzz7s7wawix72t0sh9zchyvobi0mnz">Collapse</title>
<path d="M12 10.677L8 6.935 9 6l3 2.807L15 6l1 .935-4 3.742zm0 4.517L9 18l-1-.935 4-3.742 4 3.742-1 .934-3-2.805z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="agzp0xahzc83quv3ilamvc5s9xz48v8k" class="crayons-icon collapsed"><title id="agzp0xahzc83quv3ilamvc5s9xz48v8k">Expand</title>
<path d="M12 18l-4-3.771 1-.943 3 2.829 3-2.829 1 .943L12 18zm0-10.115l-3 2.829-1-.943L12 6l4 3.771-1 .942-3-2.828z"></path>
</svg>
</span>
<span class="js-collapse-comment-content inline-block align-middle"></span>
</summary>
<div
id="comment-node-1057677"
class="
comment single-comment-node
root
comment--deep-0
"
data-comment-id="1057677"
data-path="/karelvandenbussche/the-pyramid-of-alerting-1g48/comments/284fn"
data-comment-author-id="929088"
data-content-user-id="929088">
<a name="comment-284fn" style="position: absolute; top: -8px;">&nbsp;</a>
<div class="comment__inner">
<a href="https://dev.to/jthan24" class="shrink-0 crayons-avatar m:crayons-avatar--l mt-4 m:mt-3">
<img class="crayons-avatar__image" width="32" height="32" src="https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F929088%2Fda957a9c-b7a5-4fc6-9e51-704eadcafa4a.jpg" alt="jthan24 profile image" loading="lazy" />
</a>
<div class="inner-comment comment__details">
<div class="comment__content crayons-card">
<div class="comment__header">
<a href="https://dev.to/jthan24" class="crayons-link crayons-link--secondary flex items-center fw-medium m:hidden">
<span class="js-comment-username">
Jhonny Pong
</span>
</a>
<div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:block">
<button id="comment-profile-preview-trigger-1057677" aria-controls="comment-profile-preview-content-1057677" class="profile-preview-card__trigger p-1 -my-1 -ml-1 crayons-btn crayons-btn--ghost" aria-label="Jhonny Pong profile details">
Jhonny Pong
</button>
<div id="comment-profile-preview-content-1057677" class="profile-preview-card__content p-4 pt-0 branded-7 crayons-dropdown" style="--card-color: #2302e0; border-top-color: var(--card-color);" data-testid="profile-preview-card" data-repositioning-dropdown="true">
<div class="gap-4 grid">
<div class="-mt-4">
<a href="/jthan24" class="flex">
<span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0">
<img src="https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F929088%2Fda957a9c-b7a5-4fc6-9e51-704eadcafa4a.jpg" class="crayons-avatar__image" alt="" loading="lazy" />
</span>
<span class="crayons-link crayons-subtitle-2 mt-5">
Jhonny Pong
</span>
</a>
</div>
<div class="print-hidden">
<button name="button" type="button" data-info="{&quot;className&quot;:&quot;User&quot;,&quot;style&quot;:&quot;&quot;,&quot;id&quot;:929088,&quot;name&quot;:&quot;Jhonny Pong&quot;}" class="crayons-btn follow-action-button whitespace-nowrap w-100 follow-user" aria-label="Follow user: Jhonny Pong" aria-pressed="false">Follow</button>
</div>
<div class="color-base-70">
Linux lover, DevOps, Kubernetes, AWS, Observability, Grafana for passion
</div>
<div class="user-metadata-details">
<ul class="user-metadata-details-inner">
<li>
<div class="key">
Education
</div>
<div class="value">
Central University
</div>
</li>
<li>
<div class="key">
Work
</div>
<div class="value">
DevOps
</div>
</li>
<li>
<div class="key">
Joined
</div>
<div class="value">
<time datetime="2022-09-20T00:47:39Z" class="date">Sep 20, 2022</time>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<span class="color-base-30 px-2 m:pl-0" role="presentation">&bull;</span>
<a href="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48#comment-284fn" class="comment-date crayons-link crayons-link--secondary fs-s">
<time datetime="2023-07-22T18:16:29Z" class=date-short-year>
Jul 22 &#39;23
</time>
</a>
<div class="comment__dropdown" data-tracking-name="comment_dropdown">
<button id="comment-dropdown-trigger-1057677" aria-controls="comment-dropdown-1057677" aria-expanded="false"
class="dropbtn comment__dropdown-trigger crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon "
aria-label="Toggle dropdown menu" aria-haspopup="true">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="aqfbr89h663tltr6agf4in0a4m9c6a6z" class="crayons-icon pointer-events-none"><title id="aqfbr89h663tltr6agf4in0a4m9c6a6z">Dropdown menu</title>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.25 12a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zm5.25 0a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zm3.75 1.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path>
</svg>
</button>
<div id="comment-dropdown-1057677" class="crayons-dropdown right-1 s:right-0 s:left-auto fs-base dropdown">
<ul class="m-0">
<li><a href="https://dev.to/karelvandenbussche/the-pyramid-of-alerting-1g48#comment-284fn" class="crayons-link crayons-link--block permalink-copybtn" aria-label="Copy link to Jhonny Pong&#39;s comment" data-no-instant>Copy link</a></li>
<li class="comment-actions hidden" data-user-id="929088" data-action="settings-button" data-path="https://dev.to/jthan24/comment/284fn/settings" aria-label="Go to Jhonny Pong&#39;s comment settings"></li>
<li class="comment-actions hidden" data-action="hide-button" data-commentable-user-id="841884" data-user-id="929088">
<button
class="flex justify-between crayons-link crayons-link--block w-100 bg-transparent border-0 hide-comment"
data-hide-type="hide"
data-comment-id="1057677"
data-comment-url="https://dev.to/jthan24/comment/284fn"
aria-label="Hide Jhonny Pong&#39;s comment">
Hide
</button>
</li>
<li class="mod-actions hidden mod-actions-comment-button" data-path="https://dev.to/jthan24/comment/284fn/mod" aria-label="Moderate Jhonny Pong&#39;s comment"></li>
<li class="report-abuse-link-wrapper" data-path="/report-abuse?url=https://dev.to/jthan24/comment/284fn" aria-label="Report Jhonny Pong&#39;s comment as abusive or violating our code of conduct and/or terms and conditions"></li>
<li class="current-user-actions"></li>
</ul>
</div>
</div>
</div>
<div
class="
comment__body
text-styles
text-styles--secondary
body
">
<p>Awesome, thanks for sharing!!</p>
</div>
</div>
<script>
</script>
<footer class="comment__footer print-hidden">
<button
class="crayons-tooltip__activator relative crayons-btn crayons-btn--ghost crayons-btn--icon-left crayons-btn--s mr-1 reaction-like inline-flex reaction-button"
id="button-for-comment-1057677"
data-comment-id="1057677"
aria-label="like"
data-tracking-name="comment_heart_button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" role="img" aria-labelledby="ana5at8tlovt9i339d7ddmnmbpqiuom0" class="crayons-icon reaction-icon not-reacted"><title id="ana5at8tlovt9i339d7ddmnmbpqiuom0">Like comment: </title><path d="M18.884 12.595l.01.011L12 19.5l-6.894-6.894.01-.01A4.875 4.875 0 0112 5.73a4.875 4.875 0 016.884 6.865zM6.431 7.037a3.375 3.375 0 000 4.773L12 17.38l5.569-5.569a3.375 3.375 0 10-4.773-4.773L9.613 10.22l-1.06-1.062 2.371-2.372a3.375 3.375 0 00-4.492.25v.001z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-labelledby="arztcxnsu8eey62ckwkxi3f7cvojmhqa" class="crayons-icon crayons-icon reaction-icon--like reaction-icon reacted"><title id="arztcxnsu8eey62ckwkxi3f7cvojmhqa">Like comment: </title>
<path d="M5.116 12.595a4.875 4.875 0 015.56-7.68h-.002L7.493 8.098l1.06 1.061 3.181-3.182a4.875 4.875 0 016.895 6.894L12 19.5l-6.894-6.894.01-.01z"></path>
</svg>
<span class="reactions-count">1</span><span class="reactions-label hidden m:inline-block">&nbsp;like</span>
<span data-testid="tooltip" class="crayons-tooltip__content">
Like
</span>
</button>
<span class="favorite-control-root" data-favorite-control="true" data-variant="comment" data-favoritable-type="Comment" data-favoritable-id="1057677" data-favoritable-user-id="929088" data-favorited="false" data-label-favorite="Pick as gem" data-label-favorited="Picked as gem" data-label-favorited-by-you="Picked as gem by you" data-modal-title="Gem Picked!" data-modal-body="You&#39;ve picked this as a community gem!" data-modal-remaining-zero="You have no more gems left to give out today." data-modal-remaining-one="You have 1 more gem left to give out today." data-modal-remaining-other="You have %{count} more gems left to give out today." data-modal-remaining-unlimited="You have unlimited gems to give out." data-modal-close="Got it" data-modal-exhausted-title="Out of Gems" data-modal-exhausted-body="You have no more gems left to give out today. Your allowance will refresh soon!" data-modal-exhausted-close="Got it"></span>
<button
class="actions crayons-btn crayons-btn--ghost crayons-btn--s crayons-btn--icon-left toggle-reply-form mr-1 inline-flex"
data-comment-id="1057677"
data-path="/karelvandenbussche/the-pyramid-of-alerting-1g48/comments/284fn"
data-tracking-name="comment_reply_button"
data-testid="reply-button-1057677"
rel="nofollow">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" role="img" aria-labelledby="awqqcf1z5gpdy9hs85xutdlc39adir4" class="crayons-icon reaction-icon not-reacted"><title id="awqqcf1z5gpdy9hs85xutdlc39adir4">Comment button</title><path d="M10.5 5h3a6 6 0 110 12v2.625c-3.75-1.5-9-3.75-9-8.625a6 6 0 016-6zM12 15.5h1.5a4.501 4.501 0 001.722-8.657A4.5 4.5 0 0013.5 6.5h-3A4.5 4.5 0 006 11c0 2.707 1.846 4.475 6 6.36V15.5z"></path></svg>
<span class="hidden m:inline-block">Reply</span>
</button>
</footer>
</div>
</div>
</div>
</details>
</div>
</div>
<div class="align-center">
<nav class="fs-s align-center block" aria-label="Conduct controls">
<a href="/code-of-conduct" class="crayons-link crayons-link--secondary">Code of Conduct</a>
<span class="opacity-25 px-2" role="presentation">&bull;</span>
<a href="/report-abuse" class="crayons-link crayons-link--secondary">Report abuse</a>
</nav>
</div>
</section>
<div id="hide-comments-modal" class="hidden">
<form id="hide-comments-modal__form" class="hide-comments-modal__form" data-type="json" action="/comments/hide" accept-charset="UTF-8" data-remote="true" method="post"><input type="hidden" name="_method" value="patch" autocomplete="off" /><input type="hidden" name="authenticity_token" value="zKhXQjiQrx4Yw3z-7-UkB_42OKchjt39qCIhIC6X6joAL8TKbjFA04rKC3nPtLFOlEPZN2R6OeiqATa2l-ADwg" autocomplete="off" />
<div class="hide-comments-modal__content">
<p class="pb-2">
Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's <a id="hide-comments-modal__comment-permalink" href="#">permalink</a>.
</p>
<label class="crayons-field crayons-field--checkbox my-2">
<input name="hide_children" type="hidden" value="0" autocomplete="off" /><input class="hide_children crayons-checkbox" type="checkbox" value="1" name="hide_children" id="hide_children" />
<p class="crayons-field__label">Hide child comments as well</p>
</label>
<p class="pb-4 pt-2">
<button type="submit" class="crayons-btn">
Confirm
</button>
</p>
</div>
</form> <p class="fs-s color-base-60">For further actions, you may consider blocking this person and/or <a id="hide-comments-modal__report-link" href="/report-abuse">reporting abuse</a></p>
</div>
</article>
<div class="pb-4 crayons-layout__comments-billboard">
<div class="new-bb-container pb-4 crayons-layout__comments-billboard" data-async-url="/karelvandenbussche/the-pyramid-of-alerting-1g48/bmar11/post_comments"></div>
</div>
<!-- Bottom content skipped via SKIP_BOTTOM_CONTENT config -->
</div>
</main>
<aside class="crayons-layout__sidebar-right" aria-label="Author details">
<div class="crayons-article-sticky grid gap-4 pb-4 break-word" id="article-show-primary-sticky-nav">
<div class="crayons-card crayons-card--secondary branded-7 p-4 pt-0 gap-4 grid" style="border-top-color: #0585bb;">
<div class="-mt-4">
<a href="/karelvandenbussche" class="flex">
<span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0">
<img src="https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F841884%2F011c5edc-2de3-4d83-ae8a-cdc1dbb15739.jpeg" class="crayons-avatar__image" alt="" loading="lazy" />
</span>
<span class="crayons-link crayons-subtitle-2 mt-5">
Karel Vanden Bussche
</span>
</a>
</div>
<div class="print-hidden">
<button name="button" type="button" data-info="{&quot;className&quot;:&quot;User&quot;,&quot;style&quot;:&quot;&quot;,&quot;id&quot;:841884,&quot;name&quot;:&quot;Karel Vanden Bussche&quot;}" class="crayons-btn follow-action-button whitespace-nowrap w-100 follow-user" aria-label="Follow user: Karel Vanden Bussche" aria-pressed="false">Follow</button>
</div>
<div class="color-base-70">
Plumber of a sorts, but for massive amounts of data! Senior Data Engineer &amp; Team Lead @ Lighthouse
</div>
<div class="user-metadata-details">
<ul class="user-metadata-details-inner">
<li>
<div class="key">
Location
</div>
<div class="value">
Ghent, Belgium
</div>
</li>
<li>
<div class="key">
Education
</div>
<div class="value">
Ghent University
</div>
</li>
<li>
<div class="key">
Work
</div>
<div class="value">
Senior Data Engineer &amp; Team Lead @ Lighthouse
</div>
</li>
<li>
<div class="key">
Joined
</div>
<div class="value">
<time datetime="2022-04-04T14:58:28Z" class="date">Apr 4, 2022</time>
</div>
</li>
</ul>
</div>
</div>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-2">
More from <a href="/karelvandenbussche">Karel Vanden Bussche</a>
</h3>
</header>
<div>
<a class="crayons-link crayons-link--contentful" href="/karelvandenbussche/flexibility-in-integration-engineering-43hd">
Flexibility in Integration Engineering
<div class="crayons-link__secondary -ml-1">
<span class="mr-1"><span class="opacity-50">#</span>dataengineering</span>
<span class="mr-1"><span class="opacity-50">#</span>programming</span>
<span class="mr-1"><span class="opacity-50">#</span>development</span>
<span class="mr-1"><span class="opacity-50">#</span>softwaredevelopment</span>
</div>
</a>
</div>
</div>
</div>
<div class="crayons-article-sticky grid gap-4 break-word sidebar-bb" data-async-url="/karelvandenbussche/the-pyramid-of-alerting-1g48/bmar11/post_sidebar"></div>
</aside>
</div>
<div class="mod-actions-menu print-hidden"></div>
<div data-testid="unpublish-post-modal-container" class="unpublish-post-modal-container hidden"></div>
<div class="fullscreen-code js-fullscreen-code"></div>
<script src="https://assets.dev.to/assets/billboard-1b75959755e3f87ef58c28efef9eab9853df3b84cf27a937749b2c3449fd450d.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/localizeArticleDates-ddf0f7415ce8497325dd1b64bc1e022762d4d14aec7f538e6988744eeaa9a501.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/articleReactions-03b03e63c7319a6caa1788ca81e7b25dff9fe90b38ca051b8b979a773b011e3b.js" defer="defer"></script>
<script src="https://assets.dev.to/assets/favoriteControls-882b1c32df621133f9c47da91e59df01e50d77cb6cd48a6611a285ce9036acc6.js" defer="defer"></script>
<script>
</script>
<div class="js-billboard-container pb-4 crayons-layout__comments-billboard" data-async-url="/karelvandenbussche/the-pyramid-of-alerting-1g48/bmar11/post_fixed_bottom"></div>
<div id="runtime-banner-container"></div>
<div id="i18n-translations" data-translations="{&quot;en&quot;:{&quot;core&quot;:{&quot;add_comment&quot;:&quot;Add comment&quot;,&quot;beta&quot;:&quot;beta&quot;,&quot;comment&quot;:&quot;Comment&quot;,&quot;copy_link&quot;:&quot;Copy link&quot;,&quot;edit_profile&quot;:&quot;Edit profile&quot;,&quot;follow&quot;:&quot;Follow&quot;,&quot;follow_back&quot;:&quot;Follow back&quot;,&quot;following&quot;:&quot;Following&quot;,&quot;like&quot;:&quot;Like&quot;,&quot;loading&quot;:&quot;loading...&quot;,&quot;reaction&quot;:&quot;Reaction&quot;,&quot;report_abuse&quot;:&quot;Report abuse&quot;,&quot;search&quot;:&quot;Search&quot;,&quot;success_settings&quot;:&quot;Successfully updated settings.&quot;,&quot;search_placeholder&quot;:&quot;Search...&quot;,&quot;search_find_related_posts&quot;:&quot;Find related posts...&quot;,&quot;search_powered_by&quot;:&quot;Powered by Algolia&quot;,&quot;search_submit_search&quot;:&quot;Submit search for advanced filtering.&quot;,&quot;search_displaying_recommendations&quot;:&quot;Displaying Algolia Recommendations — Start typing to search&quot;,&quot;article_form_save_changes&quot;:&quot;Save changes&quot;,&quot;article_form_schedule&quot;:&quot;Schedule&quot;,&quot;article_form_publish&quot;:&quot;Publish&quot;,&quot;article_form_loading_preview&quot;:&quot;Loading preview&quot;,&quot;article_form_preview_loaded&quot;:&quot;Preview loaded&quot;,&quot;article_form_co_authors&quot;:&quot;Co-authors&quot;,&quot;article_form_co_authors_description&quot;:&quot;Add up to 4 co-authors from %{org_name}.&quot;,&quot;article_form_co_authors_placeholder&quot;:&quot;Add up to 4...&quot;,&quot;comments_preview&quot;:&quot;Preview&quot;,&quot;comments_continue_editing&quot;:&quot;Continue editing&quot;,&quot;survey_enter_response&quot;:&quot;Enter your response here...&quot;,&quot;survey_thank_you_response&quot;:&quot;Thank you for your response.&quot;,&quot;survey_thank_you_completing&quot;:&quot;Thank you for completing the survey!&quot;,&quot;dashboard_analytics_readers&quot;:&quot;Readers&quot;,&quot;dashboard_analytics_avg_read_time&quot;:&quot;avg. read time %{seconds}s&quot;,&quot;dashboard_analytics_comments&quot;:&quot;Comments&quot;,&quot;dashboard_analytics_reactions&quot;:&quot;Reactions&quot;,&quot;dashboard_analytics_unique_reactors&quot;:&quot;from %{count} unique users&quot;,&quot;dashboard_analytics_bookmarks&quot;:&quot;Bookmarks&quot;,&quot;dashboard_analytics_followers&quot;:&quot;New Followers&quot;,&quot;top_contributors_empty&quot;:&quot;No engagers found for this period.&quot;,&quot;top_contributors_reactions&quot;:&quot;reactions&quot;,&quot;top_contributors_comments&quot;:&quot;comments&quot;,&quot;top_contributors_weight_note&quot;:&quot;Comments carry more weight&quot;,&quot;follower_engagement_ratio&quot;:&quot;%{ratio}% of followers engaged&quot;,&quot;follower_engagement_detail&quot;:&quot;(%{engaged} of %{total})&quot;,&quot;dashboard_analytics_no_data&quot;:&quot;No data available for this period&quot;,&quot;dashboard_analytics_no_referrers&quot;:&quot;No traffic sources yet&quot;,&quot;stats_by&quot;:&quot;by&quot;,&quot;editor_new_title&quot;:&quot;New post title here...&quot;,&quot;editor_body_placeholder&quot;:&quot;Write your post content here...&quot;,&quot;tags_field_label&quot;:&quot;Add up to 4 tags&quot;,&quot;tags_field_placeholder&quot;:&quot;Add up to 4 tags...&quot;,&quot;counted_organization&quot;:{&quot;one&quot;:&quot;%{count} organization&quot;,&quot;other&quot;:&quot;%{count} organizations&quot;},&quot;counted_user&quot;:{&quot;one&quot;:&quot;%{count} person&quot;,&quot;other&quot;:&quot;%{count} people&quot;},&quot;not_following&quot;:&quot;You&#39;re not following anyone&quot;,&quot;following_everyone&quot;:&quot;You&#39;re following %{details} (everyone)&quot;,&quot;you_are_following&quot;:&quot;You&#39;re following&quot;,&quot;and&quot;:&quot;and&quot;}}}"></div>
</div>
</div>
<footer id="footer" class="crayons-footer print-hidden">
<div id="footer-container" class="crayons-footer__container">
<p class="fs-s crayons-footer__description">
<a class="c-link c-link--branded fw-medium" aria-label="DEV Community Home" href="/">DEV Community</a> — A space to discuss and keep up software development and manage your software career
</p>
<ul class="footer__nav-links flex gap-2 justify-center flex-wrap fs-s p-0" style="" />
<li class="footer__nav-link flex items-center">
<a href="/">
Home
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/challenges">
DEV Challenges
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/++">
DEV++
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/videos">
Videos
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/deved">
DEV Education Tracks
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/help">
DEV Help
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/advertise">
Advertise on DEV
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/organizations">
Organization Accounts
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/showcase">
DEV Showcase
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/about">
About
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/contact">
Contact
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/free-postgres-database-tier">
Free Postgres Database
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="https://shop.forem.com/">
DEV Shop
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="https://mlh.io/">
MLH
</a>
<span class="dot ml-2"></span>
</li>
</ul>
<ul class="footer__nav-links flex gap-2 justify-center flex-wrap fs-s p-0" style="" />
<li class="footer__nav-link flex items-center">
<a href="/code-of-conduct">
Code of Conduct
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/privacy">
Privacy Policy
</a>
<span class="dot ml-2"></span>
</li>
<li class="footer__nav-link flex items-center">
<a href="/terms">
Terms of Use
</a>
<span class="dot ml-2"></span>
</li>
</ul>
<div class="fs-s">
<p>Built on <a class="c-link c-link--branded" target="_blank" rel="noopener" href="https://www.forem.com">Forem</a> — the <a target="_blank" rel="noopener" class="c-link c-link--branded" href="https://dev.to/t/opensource">open source</a> software that powers <a target="_blank" rel="noopener" class="c-link c-link--branded" href="https://dev.to">DEV</a> and other inclusive communities.</p>
<p>Made with love and <a target="_blank" rel="noopener" class="c-link c-link--branded" href="https://dev.to/t/rails">Ruby on Rails</a>. DEV Community <span title="copyright">&copy;</span> 2016 - 2026.</p>
</div>
</div>
</footer>
<div id="snack-zone"></div>
<div id="global-signup-modal" class="authentication-modal hidden">
<div class="authentication-modal__container">
<figure class="authentication-modal__image-container">
<img class="authentication-modal__image" src="https://media2.dev.to/dynamic/image/width=190,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png"
alt="DEV Community" loading="lazy" />
</figure>
<div class="authentication-modal__content">
<p class="authentication-modal__description">
We&#39;re a place where coders share, stay up-to-date and grow their careers.
</p>
</div>
<div class="authentication-modal__actions">
<a href="https://dev.to/enter?signup_subforem=1" class="crayons-btn" aria-label="Log in" data-no-instant>
Log in
</a>
<a href="https://dev.to/enter?signup_subforem=1&amp;state=new-user" class="crayons-btn crayons-btn--ghost-brand js-global-signup-modal__create-account" aria-label="Create new account" data-no-instant>
Create account
</a>
</div>
</div>
</div>
<script src="https://assets.dev.to/assets/signupModalShortcuts-0b25469b985100a01e94cbd7fccaf7f0a4d776e129aac65c766aa32cb28ab29a.js" defer="defer"></script>
<div id="cookie-consent"></div>
<div id="reaction-category-resources" class="hidden" aria-hidden="true">
<img data-name="Like" data-slug="like" data-position="1" src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18" />
<img data-name="Unicorn" data-slug="unicorn" data-position="2" src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18" />
<img data-name="Exploding Head" data-slug="exploding_head" data-position="3" src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18" />
<img data-name="Raised Hands" data-slug="raised_hands" data-position="4" src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18" />
<img data-name="Fire" data-slug="fire" data-position="5" src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18" />
</div>
<script>
var userSignedIn = false;
if (document.readyState === 'complete' || document.readyState === 'interactive') {
initAuth();
} else {
document.addEventListener('DOMContentLoaded', initAuth);
}
function initAuth() {
var paramToken = new URLSearchParams(window.location.search).get('jwt');
if (paramToken && !userSignedIn) {
authenticateUser(paramToken);
} else {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'https://forem.com/auth_pass/iframe?passed_domain=' + encodeURIComponent(window.location.origin || (window.location.protocol + '//' + window.location.host));
document.body.appendChild(iframe);
window.addEventListener('message', function(event) {
if (event.origin !== 'https://forem.com' && event.origin !== window.location.origin) {
return;
}
var data = event.data;
if (data.authenticated && !userSignedIn) {
authenticateUser(data.token);
} else if(data.authenticated && window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
window.ReactNativeWebView.postMessage(JSON.stringify({
action: 'login',
token: data.token,
}));
}
});
}
function authenticateUser(token) {
fetch('/auth_pass/token_login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getMetaContent('csrf-token'),
},
body: JSON.stringify({ token: token }),
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if (data.success) {
if (document.head.querySelector('meta[name="user-signed-in"][content="false"]')) {
// Reload the page to update the user's state
location.reload();
}
}
})
.catch(function(error) {
console.error('Error during authentication:', error);
});
}
function getMetaContent(name) {
var element = document.querySelector('meta[name="' + name + '"]');
return element ? element.getAttribute('content') : '';
}
}
</script>
</body>
</html>