Originally published at plain.jxd.dev/blog/ci.
Cross-posted here for the dev.to community — the canonical version (and any future updates) lives on the original.
Most CI is YAML: a config language pretending to be a programming language, with string interpolation standing in for control flow and a feedback loop that runs only after you push. You write a block, push, wait, watch it go red on a typo, fix the typo, push again. The language can't help you, because there is no language: just a document that a runner interprets somewhere else, later.
It works. Half the software in the world ships through it. But nobody likes the part where a ${{ }} is subtly wrong, or where the answer to "how do I reuse this across three jobs" is "copy the block." We took CI as a given for so long that we stopped noticing how much of it is friction we'd never accept anywhere else in our stack.
Plain takes a different path. CI is built into the platform, analogous to GitHub Actions, but with a few differences that change how it feels to use. None of them are individually radical. Together they add up to something that feels less like configuring a runner and more like writing the rest of your software.






