設定は簡単だった。大変なのは維持することだ
一度きれいにした開発環境も、だいたい半年ほど経つと崩れてくる。この前きちんと調べたときは、1つだと思っていたPythonが3バージョンあり、Brewfileと実際のマシンが一致せず、mise listには何の実験で入れたのか思い出せないruntimeが並んでいた。何かが壊れたわけではない。毎日使う間に、元の状態から静かにずれていただけだった。
この記事でシリーズは最後になる。これまでの6本で扱ったのはレイヤー、設定、dotfilesだったが、そこまでは午後のうちに終えられる。1年後も同じ環境を保てるかどうかは、その後もずれを見つけ、不要になったものを取り除けるかにかかっている。
ずれ方はだいたい3種類
実際に見つかるのは、主に次の3つだった。
ツールによる無言の自動インストール。 Pythonの記事では、uvが独自のPythonをダウンロードしていた。確認を求められないまま、自分で選んでいない状態がマシンに増えるため、最も見落としやすいずれ方だ。python-preference = only-systemのような宣言的な設定を使えば、無言のダウンロードを明示的なエラーに変えられる。ただし、あらゆるツールを設定だけで防げるわけではないので、結果を調べる手段は別に必要になる。
インストール後の同期忘れ。 自分でアプリを入れても、宣言ファイルへの追記を忘れることがある。するとアプリ管理の記事で作ったBrewfileが、実際のマシンから少しずつ遅れていく。普段は困らなくても、移行時には「これも入れていたのか」が一度に出てくる。復旧に使う段階まで放置せず、先に両方の状態を突き合わせておく必要がある。
使い終わったものの蓄積。 runtimeやグローバルツール、パッケージを必要があって入れ、一度だけ使い、そのまま残す。これは特に片づけにくい。個々の項目には問題がないため、エラーは出ず、リスト全体を見たときに初めて、積み重なったこと自体が問題だと分かる。
ヘルスチェックは、ずれを見せるだけでいい
確認用に、いくつかのshell関数を残している。前の記事で紹介したbrewdiffはインストール済みアプリとBrewfileを比較し、zhealthはホームディレクトリに散らばったzshファイルを探す。Pythonは複数のツールが別々のインタプリタを持っても気づきにくいので、専用の確認も用意した。
# 30-functions.zsh — surface Python version drift across toolspyversions() { echo "shell PATH : $(command -v python)" echo " reports : $(python --version 2>&1)" echo "mise current : $(mise current python 2>/dev/null || echo '—')" echo "uv would use : $(uv run python --version 2>&1)" echo "mise list :" mise list python 2>/dev/null | sed 's/^/ /'}pyversionsを実行すると、shellのpython、mise current、uv run python、そしてmiseにインストール済みのバージョンを一度に確認できる。最初の3つが一致していれば、このレイヤーは正常だ。一致しなければ、想定外のPythonをどこかのツールが持っている。Pythonの記事で行った調査を、確認コマンドを一から思い出すことなく繰り返せる。
ほかのレイヤーでも考え方は同じだ。
mise listと、実際に使っているmise.tomlを比較するbrew bundle checkで、マシンとBrewfileを比較する$HOMEの中身が「zshファイルは1つだけ」という想定に合っているか確認する
どのチェックも自動修復はしない。これは意図的にそうしている。報告だけを行う小さなコマンドなら、挙動を把握しやすく、実行もしやすい。偶然見つけて驚くしかなかったずれが、自分で対処を選べる差分になる。
30日使わなかったものを見直す
自分にとって成熟した環境とは、使っていないツールがほとんど残っていない環境だ。そのため、メンテナンスで行うことの多くは追加ではなく削除になる。
残すか迷うものについては、過去30日に実際に使ったかを考える。1か月触っていないruntime、グローバルツール、アプリは削除候補にする。自動的に消すわけではないが、残すのが当然のものから、残す理由が必要なものへ扱いを変える。
miseのruntimeなら、確認と整理は具体的だ。
$ mise list # what's installed$ mise uninstall python@3.11 # remove a version no project uses$ mise prune # drop versions nothing references前回のRubyについての判断は、同じ基準をインストール前に適用した例でもある。システムRubyが古いというだけで、Rubyプロジェクトのないマシンにmise管理のRubyを追加する理由にはならない。必要なプロジェクトができるまで待てば、あとで確認して削除するruntimeも増えない。
この作業は、整理したい気分になるのを待たず、定期的に行う。新しいツールの導入は進歩のように感じやすい一方、削除には損失や、最初の選択が間違いだったと認めるような感覚がある。サンクコストも、放置するほうへ気持ちを傾ける。決まった作業にしてしまえば、チェックを実行し、30日使っていないものを見て、残す理由を説明できないものを消すだけで済む。
mise listの行が減ること、短いBrewfile、ホームディレクトリにdotfileが1つしかないことは、設定不足とは限らない。それが整理後の正しい状態である場合もある。
メンテナンスのためのシステムを増やさない
掃除の仕組み自体も、簡単に作り込みすぎてしまう。ダッシュボード、定期ジョブ、大量のヘルスチェックを揃えた結果、それらを維持する仕事が新しく増えることもある。
2秒で終わるbrewdiffなら実際に使う。世話が必要な監視システムは使わなくなる。状態の突き合わせは、それによって避けられる混乱より低コストな間だけ役に立つ。だから確認ツールは小さく保ち、報告だけを任せ、削除の判断までは自動化しない。
シリーズ最初の記事では、環境をSystem、Runtime version、Package manager、Project dependenciesの4レイヤーに分けた。それらの設定だけでは用意できない5つ目の要素がある。たまにマシンを見直し、実際の状態と宣言した状態を比較して、壊れる前に両者を揃える習慣だ。
しばらく確認していないマシンなら、まずwhich pythonとuv run python --versionを実行し、次にmise listから1か月触っていないものを探す。それだけでも、自分が覚えている環境と、手元にある環境がまだ同じかどうかは見えてくる。
この記事はSovereign Toolsシリーズの最終回です。全記事の順番はシリーズ一覧にまとめています。