Nix solves a major technical challenge for us: having the right Python and the right
Node.js installations coexisting peacefully. This may not sound like much, but this way
we skip the land of pyenv
and
nvm
.
For local development, it's convenient. For actual deployments, it's critical.
But Nix is much bigger than that.
Your projects should guarantee three things: reproducibility, isolation, and native performance.
Your projects should be reproducible. Table stakes.
Checking out a version, tag or branch should declaratively state every requirement needed to run the application. For that specific project, on that specific branch, at that point in time.
Not just language-level dependencies like requests
or react
, but actual binaries and
language runtimes themselves. That's right, you should declare python 3.9
and
jdk 11
. Don't just document them, require them.
Check these requirements into your code.
But working on one project should not affect working on another. Your blog may depend on
python 3.9
and go 1.17
. And your consulting client has a site that requires
python 3.8
. You should be able to switch between these seamlessly.
In fact, working on the same project with differing requirements should be possible.
You'll often collaborate on a long-lived branch to update a project to python 3.10
.
Dare to live in a world where both branches can co-exist on your machine. Without having
to think about it.
This is true isolation. And Nix provides it. But... so does Docker, right?
You spent thousands of dollars on a machine. It runs quiet. It runs cool. It runs forever on battery. Why throw all that way and run your projects on Docker?
If you develop on a Mac: you're doing just that. Docker on MacOS is virtualized. And if you use Apple Silicon hardware, images can run even slower depending on the architecture.
Even keeping the development files in sync can be slow. Volume mounting is notoriously resource-intensive with Docker on Mac.
Moreover, running commands and scripting with Docker is clunky. A plain old shell experience is far nicer.
Finally: some will say running on Docker better reflects what is running on production — which does use containers. Dev / prod parity is a pillar of the 12-factor app, in fact.
I say maybe, but probably not. You're on different architectures at this point. Or worse, emulating them. Many cloud providers internally don't even use Docker at all.
Enter Nix. Using a single shell.nix
file, we declare everything our project needs.
In that point in time, on that branch, in that one instance of the project.
We list out python
, nodejs
, postgresql
, shellcheck
and more.
It's happened before. You know it has. Your colleague is using sed
on a Mac. Another
is using sed
on Ubuntu. And you are using sed
on a Mac but installed coreutils
to
get the Linux version. An absolute mess.
Nix standardizes everything. Once inside nix-shell
, everyone will have the same
sed
, the same grep
and the same find
. And best of all: the same bash
.
No surprises.
Remember, this applies to our scripts too. Open up one of those quick, only for now
then I'll rewrite it later in Python 1000 line scripts. Yes,
convert-gcp-mp4s-to-gifs-and-upload-to-aws.sh
, I'm looking at you.
Try to identify all the binaries that need to exist. You'll likely run into the regular
cast of characters: sed
, git
, and wget
. If you're lucky, you can expect most devs
and setups to have these,
OS-level differences aside.
But you need gcloud
installed, probably a version that supports their latest storage
options — the options your script uses. And — lest we forget — the aws
CLI.
Last but not least, you need ffmpeg
.
Are you going to list all of these with setup instructions, identifying versions? Provide a Docker image? Just hope for the best? Write assertions at the top of the script ensuring they exist?
No, you're just going to list them in requirements.nix
.
And at the top of your script file, refer to it, using the --pure
flag. This will
ensure nothing unlisted is available to the script.
#! /usr/bin/env nix-shell #! nix-shell requirements.nix --pure -i bash # redacted ffmpeg -y -i "$filepath" -filter_complex "<filter options>" "$ANIMATED" aws s3 cp myfolder s3://mybucket/myfolder --recursive # redacted
New dev working on the project? Tell them to install Nix, check out the repository, and run the script. That's it. Nix will install and cache all the dependencies.
Eudaimonia.