skip to content

Always latest as a system property


The conventional wisdom in software engineering: pin your dependency versions. Lock files, exact version constraints, do not touch what works. The logic is sound — uncontrolled upgrades break things.

But pinning has a cost that compounds silently. Every day you do not upgrade, the gap between your code and the ecosystem widens. Dependencies fix security vulnerabilities you never apply. APIs deprecate features you still use. When you finally upgrade — because a CVE forces your hand or a new feature requires it — you face upgrade day: hours of cascading breakage from months of accumulated drift.

We built a daily cron job called evergreen that checks for the latest stable Python version, excluding alpha and beta releases. If a newer version exists, it upgrades and rebuilds the virtual environment. Then it runs uv lock with the upgrade flag to bump all dependencies. Then it runs the test suite. If tests pass, it commits the lock file, pushes, and syncs the remote worker. If tests fail, it rolls back to the previous lock file. The entire cycle is automatic. No human reviews dependency bumps. No upgrade tickets in the backlog. The system stays current or it stays where it was — never in a broken intermediate state.

On a Saturday in April 2026, we decided to try Python 3.14, which had been released five months earlier. The upgrade was one command. All 174 packages reinstalled. Tests passed. Done. We also tried Python 3.15 alpha. It failed — lxml and ormsgpack do not have wheels yet. The evergreen filter excludes alpha and beta releases, so it would never have attempted this automatically. When those packages ship 3.15 support, evergreen will pick it up on its next daily run. No human needed. The same session, pip-audit found four CVEs in our dependencies. Fixed in one command. If evergreen had been running earlier, it would have pulled the fixed versions automatically before we even knew the CVEs existed.

The cost is one daily cron job, a sixty-line bash script, and five minutes of compute on a machine we already own. The cost of the alternative — manual upgrades, security audits, compatibility testing — is measured in engineer-hours, usually spent under pressure when something is already broken.

This does not work everywhere. Libraries with unstable APIs that change interfaces across major versions, projects with downstream consumers who depend on your exact dependency tree, and environments where reproducibility matters more than currency all need pinning. For a personal project, a startup, or any system where the blast radius of a broken dependency is one person’s afternoon — always latest wins.