The Build Is a Pipe

The deploy workflow in most projects is treated as a single monolithic script. One repo, one workflow, one big blob of steps that does everything. That works fine until you split your content out of your app repo — and then you have to ask: who builds what?

For theTube, the split is:

  • theTube — app code, renderer, CloudFront function, infra
  • theTube-content — public posts, about page, links
  • thetube-private — protected content, auth, keys

Right now the build lives entirely in theTube. When content changes, you push to theTube to trigger the deploy. That's a seam in the wrong place — the content repo should own the trigger.

The pipe model

A Unix pipe connects two programs: one writes, one reads. The pipe itself is the contract. Neither program needs to know what the other one looks like internally.

The build is the same thing. theTube-content writes content. theTube reads it and produces HTML. The workflow_dispatch call is the pipe connector — theTube-content pushes, fires a dispatch, theTube builds.

Each repo owns one stage of the pipeline:

theTube-content push → workflow_dispatch → theTube build → S3 sync → CloudFront invalidation

The stages don't bleed into each other. theTube-content doesn't know how the renderer works. theTube doesn't need to watch for content changes — it just responds when called.

What theTube builds

theTube owns the app shell: the JS bundles, the CSS, the CloudFront function, the infra. These change infrequently. When they do change, a push to theTube triggers a full rebuild.

theTube-content owns the trigger for content changes. Its "build" is writing markdown. The dispatch call is the handoff.

The tradeoff

You could go further: theTube-content syncs its own content directly to S3, bypassing the Next.js build entirely. Each repo owns its own output path. The pipe is fully decoupled.

That's the cleaner model architecturally — but it breaks generateStaticParams, build-time tag aggregation, and anything else the renderer needs to know about posts at build time. The cost of full decoupling is a dumber renderer.

Or you keep the build in theTube and use workflow_dispatch as the pipe joint — theTube-content is the trigger, theTube is the builder. Looser coupling than a single repo, tighter than full independence.

Which model holds up depends on how the renderer evolves. That's a decision for when it matters.