Replacing flake.nix#shells or the legacy shell.nix with a JSON file doesn’t simplify things—it hides them it another layer. This would be a step back since you lose a lot of flexibility in the Nix language, but also that you can have more than the default shell. Besides that they dared use the word “magical” when Nix isn’t magic, also hiding under this veneer is that Nix is best served as the build tool so the entire build system is repoducible/predictable. The fact that NGINX “no longer work[s] in a standard way” is the feature because it isn’t mutable.
We’re huge fans of Nix, and Nix flakes are really powerful tools for creating reproducible builds. We first built Devbox as an internal tool to make it easy to create Nix-based dev environments, while providing configuration options that are more familiar to developers who have previously used packages like NGINX. We’re hoping that a more approachable interface for creating dev environments can help drive adoption of Nix overall.
I think this is sending the wrong message. There is a lot of people searching for “how do I get my Nix shell into Docker for deployment” because they missed the point that the dev shell isn’t the killer feature of Nix–replacing Docker, and your dev shell, and your build tool is (or building your container with Nix because of deployment constraints dictating containers). You want the build to be top-to-bottom immutable. Creating state at any level of that ruins the reproducibility part–including a mutable shell. I get it: Nix is a learning cliff and the dev shell seems like a gentle introduction, but until you go further, you’re missing out on the biggest benefits. And the thing is that setting up the dev shell is the easy part, and getting familiar with Nix the language with a flake.nix or shell.nix is the only way you’ll start to get there (that said, Guile Scheme+Guix is also a worthy alternative to Nix doing roughly the same thing). So is the idea that developers can’t be asked to learn the tools they are using so it’s behind a JSON layer?
Mutability is not necessarily bad if it is declarative rather than imperative. You can still reproduce the entire shell top to bottom with a declarative mutable local store that is ephemeral, which is created at installation time. In this case the reproducibility is not compromised at all since the configuration is still declarative, but it allows a more standard way of using packages that expect mutability. The bad part of mutability is when it is used in an imperative way stored in a permanent location.
On dev shell isn’t a killer feature of Nix - why not? I think it actually can be. Well, I certainly hope so!
Yes. Unlike Go, neither Python nor Node has a “Go 1 guarantee” equivalent, and a change of versions very typically breaks a significant amount of working code. As a result, most projects have some system for pinning the Python/Node version and only upgrade periodically. I had a really fun bug in Node a few years ago where they broke Babel, a popular JavaScript transpiler, in something like version 12.5, not even 12.0.
Another difference is that Go since the addition of modules will cache downloaded modules at the user level, but will use module versions specific to each project. Python by default installs packages at the system level. You have to use “virtual environments” to create a project specific copy of the Python install so that packages instead install at a project level. AFAIK, package downloads are cached at the Python install level, although I could be wrong about this. Node is the opposite: everything is installed to a project local folder by default, and there’s no download caching sharing across projects. (Yarn tried to add cross-caching, but that version of Yarn never got popular. Deno caches correctly, IIUC.) The easiest thing to do is to not care about it, but on a naively implemented CI system, it can add a lot of wait time as you download the same dependencies over and over again. Anyhow, it’s all much more bespoke and harder to ignore if you want sensible defaults than in Go, where you only run into trouble if you want to do something the Go team hasn’t thought of.
Python by default installs packages at the system level. You have to use “virtual environments” to create a project specific copy of the Python install so that packages instead install at a project level.
This is correct for both Python and Ruby (as well as other languages) – packages and binaries are installed in a central location, and switching between them for different projects can create conflicts or issues unless you use virtual environments. I’ve head a lot of headaches trying to make sure I have the right Ruby installed for projects I’m working on :(
Making this easy was part of the inspiration for Devbox, and Nix provides a great backend to make that possible
Replacing
flake.nix#shells
or the legacyshell.nix
with a JSON file doesn’t simplify things—it hides them it another layer. This would be a step back since you lose a lot of flexibility in the Nix language, but also that you can have more than thedefault
shell. Besides that they dared use the word “magical” when Nix isn’t magic, also hiding under this veneer is that Nix is best served as the build tool so the entire build system is repoducible/predictable. The fact that NGINX “no longer work[s] in a standard way” is the feature because it isn’t mutable.We’re huge fans of Nix, and Nix flakes are really powerful tools for creating reproducible builds. We first built Devbox as an internal tool to make it easy to create Nix-based dev environments, while providing configuration options that are more familiar to developers who have previously used packages like NGINX. We’re hoping that a more approachable interface for creating dev environments can help drive adoption of Nix overall.
I think this is sending the wrong message. There is a lot of people searching for “how do I get my Nix shell into Docker for deployment” because they missed the point that the dev shell isn’t the killer feature of Nix–replacing Docker, and your dev shell, and your build tool is (or building your container with Nix because of deployment constraints dictating containers). You want the build to be top-to-bottom immutable. Creating state at any level of that ruins the reproducibility part–including a mutable shell. I get it: Nix is a learning cliff and the dev shell seems like a gentle introduction, but until you go further, you’re missing out on the biggest benefits. And the thing is that setting up the dev shell is the easy part, and getting familiar with Nix the language with a
flake.nix
orshell.nix
is the only way you’ll start to get there (that said, Guile Scheme+Guix is also a worthy alternative to Nix doing roughly the same thing). So is the idea that developers can’t be asked to learn the tools they are using so it’s behind a JSON layer?Mutability is not necessarily bad if it is declarative rather than imperative. You can still reproduce the entire shell top to bottom with a declarative mutable local store that is ephemeral, which is created at installation time. In this case the reproducibility is not compromised at all since the configuration is still declarative, but it allows a more standard way of using packages that expect mutability. The bad part of mutability is when it is used in an imperative way stored in a permanent location.
On dev shell isn’t a killer feature of Nix - why not? I think it actually can be. Well, I certainly hope so!
Is this really still such a big problem with Python and Node? I never had the need for multiple Go SDKs on my machine.
Yes. Unlike Go, neither Python nor Node has a “Go 1 guarantee” equivalent, and a change of versions very typically breaks a significant amount of working code. As a result, most projects have some system for pinning the Python/Node version and only upgrade periodically. I had a really fun bug in Node a few years ago where they broke Babel, a popular JavaScript transpiler, in something like version 12.5, not even 12.0.
Another difference is that Go since the addition of modules will cache downloaded modules at the user level, but will use module versions specific to each project. Python by default installs packages at the system level. You have to use “virtual environments” to create a project specific copy of the Python install so that packages instead install at a project level. AFAIK, package downloads are cached at the Python install level, although I could be wrong about this. Node is the opposite: everything is installed to a project local folder by default, and there’s no download caching sharing across projects. (Yarn tried to add cross-caching, but that version of Yarn never got popular. Deno caches correctly, IIUC.) The easiest thing to do is to not care about it, but on a naively implemented CI system, it can add a lot of wait time as you download the same dependencies over and over again. Anyhow, it’s all much more bespoke and harder to ignore if you want sensible defaults than in Go, where you only run into trouble if you want to do something the Go team hasn’t thought of.
This is correct for both Python and Ruby (as well as other languages) – packages and binaries are installed in a central location, and switching between them for different projects can create conflicts or issues unless you use virtual environments. I’ve head a lot of headaches trying to make sure I have the right Ruby installed for projects I’m working on :(
Making this easy was part of the inspiration for Devbox, and Nix provides a great backend to make that possible