凝集性を意識したdotfiles運用
こんにちは、FLINTERSの笠本です。
この記事はアドベントカレンダー19日目の記事です。
dotfilesを触っていて、こんな感覚を持ったことはありませんか。
- 変更自体は小さいのに、やけに面倒に感じる
- 触る前に「ちょっと気合い」が必要
- 何かを追加するたびに、どこを直せばいいか一瞬考える
はっきりとした不満があるわけではない。 でも、なぜか触りたくない。
自分の場合、その原因はしばらく分かりませんでした。
設定はちゃんと動いているし、破綻もしていない。
dotfilesなんて、こんなものだろうと思っていました。
ただ、ツールを追加するたびに、毎回こうなっていました。
致命的ではありません。
でも、「あれ、これどこだっけ?」が確実に発生する。
- aliasはどこに書いた?
- install時にconfigのコピー必要だった?
- この設定、まだ使ってる?
この小さな思考停止の積み重ねが、 無意識のストレスになっていると気づきました。
この時、1つの機能 / ツールに関する関心事が、あちこちに散らばっていたことから、これは凝集性の低さが原因だろうと考えました。
そこで、dotfilesの構成を 「凝集性を上げる」 という観点で組み直してみました。
この記事では「どう構成を変えたか」「それによって何が楽になったか」を紹介します。
※この記事で紹介している dotfiles 全体はこちら
👉 https://github.com/onigiri-w2/dotfiles
どう変えたのか
取り入れた考え方は、とても単純です。
機能的凝集 ── 関連するものは、1箇所にまとめる
dotfilesにも、この考え方をそのまま適用しました。
具体的には、
1つの機能 / ツールに関するものを、すべて1つのフォルダに集約する
という構成にしています。
例えば nvim なら、こうです。
features/nvim/
├── install.zsh # nvimのinstallや依存関係のセットアップ
├── shell.zsh # alias、PATH、環境変数など
└── config/ # 設定ファイル群
「nvimに関することを知りたければ、このフォルダだけ見ればいい」
その状態を作ることを最優先にしました。
install.zsh
brew install nvim
brew install ripgrep # nvimの検索で使う
brew install lazygit # nvim内のgit操作で使う
ln -sf "$DOTFILES/features/nvim/config/nvim" "$HOME/.config/nvim"
ポイントは、nvimが依存しているものを、すべてここに書くことです。
ripgrep や lazygit は他のfeatureでも使うかもしれません。
それでも気にしないことにしました。
「このパッケージ、誰が使ってるんだっけ?」と後から辿るより、
「nvimに必要なものは、nvimのフォルダに全部ある」
状態のほうが、理解コストが圧倒的に低いからです。
brew install も冪等なので、同じパッケージを複数のfeatureで書いても実害はありません。
ここでは DRYよりも凝集性を優先しています。
shell.zsh
alias v="nvim"
alias vim="nvim"
shell.zsh は、そのfeatureに関する「alias / PATH / 環境変数」などを書く場所です。
install.zsh とは、役割を明確に分けています。
・install.zsh → 一度だけ実行される処理
・shell.zsh → 毎回読み込まれるシェル設定
全体の構造
全体はこうなっています。
dotfiles/
├── bootstrap.zsh # .zshrcから読み込むエントリポイント
├── setup.zsh # 初回セットアップ
├── shell.zsh # グローバルなシェル設定
└── features/ # 機能ごとのフォルダ
├── nvim/
├── git/
├── tmux/
└── ...
setup.zsh
新しいMacを使い始めるときに、1度だけ実行するスクリプトです。
全featuresの install.zsh を順番に実行します。
ここで必要なパッケージのインストールやシンボリックリンクの作成などをまとめて行います。
for feature in "$DOTFILES/features/"*/; do
[[ -f "$feature/install.zsh" ]] && source "$feature/install.zsh"
done
bootstrap.zsh
シェル起動時に毎回読み込まれるスクリプトです。.zshrc から source します。
全featuresの shell.zsh を読み込んで、aliasや環境変数を設定します。
# bootstrap.zsh
for feature in "$DOTFILES/features/"*/; do
[[ -f "$feature/shell.zsh" ]] && source "$feature/shell.zsh"
done
ルートの shell.zsh
特定のfeatureに属さない、グローバルな設定を書きます。
PATHの基本設定など、全体に影響するものだけを置いています。
ルートにあるファイルは
featuresを呼び出して束ねる役割で、
日常的に触るのはほぼ features/ 配下だけです。
得られた変化
構成を変えてから、dotfilesに対する感覚が明確に変わりました。
迷いが発生しなくなった
- aliasはここ
- installはここ
- configはここ
考える前に場所が決まっているので、思考が止まらなくなりました。
「触るのが億劫」がなくなった
以前は、小さな変更でも「どこに影響があるか」を一瞬考えていました。
今は、そのツールのフォルダだけを見ればいいので、心理的なハードルがかなり下がりました。
「もう要らないかも」の判断がしやすい
使わなくなったツールは、features/ツール名/ を消すだけ。
設定が1箇所にまとまっているので、「どこかに残骸がありそう」という不安が減りました。
※ ただし、brewで入れたパッケージや設定ファイルは手動で消す必要があります。(後述)
Brewfileは使わない
恩恵は得ましたが、その裏で割り切っていることもあります。
Brewfileでの一括管理
各featureの install.zsh に brew install を直接書いています。
brew bundle は使っていません。
重複installは許容する
同じパッケージを複数のfeatureでinstallしている可能性はあります。
brewは冪等なので問題ありません。
ここでも DRYより凝集性を優先しています。
uninstall, cleanup は自動化しない
featureフォルダを消しても、brewのパッケージや設定ファイルは残ります。
なので、手動でクリーンアップ作業を行う必要があります。
ただし、ツールを消す頻度は低いので現状は手動で十分だと判断しています。
おわりに:凝集性は、思考コストを下げる
今回やったことは、
「dotfilesをモダンにした」「高度な仕組みを入れた」といった話ではありません。
ただ、
凝集性が低いと人は無意識にストレスを溜める
という説に立って、構成を組み直しただけです。
1つのツールに関する情報が分断されていると、以下のような小さな思考が発生します。
- 探す
- 思い出す
- 確認する
それを 「このフォルダを見ればいい」 という状態にしただけで、dotfilesとの付き合い方がかなり楽になりました。
もし皆さんが今、dotfilesの管理に微妙なストレスを感じているなどある場合、もしやするとそれは低凝集が原因かもしれません。
暇な時間にこの観点で見直してみてるのはいかがでしょうか。
Discussion