IRCNF

mise, Nix, and devcontainers all solve "works on my machine" — they just disagree on how

Share:
mise, Nix, and devcontainers all solve "works on my machine" — they just disagree on how

The phrase "works on my machine" has haunted software teams for decades. New hire joins, spends two days getting their environment working. Senior dev upgrades Python, breaks three projects. CI fails on a library version nobody remembered pinning. The root cause is always the same: developer environments are stateful, implicit, and manually assembled.

In 2026, this problem is genuinely solved — but "solved" means three competing tools with radically different philosophies. Understanding the tradeoffs is the real skill.

Tier 1 — mise: The Pragmatic Starting Point

mise (formerly rtx) is a polyglot version manager written in Rust. It replaces asdf while running 10–20x faster due to its compiled core and parallel plugin resolution. It handles Node.js, Python, Go, Ruby, Java, and dozens of other runtimes from a single tool.

The core mechanic is .mise.toml at the project root:

[tools]
node = "22.3.0"
python = "3.12.4"
go = "1.22.5"

[env]
DATABASE_URL = "postgres://localhost/myapp_dev"

Run mise install and it reads the config, downloads the pinned versions into a content-addressed local cache (~/.local/share/mise/installs/), and activates them for the current directory. No symlink juggling, no shell hacks beyond adding mise activate to your profile.

The performance difference from asdf is not subtle. Installing a Node.js version that asdf takes 45 seconds to resolve and install, mise completes in under 4 seconds. For teams that constantly switch between projects with different runtime requirements, this compounds into real time savings.

What mise does not do: it manages language runtimes, not system libraries. If your project needs a specific version of libpq, openssl, or ffmpeg, mise cannot help. It also cannot reproduce the exact glibc version on Linux or the exact Xcode toolchain on macOS. For most application developers, these gaps do not matter. For everyone else, read on.

Tier 2 — Nix and Nix Flakes: Full Reproducibility

Nix is a functional package manager built around one idea: every package is a pure function of its inputs. Given the same inputs, you always get the same output. Packages live in /nix/store/ under content-addressed paths like /nix/store/ybmrfz0-nodejs-22.3.0/. Two packages can depend on different versions of the same library without conflict because they literally live at different paths.

nixpkgs is the package repository: over 90,000 packages, all reproducibly built, spanning every major language ecosystem plus system tools, fonts, databases, and compilers. It is the largest single-source package set of any Linux distribution.

Nix flakes (stabilized in NixOS 23.11) add a lockfile-driven dependency model to Nix itself. A flake.nix at the project root pins the exact nixpkgs commit and defines a devShell:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";

  outputs = { self, nixpkgs }: {
    devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
      packages = with nixpkgs.legacyPackages.x86_64-linux; [
        nodejs_22
        python312
        go_1_22
        postgresql_16
        ffmpeg
      ];
    };
  };
}

Run nix develop and you drop into a shell where every tool is exactly as specified — down to the C library versions. The flake.lock file pins the nixpkgs commit SHA. Six months later, on a different machine, in a different country, nix develop produces the same environment.

The honest learning curve: Nix has its own functional language (also called Nix), its own mental model for how builds work, and documentation that assumes familiarity with functional programming concepts. Most developers spend 1–2 weeks before they feel comfortable writing flakes from scratch. Tools like devenv.sh and flake-parts reduce this significantly, but there is no shortcut around the foundational concepts.

The payoff is real. Teams using Nix flakes report zero "environment drift" issues in post-mortems. CI runs the same nix develop shell as local development. New hires run nix develop on day one and have a working environment within minutes, not days.

Tier 3 — devcontainers: Onboarding and Cloud-First Development

devcontainers is an open specification (backed by Microsoft and used in VS Code and GitHub Codespaces) that defines a development environment as a Docker container. The configuration lives in .devcontainer/devcontainer.json:

{
  "name": "My Project",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:22",
  "features": {
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/go:1": { "version": "1.22" }
  },
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
    }
  }
}

VS Code's Remote - Containers extension mounts your local files into the container and runs your entire editor inside it — language servers, debuggers, terminals, all executing in the container's environment. GitHub Codespaces takes this further: click "Open in Codespaces" and the entire development environment runs on GitHub's cloud infrastructure, not your laptop.

Where devcontainers win: onboarding is genuinely one-click. Security isolation is real — the container filesystem is separate from your host. For compute-heavy tasks (large model training, video rendering, compiling massive C++ codebases), offloading to Codespaces with 32-core cloud machines is legitimate productivity.

Where devcontainers struggle: Docker overhead is real on macOS. File system performance through the Docker VM layer can be 2–3x slower than native for I/O-intensive tasks like running a large test suite. You are also dependent on network connectivity and Docker being operational — neither is guaranteed in all development contexts.

How Real Teams Combine These Tools

The false choice is picking one and going all-in. Teams that have figured this out typically layer:

  • mise for day-to-day language switching. Every developer has it. It handles 90% of "which version of Node/Python/Go" questions with minimal friction. The .mise.toml is committed to the repo.
  • Nix flakes for teams with system-level requirements. If you need specific versions of native libraries, or if you are building on both Linux and macOS and need identical environments, flake.nix is worth the investment. Some teams use Nix only for CI, getting reproducibility where it matters most.
  • devcontainers for onboarding and CI parity. The .devcontainer/ config is kept for new hire day one and for GitHub Codespaces access. It does not replace local development tooling but provides a guaranteed fallback.

A concrete example: a team of 12 developers uses mise locally for fast version switching, Nix flakes in CI (GitHub Actions runs nix develop --command make test), and a devcontainer for Codespaces access when team members are traveling or on locked-down corporate machines.

The Honest Tradeoffs

  • mise: installs in 30 seconds, works everywhere, handles 90% of real problems, but cannot manage system libraries or guarantee binary reproducibility below the language runtime level.
  • Nix: the most powerful option available, produces genuinely bit-reproducible environments, covers every layer of the stack, but requires real investment to learn and can make simple tasks (adding a new package dependency) feel heavyweight until the concepts click.
  • devcontainers: lowest barrier to entry for onboarding, cloud-native, VS Code-native, but carries Docker overhead, requires connectivity, and can obscure environment details in ways that make debugging harder.

Where to Start

If your team has fewer than 5 developers and the pain is "everyone's on different Node/Python versions": install mise today, commit a .mise.toml, done. You will fix 90% of environment complaints in an afternoon.

If your team is 10+ developers, you are shipping to Linux, you have native library dependencies, and environment drift has caused real production incidents: invest in Nix flakes. Budget 2–3 weeks for one developer to become the team's Nix expert. The return is genuine.

If your primary pain is onboarding time, you work with GitHub heavily, or you need secure isolation for contractors: devcontainers are the right lever. They compose well with both mise and Nix — you can run mise inside a devcontainer, or build your devcontainer image from a Nix derivation.

The "works on my machine" problem is solved. The remaining question is which solution fits your team's actual constraints — and whether you are willing to pay the learning costs that the more powerful options require.

Share:
mise, Nix, and devcontainers all solve "works on my machine" — they just disagree on how | IRCNF - Intelligent Reliable Custom Next-gen Frameworks