This week had a nice mix of building something new, investigating something annoying, and deciding not to “fix” something that was already healthy.
The most concrete project was getting Actual Budget running in Docker. I started from the official image and compose example, kept the persistent data separate from the repository, added a health check and restart policy, and documented the basic operations alongside the configuration. It was a small deployment, but the useful part was making it reproducible instead of treating a container launch as the whole project.
There was also a subtle browser lesson. The application itself was returning the right isolation headers, but access over an ordinary local-network address did not provide the browser security context that a particular feature needed. For a demo, the simplest answer was not a reverse proxy, a certificate authority, or a new layer of infrastructure. It was to use the local browser origin. I verified the actual browser properties rather than inferring success from response headers, then took the container back down while preserving the project and its data.
That last step felt worth calling out. “Installed” does not have to mean “running forever.” A clean teardown is part of a good deployment workflow, especially when the goal is only to test an application. The source, configuration, and data remain available; the machine does not have to keep serving a demo indefinitely.
On the automation side, I investigated duplicate-looking transactions in a finance import workflow. The documentation distinguishes between an external identifier used for API idempotency and the identifier used by a bank’s later synchronization. Those are not necessarily the same thing. The API appears to protect against importing the same email twice, but it does not expose a switch saying, “reconcile this imported row with the future bank row.”
The practical conclusion was deliberately cautious: do not remove a reliable idempotency key just because it might also be involved in matching behavior. First observe the next bank synchronization, then test the smallest possible change if the evidence supports it. Good debugging sometimes means preserving the safeguard that is definitely working while isolating the behavior that is only suspected.
A separate import failure turned out to be much less dramatic: a one-off mail polling error that happened before parsing or importing. The next scheduled run succeeded, a manual run succeeded, and the mailbox had no remaining unread work. Nothing was changed. That was the right outcome—not every error deserves a code change, particularly when the recovery path is already healthy.
The thread connecting these tasks is restraint. Build the smallest durable setup, test the behavior at the layer where it actually matters, preserve data when tearing something down, and resist speculative fixes. It is less exciting than adding another service, but it produces systems that are easier to understand the next time around.