1. It worked. Then it broke the next day.
Anyone who has built automation knows this experience. Something that worked yesterday suddenly stops working today. Read the error. Find the cause. Fix it. It works again. Then, a few weeks later, it breaks again.
This isn't a quality problem. It's a structural problem.
There was a period of scraping external service UIs to automate posting. It worked for a while. But every time a service redesigned its interface, the script broke. Every fix consumed time. A contradictory task called "automation maintenance" kept appearing.
The root of the problem was singular: control was not in hand.
2. The concept of a boundary
Software design has a concept called a boundary — the line separating what a system can control from what it cannot.
What sits inside your boundary — your code, your data, your repository — can be changed by your own will. When it breaks, you can fix it. When you want to change the specification, you can.
What sits outside — external service APIs, platform UI, other companies' authentication systems — changes on someone else's schedule. There is no way to stop it.
Whether automation keeps running depends on how this boundary is drawn. More external dependencies means more frequent breakage.
3. What happened with Qiita auto-publishing
The goal was a system where pushing Markdown to GitHub would automatically publish articles to Qiita. What followed was a series of cascading failures.
The first error was QiitaNotFoundError — article IDs from a different account remained in the files. IDs were cleared and the process restarted.
The next error came from misunderstanding the --all option, which ignored ignorePublish: true settings and attempted to process everything. Switching to individual specification was necessary.
Then: 'public/path/to/file.md' is not found. The Qiita CLI expects a basename, not a path. The documentation hadn't been read carefully enough.
When articles finally published, the same article appeared a dozen times — because every push triggered Actions and each run created a new post. The ID was never being written back to the file.
Attempting to write the ID back produced 403 Forbidden. The GITHUB_TOKEN in Actions didn't have write permissions by default.
Each fix uncovered the next problem. Each was small individually. Together, they consumed half a day.
4. What actually went wrong
Looking back, the failure pattern was clear.
First: the external service specifications weren't accurately understood. The behavior of --all, the basename requirement, the handling of IDs — these are Qiita and Qiita CLI specifications. They live outside the boundary. Building automation on misunderstood external specs produces unexpected behavior.
Second: permissions were overlooked. The GITHUB_TOKEN in Actions is read-only by default. Write access requires explicit configuration. Permissions are part of the boundary that must be explicitly set.
Third: idempotency wasn't designed in. Idempotency means running the same operation multiple times produces the same result. The publishing process wasn't idempotent, which caused duplicate posts. Checking for the presence of an ID before acting was what finally resolved it.
5. The design that worked
The design that finally held was simple.
Only post new articles whereid: nullis set in the file
After posting, write the Qiita-assigned ID back to the file and commit
On subsequent runs, the ID exists and the article is skipped
Idempotency was now guaranteed. No matter how many times a push happens, only unpublished articles get published. Articles with IDs written back will never duplicate.
Only when this design was running could it honestly be called automation. Before that, it was "continuously fixing things that keep breaking."
6. Three conditions for lasting automation
From this experience, three conditions for automation that keeps running became clear.
First: minimize external dependency. Using external service APIs is unavoidable, but limit where the dependency sits. Use APIs rather than UIs. APIs change slowly; UIs change constantly.
Second: design for idempotency. Make every operation safe to run multiple times. Check state before acting. Always verify "does this already exist?" and "has this already been processed?"
Third: make boundaries explicit. Clearly separate what you control from what you don't. For things outside your control, build fallbacks for when they change.
7. Breaking is information
When automation breaks, frustration is natural. But the way it breaks contains information.
Which error appeared. Where it stopped. Which assumption failed. Reading these reveals where the design was leaning on external dependencies. Breaking teaches you where the boundary was drawn too loosely.
The chain of failures that day was exhausting. But the design that emerged was more robust. Idempotency checks, ID write-back, explicit permission configuration — these should have been there from the start, but breaking is what made them visible.
Automation failures are material for stronger automation.
8. What it means to keep running
When thinking about systems that run for a thousand years, the robustness of automation is unavoidable. External services disappear. APIs change. Platforms shut down.
So boundaries are designed. Data lives in your own repository. External dependencies are minimized. The question of what remains when something breaks is built into the design from the start.
Automation is a mechanism that keeps running without human intervention. For it to actually keep running, it needs boundaries that don't break easily — and a design that recovers when they do.
Behind the single line "push and it publishes," that design lives.
Automation breaks because control lives outside the boundary. Reduce external dependencies, design for idempotency, make boundaries explicit. These are the conditions for automation that lasts.
TokiStorage is a project for preserving voice, image, and text for 1,000 years.
The design philosophy of lasting automation is built into the preservation system itself.