The idea that you’ll install just one global version on your laptop and use that for every project is ridiculous
One challenge that I’ve run into for any programming language is the ability to manage multiple versions successfully. The idea that you’ll install just one global version on your laptop and use that for every project is ridiculous.
Have you experienced this before:
- Decide to start a new project in a new language to “learn”
- Get said project working
- Disconnect with it for 2–3 months
- Re-engage with the project and realize things are way out of date already
- Spend half a day getting your laptop working properly again so you can build the project
You can avoid all this by A) never using system-wide installations of developer tools and/or languages B) finding a proper version manager that makes updating just a few commands AND getting back to the ‘old’ versions just as easy.
[ The remainder of this article focuses on Linux & Mac environments, and while the overall concepts still apply, the tactical guidance does not. ]
If you rely on system-wide installations of tools on your development machine, you’ve committed a cardinal sin. You may not be forced to atone for it now or even in the near future, but at some point, it’s going to bite you, and you’ll lose half a day putting Humpty back together — I promise you.
The reason for this is that those ‘packaged’ distributions of these tools that install into system-wide locations are tied to other, packaged, system-wide utilities. Once you do a system upgrade, things tend to go badly, and all of your local caches, tooling, and potentially even configuration will need fixing.
Another challenge presented by relying on system-wide utilities is frequently they need to interact with system-wide paths, i.e., locations outside of your home directory. This requires one of two bad behaviors; running these utilities with privileged execution (sudo) or updating permissions on system-wide paths.
Resolving these issues is simple; install the languages you rely on and their tooling into your home directory (or other paths dedicated to your user).
A ‘version manager’ is a straightforward concept; I want to use language X, libraries associated with it, tooling associated with it, and I want to use all of these things tied to a particular release.
Typically, ‘version managers’ allow installing all of these things into a local directory owned by your user (commonly inside your home directory) that gets referenced via some system of symlinking.
Via a version manager, I can install many releases of the same language, use a command to switch which version is active, and automatically have an isolated copy of all the tools and libraries for each release.
Now, because we are talking about developer tooling, it’s likely already evident that there are a ton of different version managers. Historically, I’ve been partial to the ‘env’ type ones; rbenv, pyenv, goenv, etc. Recently, however, I’ve been made a convert to a single manager that works with many languages: asdf.
The allure of
asdf is simple, one version manager for all languages. I can use a common workflow instead of having a different version manager for each ecosystem with similar functionality but different commands or semantics.
Now, I know what you’re thinking because I’m also a skeptic; “It can’t possibly have all the languages I need and do a good job.”
Look, the list of ‘plugins’ (i.e., things supported) is extensive: https://github.com/asdf-vm/asdf-plugins.
There’s a ton of stuff on that list that I have no idea what the heck it’s for, so I assume it’s reasonably complete!
Setting up asdf is pretty straightforward:
brew install asdf
Update profile (add):
asdf tool has the notion of ‘plugins’. The plugins do the heavy lifting within the broader framework the tool provides. This means that all the commands and interactions with
asdf are the same regardless of the language; the plugin translates accordingly to its own ecosystem.
To setup Node, we need to install the corresponding plugin. First, install the plugin:
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
Then, install a version (we’ll use latest, but you can get a listing of versions you could install using
asdf list all nodejs):
asdf install nodejs latest
Finally, we need to set the version of Node that should be the ‘default’:
asdf global nodejs latest
This means that anytime we use Node (or npm), we’ll get the latest version that we just installed.
We can validate this behavior by running:
Having the ability to set the default version of Node is obviously important, especially for the tools that you install via
npm -g and want to use day-to-day.
But another capability that most version managers supply is to set the version to use specifically for one project. The workflow looks like this (example in Node but applicable for other languages you manage with
asdf as well):
- Install the required language version (
asdf install nodejs <version>)
- Navigate to the root of the project that requires this version
asdf local nodejs <version>
asdf local command, writes a special file to the root of the project that informs the
asdf managed languages what version to use dynamically.
This means if you’re developing numerous Node projects (in this example) all using different versions, you can seamlessly switch between projects and know that the
asdf managed projects will automatically use the correct versions!
So to wrap up, use a version manager for the languages that you develop in. Not only does this protect you from significant issues when your operating system has to perform upgrades, but it also enables you to use many versions simultaneously.
Using a tool like
asdf, you can have one tool that allows you to manage versions for many languages to simplify your machine’s tooling.