<?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: Phillip</title>
    <description>The latest articles on DEV Community by Phillip (@phillipmex).</description>
    <link>https://dev.to/phillipmex</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%2F4032939%2F292d5af0-ce58-4ad7-ab7b-d5e813d941db.png</url>
      <title>DEV Community: Phillip</title>
      <link>https://dev.to/phillipmex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phillipmex"/>
    <language>en</language>
    <item>
      <title>File presence is all you need: a 7-stage video pipeline with no database and no queue service</title>
      <dc:creator>Phillip</dc:creator>
      <pubDate>Fri, 17 Jul 2026 01:59:41 +0000</pubDate>
      <link>https://dev.to/phillipmex/file-presence-is-all-you-need-a-7-stage-video-pipeline-with-no-database-and-no-queue-service-n9a</link>
      <guid>https://dev.to/phillipmex/file-presence-is-all-you-need-a-7-stage-video-pipeline-with-no-database-and-no-queue-service-n9a</guid>
      <description>&lt;p&gt;I run a video automation pipeline on a home Windows PC: idea file in, QA'd&lt;br&gt;
1080p YouTube upload out. Seven stages — script generation (Ollama/Claude),&lt;br&gt;
TTS narration (Piper), image sourcing, Remotion render, thumbnail, human QA,&lt;br&gt;
YouTube Data API upload. It has produced 156 uploads and once ran 8+ days&lt;br&gt;
unattended.&lt;/p&gt;

&lt;p&gt;Here's the part worth writing about: &lt;strong&gt;it has no database, no message queue,&lt;br&gt;
and no daemon.&lt;/strong&gt; The entire orchestration layer is the filesystem.&lt;/p&gt;
&lt;h3&gt;
  
  
  One folder per job, file presence = state
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs/
  connect4-tactics/
    idea.md            &amp;lt;- input
    script.json        &amp;lt;- stage 1 done
    manifest.json      &amp;lt;- stage 2 done (+ audio/*.wav)
    images/            &amp;lt;- stage 3 done
    connect4-tactics.mp4   &amp;lt;- stages 4-5 done (+ .jpg thumbnail)
    qa-approved.txt    &amp;lt;- the human said yes
    youtube-id.txt     &amp;lt;- stage 7 done; folder moves to archive/uploaded/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Every stage's contract is the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;listJobs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;outputFileFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// already done — skip&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runStage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                              &lt;span class="c1"&gt;// do the work&lt;/span&gt;
  &lt;span class="c1"&gt;// the output file IS the state transition&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole scheduler. There is no &lt;code&gt;status&lt;/code&gt; column to update, so there&lt;br&gt;
is no &lt;code&gt;status&lt;/code&gt; column to be wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this buys you
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Crash safety for free.&lt;/strong&gt; Power cut mid-render? The mp4 was never written,&lt;br&gt;
so the next scheduled run re-renders that job and skips everything else.&lt;br&gt;
Recovery isn't a code path — it's the &lt;em&gt;only&lt;/em&gt; code path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redo = delete.&lt;/strong&gt; Bad narration on one video? Delete &lt;code&gt;manifest.json&lt;/code&gt; and the&lt;br&gt;
WAVs; only stage 2 re-runs. Every partial redo you'd normally build tooling&lt;br&gt;
for is a &lt;code&gt;rm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dashboard is &lt;code&gt;ls&lt;/code&gt;.&lt;/strong&gt; Any file manager shows you exactly where every&lt;br&gt;
video is in the pipeline. Debugging is opening a folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The QA gate is incorruptible by automation.&lt;/strong&gt; Upload requires&lt;br&gt;
&lt;code&gt;qa-approved.txt&lt;/code&gt;, and only a human watching the video creates that file. The&lt;br&gt;
gate isn't a boolean a bug can flip — it's a file only I write.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it costs you
&lt;/h3&gt;

&lt;p&gt;Honesty section: this design assumes &lt;strong&gt;one machine, one writer&lt;/strong&gt;. Two&lt;br&gt;
concurrent runs of the same stage would race (I serialize via Task Scheduler,&lt;br&gt;
which never overlaps runs). File-presence also can't express rich state like&lt;br&gt;
"attempted 3 times, failing" — my answer in the one flaky stage (network&lt;br&gt;
image downloads) is to not track attempts at all: a failed download degrades&lt;br&gt;
to a text-card visual and the pipeline keeps moving. If you need multi-node&lt;br&gt;
workers or per-job retry metadata, use a real queue; below that threshold,&lt;br&gt;
the filesystem is shockingly hard to beat.&lt;/p&gt;

&lt;h3&gt;
  
  
  The health check
&lt;/h3&gt;

&lt;p&gt;The other half of unattended reliability: a &lt;code&gt;check-health.ts&lt;/code&gt; that verifies&lt;br&gt;
every credential, binary, model and token &lt;em&gt;before&lt;/em&gt; a scheduled run touches&lt;br&gt;
anything — expired OAuth tokens are found by the health check at 6am, not by&lt;br&gt;
the upload stage at 2am.&lt;/p&gt;




&lt;p&gt;I've packaged the whole pipeline (TypeScript source, setup + production&lt;br&gt;
docs) as a one-time self-hosted kit: &lt;a href="https://beforeyoustart.gumroad.com/l/channel-kit" rel="noopener noreferrer"&gt;https://beforeyoustart.gumroad.com/l/channel-kit&lt;/a&gt;. No revenue promises —&lt;br&gt;
it's infrastructure, not a business model. But if you've ever wanted to see&lt;br&gt;
what "boring tech" looks like applied to video automation, the source is the&lt;br&gt;
best argument I have.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>architecture</category>
      <category>automation</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
