This is the pattern for changing a file that a running service watches and reloads from live (a routing-rule file, a config directory, anything a process re-reads without a restart) so that a bad write doesn’t sit unnoticed and a good write doesn’t silently lose its ownership.
A directory watcher can fail whole, not partial
A process that watches a whole directory and reloads when it changes often treats that directory as one unit to parse. If it does, one file it cannot read (say, a file whose permissions no longer let the process open it) can fail the entire load, not just that file. The process then falls back to whatever it loaded last and keeps serving that correctly, indefinitely. A health check that only asks whether the service is responding will report a clean answer, because it is one: the process is doing exactly what it was told to do with the configuration it still has.
The consequence worth designing around: there is no restart-shaped safety window here. A bad write takes effect, or fails to, within seconds of landing on disk, not at the next restart. A restart doesn’t create the danger; it’s usually the first thing to notice it, because a fresh start has to parse the whole directory from nothing, where the already-running process didn’t have to.
An atomic replace is only atomic on one filesystem
The standard safe way to update a file that something else might be reading mid-write is to build the new version somewhere else, then move it into place with a single filesystem rename, a step that’s atomic, so nothing ever sees a half-written file. That guarantee has one precondition that’s easy to miss: the temporary file and the target need to live on the same filesystem. When they don’t, the move silently stops being a rename and becomes a copy: a new file, created fresh, owned by whoever performed the operation rather than whoever owned the original.
That makes where the temporary file gets created a real decision, not an implementation detail. A general-purpose scratch directory is often on a different filesystem from wherever the target actually lives, especially once the target is a mounted volume or a network share. The temporary file belongs beside the target: the same directory, or at minimum a guaranteed-same filesystem, never in a system’s default temporary location chosen without checking.
Permissions before ownership, because ownership is a one-way door
When a replace needs to change both who owns a file and what mode it has, order matters in a way that isn’t obvious going in: set permissions first, then change ownership. The moment ownership transfers away from whoever is doing the write, that process loses the right to change the mode. It no longer owns the file, so it no longer gets to decide who else can read it. Doing it in the other order can leave the process that just wrote the file unable to correct its own mode.
Validate the content, then verify the read
Two checks belong on either end of the same operation, and neither is optional because the other one exists. Before writing: validate that the new content is well-formed, so a bad write is never the thing a live process discovers by failing to parse it. After writing: verify that the reader can actually read the result immediately, rather than waiting for a restart, a scheduled check, or a report from someone else to be the thing that notices. A restart is exactly the wrong test for this: it’s also the trigger that turns a silent failure into a visible one.
This pattern came out of a single day when a routing-rule directory went silently unreadable for twenty-eight hours, told as it happened in Act 26: The watcher that was not dead.