How often do you commit your changes? How often do you upgrade your dependencies?
Commit Often
There is a common answer to the first question:
Everyone Commits To the Mainline Every Day
Code is integrated and tested after a few hours-a day of development at most.
We recommend that you aim to commit changes to the version control system at the conclusion of each separate incremental change or refactoring. If you use this technique correctly, you should be checking in at the very minimum once a day, and more usually several times a day.
This makes sense: small frequent merges are easier to integrate than large, occasional ones. The less often you commit (to a shared “trunk” branch) the more painful integration becomes. You find problems too late, and spend a lot of time in integration or merge hell: performing painful merges or grepping through large changesets to find the needle in the haystack that broke the app.
Upgrade Often
The same logic applies to upgrading dependencies. If you upgrade frequently, each individual upgrade is usually quick and painless. Yet there is less consensus about how often to upgrade.
I’m a fan of small, frequent upgades. I consider any outdated library to be tech debt. You don’t always need to address it right away but you should review it and make an informed decision. This is the difference between Reckless/Inadvertant debt and Prudent/Deliberate debt.
I’m not a fan of Long-Term Support (LTS) releases either. It means you’re setup up for a major upgrade, you’re missing out on faster/safer/cooler software. Your environment probably isn’t as static as you pretend either - why would you use a locked version of Selenium/WebDriver unless you’ve turned off Firefox/Chrome updates.
Uninformed Pessimism
Can a team simultaneously be “bleeding edge” and a “late adopter”?
I’ve seen it. I was on a team that had used an a beta release of a library, then skipped the next few stable releases. When we finally upgraded a method was missing. I dug through the release notes for several versions, looking for a deprecation notice with a recommended alternative. I never found one - because the method never made it out of the beta!
The team did not choose to do this - they were simple uninformed. They’d been inadvertently reckless. This is a trap you can fall in even if you don’t use beta versions. If you don’t have a good dependency report that shows what you’re using and what upgrades are available then you are uninformed. You could try to manually assemble a report, but that is impractical on large projects (most Java projects), or projects with lots of small, frequently released libraries (most Ruby projects).
Informed Pessimism
The first step towards better dependency management is becoming is “Informed Pessimism”: generate and regularly review a report of available upgrades. Many package managers have a report or command you can use:
- mvn versions:dependency-updates-report
- bundle_outdated
gem outdated
yum list updates
apt-get -s dist-upgrade
Bundler usually beats Maven, but in this case the Maven plugin generates a nice report I can display from a CI server. Here’s a live examle.
Cautious Optimism
Alex Chaffee proposed Cautious Optimism as the ideal. A Cautious Optimism build system will attempt to upgrade dependencies as soon as possible.
Cautious Optimism isn’t for everyone, but if you have good dependency management and a Continuous Delivery setup you can trust your pipeline to catch problems introduced by an upgrade. If a problem is found, you lock the dependency to the last-known good version until a solution is found. Some tools that are useful in implementing Cautious Optimism:
Matrix Builds
If your team can release frequently it can probably upgrade frequently. The only exceptions may be upgrades to large frameworks - Java/Spring, Ruby/Rails, etc. It may be prudent to delay an upgrade even if its possible - just because it means a long downloads and a lot of knew features to study.
It is possible to get the feedback without actually committing to an upgrade. I view this as using your CI/CD setup to answer two questions:
- Is the app releasable? (Test with fixed dependencies)
- Is the app upgradable? (Test with latest dependencies)
I’d probably do the upgradable check less often. The releasabilty checks should be every commit, but you can probably check for upgradability nightly.
Conclusion
Most Agile teams believe in the “commit often” motto. You should remember that everyone is supposed to commit often. Unless everyone has integrated recently, someone on the team may still be headed towards merge hell.
I consider third-party library developers to be part of the extended team. Unfortunately these developers cannot push their own changes and cannot resolve the conflicts. The core team needs to be proactive about reviewing and pulling upgrades as often as practical. Every day may not be practical, but try to avoid skipping releases.