Always latest as a system property
/ 2 min read
The conventional wisdom in software engineering: pin your dependency versions. Lock files, exact version constraints, “don’t touch what works.” The logic is sound — uncontrolled upgrades break things.
But pinning has a cost that compounds silently. Every day you don’t 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.
The alternative: automate the upgrade, not the pinning
We built a daily cron job called evergreen that:
- Checks for the latest stable Python version (not alpha/beta)
- Upgrades if newer (rebuilds the venv)
- Runs
uv lock --upgradeto bump all dependencies - Runs the test suite
- If tests pass: commits the lock file, pushes, syncs the remote worker
- If tests fail: 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.
What this looks like in practice
On a Saturday in April 2026, we decided to try Python 3.14 (released October 2025, five months old). The upgrade was one command. All 174 packages reinstalled. Tests passed. Done.
We also tried Python 3.15 (alpha). It failed — lxml and ormsgpack don’t have wheels yet. The evergreen filter excludes alpha/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 4 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
One daily cron job. A 60-line bash script. 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.
When this doesn’t work
- Libraries with unstable APIs that change interfaces across major versions
- Projects with downstream consumers who depend on your exact dependency tree
- Environments where reproducibility matters more than currency (scientific computing, regulated industries)
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.