<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Byte Chap</title>
    <description>The latest articles on DEV Community by Byte Chap (@sourcebento).</description>
    <link>https://dev.to/sourcebento</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4142891%2Fe0715329-96f5-4971-b0e2-33516f861b50.png</url>
      <title>DEV Community: Byte Chap</title>
      <link>https://dev.to/sourcebento</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sourcebento"/>
    <language>en</language>
    <item>
      <title>A cron job can exit successfully and still fail your business workflow</title>
      <dc:creator>Byte Chap</dc:creator>
      <pubDate>Fri, 25 Sep 2026 12:20:00 +0000</pubDate>
      <link>https://dev.to/sourcebento/a-cron-job-can-exit-successfully-and-still-fail-your-business-workflow-333</link>
      <guid>https://dev.to/sourcebento/a-cron-job-can-exit-successfully-and-still-fail-your-business-workflow-333</guid>
      <description>&lt;p&gt;A scheduled process can exit with code zero while the business task is still incomplete.&lt;/p&gt;

&lt;p&gt;A backup script may finish before its upload is durable. An import may return successfully after skipping every malformed row. A billing job may write invoices but fail before notifying customers. Process success and workflow success are different states.&lt;/p&gt;

&lt;p&gt;Logs help explain a failure after someone knows to look. A completion heartbeat answers the earlier question: &lt;strong&gt;did the expected work finish within the expected window?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Send the heartbeat after the critical work
&lt;/h2&gt;

&lt;p&gt;Put the heartbeat at the end of the business-critical path. If a script pings before the backup reaches remote storage, the monitor confirms only that the script started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

create_backup
upload_backup
verify_remote_object

curl &lt;span class="nt"&gt;--fail&lt;/span&gt; &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--show-error&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://your-monitor.example/ping/&amp;lt;job-token&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ping should follow verification: the object exists, the export contains accepted rows, or the downstream API acknowledged the operation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A heartbeat should prove the outcome you care about, not merely that a process woke up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This loose coupling works across shell scripts, containers, queues, serverless functions, and old applications that are difficult to instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use an interval and a grace period
&lt;/h2&gt;

&lt;p&gt;A daily job rarely completes at the exact same second. Queue delays, database locks, larger inputs, deployments, and daylight-saving changes can shift completion.&lt;/p&gt;

&lt;p&gt;Model two values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the expected run interval;&lt;/li&gt;
&lt;li&gt;a grace period based on observed variance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a job normally completes in 12 minutes and occasionally needs 20, a 30-minute grace period may be reasonable. If the business deadline is stricter, the workflow needs more capacity rather than a quieter monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open one incident and close it on recovery
&lt;/h2&gt;

&lt;p&gt;A missed heartbeat should open a single incident instead of sending the same notification every polling cycle. When the heartbeat returns, close the incident and send a recovery message.&lt;/p&gt;

&lt;p&gt;The pair gives operators a useful duration and prevents an inbox full of duplicate symptoms. Keep the check idempotent so that multiple scheduler passes do not create duplicate incidents for the same missed window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the alert path outside the failed component
&lt;/h2&gt;

&lt;p&gt;A monitor running on the same machine as the job cannot report a full host failure. For important work, run the monitor on another host and configure more than one notification path, such as email plus Telegram or a generic webhook.&lt;/p&gt;

&lt;p&gt;The monitor itself also needs observation. An external uptime check for its public endpoint closes the most obvious blind spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the recovery procedure before the alert
&lt;/h2&gt;

&lt;p&gt;Include the job name, expected schedule, last heartbeat, incident start time, and a link to a short runbook. The runbook should answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is rerunning the job safe?&lt;/li&gt;
&lt;li&gt;How do we detect partial work?&lt;/li&gt;
&lt;li&gt;Who owns the downstream system?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with one job whose silent failure already has a real cost. Measure normal completion time for a week, choose a defensible grace period, and run a controlled failure. If the alert arrives but the recovery steps remain ambiguous, the monitoring is not finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working reference implementation
&lt;/h2&gt;

&lt;p&gt;I build SourceBento and sell the complete source code for a small self-hosted Cron Monitor implementing this pattern with TypeScript, Express, SQLite, Docker Compose, missed-run incidents, recovery alerts, and email, Telegram, or webhook notifications.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://sourcebento.com/demo/cron/?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=launch_2026_09&amp;amp;utm_content=cron_workflow" rel="noopener noreferrer"&gt;public Cron Monitor demo&lt;/a&gt; does not require registration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I am the author and seller of SourceBento. The operational guidance above stands on its own whether or not the product fits your stack.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
