Homebrew doesn’t actually check if an upgrade will break your installed software.
Let’s say you’ve got a Python project that absolutely needs Python 3.9. You run brew upgrade python. Homebrew, being the eager beaver it is, happily installs Python 3.12. Your project? It now fails to run because it’s looking for 3.9, and the system’s default python command now points to 3.12. This is a common, albeit frustrating, scenario. Homebrew’s core philosophy is to get you the latest versions, not to maintain strict backward compatibility for your specific application stack.
Here’s how we can navigate this and keep your Homebrew packages in a sane state:
The "Safe Upgrade" Workflow
The key is to understand what’s about to change before you commit to it.
-
See What’s Outdated: Before anything, let’s see which packages have newer versions available.
brew outdatedThis command lists all installed packages for which a newer version exists in Homebrew’s repositories. It’s your "potential trouble" list.
-
Inspect a Specific Package: If you see a critical package like
pythonornodein thebrew outdatedlist, you’ll want to know what version Homebrew wants to install.brew info pythonLook for the line that shows the latest available version. For example, you might see
3.12.1 (latest). This tells you thatbrew upgrade pythonwould install3.12.1. -
Check Dependencies: Sometimes, upgrading one package will pull in newer versions of its dependencies, which can also cause issues.
brew dependency pythonThis will show you what
pythonrelies on. If any of those dependencies are also outdated,brew upgrade pythonmight implicitly upgrade them too. You can check theinfofor those dependencies as well. -
Upgrade One by One (The Safest Bet): For critical packages, avoid
brew upgrade. Instead, upgrade them individually.brew upgrade python@3.9If you specifically want to upgrade to
python@3.9(which might be a specific versioned formula), this is how you’d target it. If you just want the latest python, you’d typically runbrew upgrade python. However, if you need a specific version, you might need to install it explicitly, e.g.,brew install python@3.10and then manage yourPATHor symlinks. Homebrew’s versioned formulae (likepython@3.9) are designed to coexist. -
The "Dry Run" (Sort Of): Homebrew doesn’t have a perfect
dry-runfor upgrades that shows exactly what will be installed and how it affects your system. However,brew upgrade --verbosecan sometimes give more insight, but it’s often too noisy. The best "dry run" is manually checkingbrew infofor each package you’re considering upgrading and then examiningbrew outdated. -
Pinning a Package (To Prevent Accidental Upgrades): If you have a package that must not be upgraded, you can "pin" it.
brew pin python@3.9Now, if you were to run
brew upgrade,python@3.9would be skipped. To unpin it later:brew unpin python@3.9This is crucial for environments where specific versions are required.
-
The Standard Upgrade (When You’re Ready): When you’re confident that the upgrades won’t break your workflow, or for less critical packages, you can proceed with a broader upgrade.
brew upgradeThis command upgrades all outdated packages. If you’ve pinned packages, they will be skipped.
-
Cleaning Up Old Versions: After upgrades, Homebrew keeps old versions around for a while. You can clean them up to free disk space.
brew cleanupThis removes old versions of all formulae, leaving only the currently installed ones.
A Real-World Scenario: Node.js
Let’s say you’re developing a web app that uses a specific Node.js version, e.g., Node 18.
- You run
brew outdated. You seenodein the list, withnode 20.x.xavailable. - You run
brew info node. It confirms20.x.xis the latest. - You know that upgrading
nodemight break your current project. - You decide to keep your current Node.js. You run
brew pin node. - Later, when you need to work on a project that requires Node 20, you can unpin it, upgrade, and then potentially pin the new version or manage multiple Node versions using
nvm(Node Version Manager) which integrates with Homebrew.
The next common issue you’ll encounter is dealing with outdated "kegs" (the directories where Homebrew installs package versions) that brew cleanup doesn’t remove because they are currently linked.