こんにちは、プロダクト開発部のふくいです。
コドモンのプロダクトエンジニアの開発環境は Mac で統一されているのですが、半年ほど前に自身の開発環境を Windows11 + WSL2 の環境に移行しました。この記事では移行の際に引っかかった細かい点や、移行後に感じたことを雑多に書いてみたいと思います。
なぜ移行したのか?
主な理由は、自身が開発・保守を担当しているICTコドモンの利用者に Windows ユーザーが多いことです。 ブラウザの環境やフォントの描画、改行コードの扱い、IME の挙動、ファイルパスの違いなど、OS が違うと「ユーザー(ご利用いただいているお客さま)に見えている世界」と「開発者に見えている世界」の肌感覚にずれが生じます。実利用環境に近い OS で開発すれば、ユーザー体験の不具合や違和感をよりキャッチアップしやすくなる効果があるのでは、と感じていました。
コドモンのプロダクトエンジニアが使用する開発用のPCは原則として Mac で統一されているのですが、1on1の席で EM にこの課題感を伝えたところ、内容に共感いただき、すぐに調整に動いていただけることになりました。このような個人のぼんやりとした課題感にも素早く反応いただけるのは本当に感謝しかありません。その後、社内のセキュリティ上の要件確認やPC調達の調整などを経て、無事 Windows PC を貸与いただくことができました。
Windows とはいえ、開発そのものはネイティブの環境ではなく WSL2 の Linux 上の Docker コンテナで行う想定です。開発を行う上で Mac との使い勝手の違いはそれほど気にしなくてもよさそう、と見込んでいましたが、特に大きな詰まりどころもなく、スムーズに切り替えることができました。
移行後に遭遇した注意点
Docker でマウントしたファイルのデフォルトのオーナーとグループ
Mac の Docker Desktop はファイル共有レイヤーが所有者をうまく吸収してくれていて、ホストとコンテナで uid・gid が食い違っていても、あまり意識せずに済んでいました。一方 WSL2 のバインドマウントは Linux のネイティブなマウントなので、ホスト側の数値の uid・gid がそのままコンテナに渡ります。
たとえば WSL のデフォルトユーザーは uid・gid が 1000 であることが多いですが、コンテナ側のプロセスがそれと違うユーザーで動いていると、所有者やグループにずれが生じます。PHP の場合ですと例えば php-fpm が www-data(uid が 33 や 82 など)で動くため、コンテナが生成したキャッシュ・ログ・アップロードファイルが、ホストから見ると別ユーザー所有になってしまう、という状況が発生します。逆にホスト側(uid 1000)では、例えばビルド時に作ったsocketファイルなどにコンテナのプロセスが書き込めず、サービス提供ができない状況なども発生します。
一部の Dockerfile や docker-compose.yml で WSL2 では動作しない箇所があったため、コンテナ内の UID/GID をホスト側と明示的に合わせる対応を実施しました。
sed コマンドの非互換
地味に困ったのが sed の方言(?)の違いです。macOS は BSD sed、WSL で使用している Ubuntu は GNU sed なので、Mac 向けに書いたスクリプトがそのままでは動きません。
例えばインプレース編集についてですが、BSD では拡張子引数が必須で sed -i '' 's/.../.../' file と書きますが、GNU では sed -i 's/.../.../' file です。Mac 用の sed -i '' を WSL に持ってくると、'' が最初のファイル名として解釈されてエラーになります。
プロダクトのリポジトリ内で使われている周辺スクリプトの幾つかに BSD sed 前提で書かれているものがあったため、両方で利用できるように地道に修正していきました。
WSL2 と Windows 間のネットワーク調整
デフォルトの WSL2 は NAT ネットワークで、Windows と WSL2 が別 IP になります。Windows 側からは localhost forwarding がデフォルトで有効なため localhost で WSL2 内の環境にアクセスできますが、WSL2 側から Windows のサービスに localhost でアクセスすることはできず、PC 外の LAN からも直接 WSL2 上の環境にはアクセスができません。特に後者については、LAN 上にある検証用のタブレットやスマートフォンなどから、開発中の WSL2 上のサービスにアクセスしたい場合がありました。
こちらは %USERPROFILE%\.wslconfig に次のように書いてミラーモードを有効にすることで解消できました。
[wsl2] networkingMode=mirrored
ただ Docker Desktop のポートフォワードと衝突してポートが掴めなくなるなどの事象も報告されているとのことで、Docker 中心の構成の場合は一度試してから継続利用を判断するのが安全かと思います。私の構築している環境上では特に問題になることはありませんでした。
Claude Code の hooks で完了を音で知らせる
タスクを Claude Code にお任せする場合に、終わったタイミングを通知表示と音で知りたくなります。Mac の際と同様に Claude Code の ~/.claude/settings.json でライフサイクルイベントにコマンドを設定しました。エージェントが停止して何らか聞かれる際に発火するのが Notification イベント、応答を終えたときに発火するのが Stop イベントです。
Powershell で通知センターにメッセージを通知しつつ SoundPlayer で音を鳴らすコマンドを追加しました。起動が少々重いためか少しタイミングがずれることがありますが、概ね問題なく動作しています。標準に用意されているものだけで設定できるのがお手軽でとてもうれしいです。
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -ExecutionPolicy Bypass -Command \"Add-Type -AssemblyName System.Windows.Forms; \\$n = New-Object System.Windows.Forms.NotifyIcon; \\$n.Icon = [System.Drawing.SystemIcons]::Information; \\$n.BalloonTipIcon = 'Info'; \\$n.BalloonTipTitle = 'Claude Code'; \\$n.BalloonTipText = '確認事項があります'; \\$n.Visible = \\$true; \\$n.ShowBalloonTip(5000); (New-Object System.Media.SoundPlayer 'C:\\Windows\\Media\\notify.wav').PlaySync(); Start-Sleep -Seconds 6; \\$n.Dispose()\""
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -ExecutionPolicy Bypass -Command \"Add-Type -AssemblyName System.Windows.Forms; \\$n = New-Object System.Windows.Forms.NotifyIcon; \\$n.Icon = [System.Drawing.SystemIcons]::Information; \\$n.BalloonTipIcon = 'Info'; \\$n.BalloonTipTitle = 'Claude Code'; \\$n.BalloonTipText = '作業が完了しました'; \\$n.Visible = \\$true; \\$n.ShowBalloonTip(5000); (New-Object System.Media.SoundPlayer 'C:\\Windows\\Media\\tada.wav').PlaySync(); Start-Sleep -Seconds 6; \\$n.Dispose()\""
}
]
}
],
},
以下の記事の内容をほぼそのまま拝借させていただきました。ありがとうございます!
移行後の所感
移行後、半年経った上での所感を簡単にまとめてみました。
よかったところ
幾つか細かい点はあったものの、移行は想定よりもスムーズで引っかかる点はあまりありませんでした。また、開発者個人としてのぼんやりとした薄い不安感(この処理 Edge でもちゃんと動作するんだろうか、、)や、地味なストレス(Windows のテストのために別環境を準備しなければ、、)などの解消はあると感じました。お問い合わせやトラブル対応時の再現性確認や環境による問題の切り分けなどで実務上の効果が出ている場面もあり、使い慣れや手元にあるがゆえのメリットは十分に享受できていると感じています。
効果を感じられなかったところ
当初目的の、ユーザー体験の不具合や違和感をよりキャッチアップしやすくなったか、という点については、これまでのところ残念ながら開発している中で目に見えるような実感は得られませんでした。これだけではお客さまの体験を肌感覚で理解する手段としてはあまりにもナイーブで小手先に過ぎる対応だったか、と振り返ってみて思います。
総評
当初目的の直接の効果は得られませんでしたが、現時点では個人としては実務上で一定の意義はあると感じており、私の開発環境は引き続き Windows + WSL2 の環境を維持していきたいと考えています。
課題と感じているところ
一方でいくつか課題と感じた点もあります。
Windows 利用者は今後増える?
Mac, Windows (WSL2 = Linux) の両環境で動作するコードや開発環境を維持するには、小さいながらもコストがかかります。また、開発者のPC環境を揃えておくことは、調達その他のコストの観点だけでなく、セキュリティ上の観点からも考える必要があります。
とはいえ、開発環境のOSその他の選択は開発体験に大きく関連する要因でもあり、Windows で開発したい!という方が、私以外にも現れた場合に備えて、柔軟に対応できるようにしておきたい、と現時点では考えています。
CLI 認証時にブラウザが WSL2 側で開く
aws sso login や gh auth login のように認証でブラウザを開くコマンドでは、WSL2(Linux)側のブラウザを起動しようとして、ホスト(Windows)側の普段使いのブラウザに飛んでくれません。
今はログイン URL を Windows 側のブラウザにただただコピペして対応しています。wslu パッケージの wslview を入れて BROWSER=wslview を設定すると、Windows のデフォルトブラウザで開けるようになる、という話も耳にしたので、おいおい乗り換えたいと考えています。
なお、gauge + Playwright や Vitest による Web のブラウザテストは WSL2 側の Chrome でそのまま動かしていまして、今のところ大きな問題は出ていないのですが、これもどちらでも動作できるようにしておきたいところです。
まとめ
自身の開発環境を Mac から Windows11 + WSL2 に乗り換えた話を書いてみました。思ったよりもスムーズに乗り換えることができましたので、もし乗り換えの意義や課題感を持っている方がいらっしゃいましたら、試してみていただけると幸いです。