6つの開発環境管理ツールを経てDevboxが一番バランスが良さそうだと思いました

みなさん、開発環境の再現性に悩んだことはありませんか？「自分の環境では動くのに、他のメンバーの環境では動かない」という経験をお持ちの方も多いのではないでしょうか。

僕もこの課題と長く向き合ってきました。今日は、最終的に[devbox](https://devbox.sh)に行き着くまでの道のりを共有したいと思います。この経験が、同じような課題に直面している方々の参考になれば幸いです。

## Node.jsバージョン管理との出会い：nvmとfnm

多くの開発者と同じように、僕の旅は[nvm](https://github.com/nvm-sh/nvm)から始まりました。Node.jsの開発では、プロジェクトごとに異なるバージョンが必要になることが多いですよね。当時のnvmは、この要件を満たすには十分なツールでした。

```bash
nvm install 16
nvm use 16
```

しかし、日々の開発を続けていく中で、バージョンの切り替え速度に課題を感じ始めました。プロジェクトを切り替えるたびに数秒待つ必要があり、シェルを起動するたびに初期化に時間がかかっていました。これらは開発のワークフローに決定的な影響を与えるほどの遅さではありませんでしたが、より快適な環境を求めて[fnm](https://github.com/Schniz/fnm)に移行することにしました。

```bash
fnm install 16
fnm use 16  # ほぼ瞬時に切り替わります
```

nvmは安定性が高く、多くの開発者に愛用されているツールでした。一方、Rustで書かれたfnmは高速な動作が特徴でしたが、どちらもNode.jsに特化したツールという制限がありました。この制限は、後に新たな課題として浮かび上がってくることになります。

## 視野の広がり：asdf/miseの時代

プロジェクトの要件は徐々に複雑化していきました。Node.js以外にも、様々なツールのバージョンを統一的に管理したくなってきたのです。例えば、[direnv](https://direnv.net)のインストールをチームメンバーにお願いする機会が増え、[jq](https://stedolan.github.io/jq/)を使ったシェルスクリプトの導入も必要になってきました。さらには、sedやgrepのようなCLIツールでGNU/BSDの差異に悩まされることも増えてきました。

これらの課題を解決するため、より包括的なバージョン管理ツールである[asdf](https://asdf-vm.com)を導入しました。asdfは多言語対応の統合的なバージョン管理が可能で、プラグインエコシステムにより高い拡張性を持っていました。

```bash
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
asdf install nodejs latest
```

その後、コマンドがより直感的なmiseに移行しました。miseはasdfのプラグインエコシステムを活かしながら、より使いやすいインターフェースを提供してくれました。

```bash
mise install node@22
```

asdf/miseによって、チーム全体での環境統一が容易になりました。しかし、新たな課題もまた見えてきます。

## パッケージ管理の新たな課題

asdf/miseは優れたツールでしたが、完璧ではありませんでした。必要なパッケージが提供されていないケースが少なからずあり、その場合はプラグインを自作する必要がありました。これは予想以上に手間のかかる作業でした。

さらに状況が変わったのは、プロジェクトがモノレポ化を進めることになった時です。[moon](https://moonrepo.dev)とそのパッケージ管理ツールである[proto](https://moonrepo.dev/docs/proto)を導入しました。

```bash
proto install node lts --pin
```

protoは確かにmoonと深く統合されており、必要なツールを自動的に揃えてくれる便利な機能がありました。しかし、ここでも同じ課題に直面します。必要なツールのパッケージが不足しており、[インストーラーを自作](https://github.com/appthrust/proto-toml-plugins)する必要がありました。さらに、Pythonに依存する[yamllint](https://github.com/adrienverge/yamllint)のような複雑な依存関係を持つツールのパッケージ化は、工数の観点から現実的ではありませんでした。

## Nixとの出会い：強力だが急峻な学習曲線

次に試した[Nix](https://nixos.org)は、それまでのツールとは一線を画す存在でした。パッケージの豊富さは群を抜いており、yamllintのような複雑な依存関係を持つツールでも、依存関係を含めて簡単に導入できました。長年抱えていた「パッケージが足りない」という課題が、ほぼ完全に解決されたのです。

```nix:flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };

        tools = with pkgs; [
          nodejs_22
          jq
          direnv
          yamllint
        ];
      in
      {
        devShells.default = pkgs.mkShell {
          buildInputs = tools;
          shellHook = ''
          '';
        };
      }
    );
}

```

Nixは豊富なパッケージ群を持ち、完全な依存関係の解決と環境の完全な再現性を提供してくれました。しかし、新たな課題もありました。それは言語自体の学習コストの高さです。他のツールではtomlなどで必要なツールをリストアップするだけで良かったのに対し、Nixにはそのシンプルさがありません。また、特定のバージョンを導入したい場合も、nixpkgsのコミットハッシュを指定するなど、やや回りくどい作業が必要でした。

## devboxとの出会い：シンプルさへの回帰

最近[devbox](https://www.jetify.com/devbox)というツールを見つけました。devboxは、Nixの強力な基盤を活かしながら、シンプルなインターフェースを提供してくれます。

npmやhomebrewを使ったことがある人なら、5分で使い始められるほど学習コストが低いのです。裏でNixが動いているため、Nixの持つ膨大なパッケージを利用できる一方で、複雑な設定や概念を意識する必要がありません。

```bash
devbox init
devbox add nodejs@22 jq direnv yamllint
devbox shell
```

シンプルな設定と豊富なパッケージ、低い学習コストに加えて、DockerfileやGithub Actions、direnvの.envrcとの連携など、既存の開発フローとの親和性も高いです。個人プロジェクトで使ってみてかなり良さそうという印象でした。まだチームプロジェクトには導入していませんが、チームでの採用も期待感が持てるツールだという感想です。

## 振り返って：開発環境管理の進化

この長い旅を経て、開発環境の再現性確保について多くの学びがありました。パッケージの網羅性は、必要なツールが揃っているかどうかだけでなく、バージョン指定の柔軟性も重要です。依存関係の管理では、複雑な依存も確実に解決でき、バージョンの競合を防げることが求められます。

インターフェースの直感性も重要な要素です。チーム全員が使いこなせることはもちろん、充実したドキュメントの存在も大切です。学習コストは可能な限り低く抑え、既存の知識を活かせることが理想的です。さらに、CI/CDパイプラインやコンテナ環境との連携など、他のツールとの統合性も欠かせません。

みなさんの開発現場でも、環境の再現性は重要な課題の一つではないでしょうか？ここで紹介した各ツールの特徴を参考に、自分たちのプロジェクトに最適なツールを選択してみてください。

devboxは、これらすべての要素をバランスよく満たしてくれそうなツールです。特に、Nixの強力さを活かしながら、その複雑さを隠蔽している点は秀逸です。開発環境の構築と管理は常に進化し続けていますが、devboxは今後の選択肢として非常に魅力的だと感じています。