Free converters stamp a watermark on web assets by design. Four small ffmpeg wrappers do the job locally, and the watermark was the least of it: the real work was choosing the quality, preset and GIF palette flags ffmpeg lets you get wrong.
Technical notes, projects, modules, tools, workflows, and architecture on electronics, software engineering, and DevOps.
Free converters stamp a watermark on web assets by design. Four small ffmpeg wrappers do the job locally, and the watermark was the least of it: the real work was choosing the quality, preset and GIF palette flags ffmpeg lets you get wrong.
Photos into one PDF, a PDF under two megabytes, a layout inspected page by page — usually done by uploading to a stranger's website. Three small tools do it locally with Pillow and Ghostscript; the only real decisions were which defaults to bake in.
cp -r drops owner, permissions and timestamps, producing copies that look right and fail in use. A 58-line rsync wrapper makes the destination truly equal to the source — and most of its lines are refusals, because making it equal means agreeing to delete.
Retyping long Docker commands costs seconds, invites typos and taxes working memory. Ten short executables on PATH removed that cost; the interesting decisions were why not aliases, how to keep names guessable, and why one of them is 260 lines long.
A storage server that answers a connection only proves a process is listening. I check each capability separately — browser uploads via presigned URL, private buckets refusing strangers, large files through the proxy — before any application depends on it.
A failed login in a half-built service is either a misconfigured identity provider or wrong code. I rule out the first by rehearsing a full login with my own tool before writing any app, and found compliant providers still disagree on tokens and claims.
This Laravel app answers HTTP and runs long-lived queue workers, yet a container is meant for one program. A process manager, FrankenPHP worker mode and a cache-ordered build put both in one image, instead of building the same code twice.
Its software reached the end of support, and its records must outlive it. One component rewrote data as it read it, so the migration copies rather than converts — an operation with no rollback has to be survivable, not merely careful.
Staging and production shared 136 keys, but 21 had to stay different, including the one that decrypts years of data. Promoting a release's structure, not a file's contents, keeps that key in place and stops a schema migration from silently never running.
One rotation reaches seven services, and a value elsewhere can look exactly like one of their credentials, so search-and-replace could break an integration nobody watches. Working from an explicit list keeps the rotation inside its blast radius and away from the keys that only decrypt.
Get an email when a new article is published.