There are 3 Types of Dependencies
A friend asked me how I managed a single neovim config
across multiple machines when the neovim versions are different. neovim
frequently breaks
APIs between
versions. Writing a single config for multiple versions, even if you
never upgrade neovim explicitly, can still be a pain.
The answer is "simple", use the same version across all machines. But managing
dependencies are hard. What if one machine runs MacOS, another Ubuntu and yet
another Arch Linux? But OS package managers struggle with tools like neovim.
The issue is not specific to neovim. Other tools have the same issue: aws
CLIs, delta, rg and direnv.
The 3 Types of Dependencies
I find that there are 3 types of dependencies:
- System dependencies (these make sure your computer turns on, can connect to the internet, drivers for your mouse)
- Tooling dependencies (your editor, command-line tools, small utilities)
- Project dependencies (packages/libraries,
react,numpy)
It's not always clear what category a dependency belongs in. For example, is
python3.12 a project dependency or tooling dependency? It's usually the
former since different projects may each have different versions of python for,
at least, a moment in time. It could also be both if any of your tools need
python.
As a general rule of thumb,
- System dependencies are OS-level and critical
- Tooling dependencies are both personal and used across projects and,
- Project dependencies are specific to a project and the entire team on the project should be expected to have installed.
The crux of the problem is that most machines have a package manager that
treats everything like system dependencies. neovim and the program/package
that connects you to the internet are controlled by the same system. The issue
then roots from how each distro manages packages. Ubuntu takes the approach of
slow updates to avoid things breaking. This is nice for system dependencies,
annoying for tooling dependencies and (often) hostile for project dependencies.
The difference is fundamental. How do we select versions for each dependency type?
- System dependencies cannot break and so the versions need to be controlled to keep the entire system stable.
- Tooling dependencies are personal and the versions are controlled to match your preferences and configs.
- Project dependencies need to be changed when those changes are wanted and compatible with the project.
Managing your Tooling Dependencies with asdf
The fix is straightforward: use different package managers for each type of dependency:
- Use
apt/pacman/... for your system dependencies - Use
npm/pip/... for your project dependencies
How about my tooling dependencies?
There are a bunch of options. But, I'd like to recommend a tool I've come to
enjoy using to manage my tooling dependencies, asdf.
asdf is a universal version manager, like pyenv or nvm, but for many
tools at once. It lets you install and lock versions of editors, languages, and
CLIs independently of your system package manager.
For example, I can use asdf to remedy the "neovim versioning problem" I
introduced above. The process starts with following the install
instructions and then:
$ asdf plugin add neovim # Add the neovim "plugin" to tell asdf you want it to manage nvim
$ asdf install neovim stable # Install the stable version (or use a tag like 0.11.4)
$ asdf set --home neovim stable # Use the stable version of neovim everywhere by default
$ nvim --version # Now the exact version of neovim I want has been installed
NVIM v0.11.4
The Other Options
For completeness, I should bring up the other options for managing tool dependencies.
pyenv and nvm are tools to manage python and node versions. I would argue
that these (almost always) manage project dependencies. Yes, the version of
node/npm is a dependency for your project. However, nvm itself is a tool
dependency. This highlights one of the drawbacks of tooling dependencies: we
don't have many good tools to manage them. pyenv and nvm, as a result, have
had to invent their own install processes distinct from your system package
manager and the project package manages like pip and nvm.
Another tool, nix, could also be used to manage tooling dependencies. If you
think nix (or any other tool for that matter) is great, I'd love to read your
post about how you can use it to solve the "neovim version problem" ;).
Conclusion
The "neovim version problem" I presented initially is a case of a more
general problem of managing the versions of your tools.
Tooling dependencies are a type of dependency distinct from system and package
dependencies. Package managers like apt or npm are best for system and
project dependencies, respectively. However, using the system package manager
to manage the versions of your tools can create the "neovim version problem".
To remedy the issue, I presented a tool I use, asdf to solve the "neovim
version problem". asdf makes design decisions which match the needs of tool
dependencies better than apt or npm.