rss linkedin linkedin gitlab github twitter mastodon instagram
Go Tool: direnv
Nov 20, 2020

direnv is definitively a Go tool, in the context of it is written in Go, but it is not a development tool like the ones I covered before in previous posts (like versions, ifacecodegen, counterfeiter or retool), however it is one of those tools that you should consider using when developing programs (in any programming language really, not only Go), specially if you build programs that have different binary dependencies or any other dependencies that could be configured via environment variables.

Whats is direnv?

According to the official page:

direnv is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.

(Emphasis mine)

The key takeaway of that description is the powerful ability to load/unload environment variables depending on where you’re are located at when interacting with your terminal.

Why is using direnv relevant?

Consider the following hypothetical scenario: You’re building something that happens to depend on a tool to generate some code, let’s call it tool (v1), after a while you starting working on a new project, this new project uses the same tool but a different version tool (v2) but compared to v1 it generates code in a different way. Having v1 installed will conflict with v2 (and the same will apply the other way around) because both tool generate incompatible artifacts.

In the context of Go, handling dependencies like that is easily managed using Go Modules, however what about our actual runtime needs? What do I mean by that? If we depend on tool (v1) then we need to have it installed and available in our PATH, same story with tool (v2).

How can we handle this conflict? More importantly, how do we go about having those tools sandboxed?

Introducing direnv

This is where direnv really shines, it allows us to sandbox things that happen to use environment variables as configuration options, the most common example of this is creating concrete PATH variables to happen to be relative to your working directory, the idea is that only the binaries installed inside our folder will be used. Having this configuration allows direnv to clearly define a boundary that is only applicable to the folder we are in.

The official Github repository includes specific steps for installing it and using it, it works like this:

  1. Create a new .envrc file wherever you want the configuration to apply for current directory and other subdirectories, and
  2. Configure that .envrc to define the directives you want to apply.

For example, using our hypothetical scenario from above, when working on project1 (which uses tool (v1)), on the parent directory we could define the .envrc as following:

PATH_add bin
export GOBIN=$PWD/bin

This indicates direnv:

  1. To update PATH to point to bin/ in the current working directory and,
  2. To also export GOBIN to point to that path as well.

The idea is that when we go install tool (v1) it will get installed under bin/ and also when we try to execute it will be available under the same path.

If we create a similar .envrc for project2 (which uses tool (v2)), the results will be similar. In practice what this means is that now both projects can refer to different versions of the same tool without conflicting to each other. That is a powerful feature.


I can’t recommend direnv enough, it’s such a simple yet powerful tool that should improve your development experience.

Give it try, you won’t be disappointed.

Back to posts