先日github.comのTeamとEnterprise CloudプランでCodespacesがご利用いただけるようになりました。Codespacesはソフトウェアチームに対して、クラウド上でより速く、よりコラボレーティブな開発環境を提供します。詳しくはCodespacesのページをご覧ください。
GitHub.comのコードベースはもうすぐ14歳になります。GitHub.comの最初のコミットがプッシュされたとき、Railsはできてからまだ2年しか経っていませんでした。AWSはできてから1年で、AzureやGCPはまだ存在していませんでした。14年という歳月はCOBOLの世界では長くないかもしれませんが、インターネットの世界ではかなりの長さです。
この14年の間に、GitHub.comのコアリポジトリ(github/github)では100万回以上のコミットが行われました。これらのコミットの大部分は、macOS上で開発やテストされたものです。
しかし、私たちの開発プラットフォームは進化しています。この数ヵ月の間に、私たちはmacOSモデルを捨てて、GitHub.comの開発の大部分をCodespacesに移行しました。これは、私たちの日々の開発フローにとって根本的な変化でした。その結果、Codespacesはより強固なものとなり、GitHub.com開発の将来に向けて万全の態勢を整えることができました。
現状
長年にわたり、私たちはローカル開発がすぐに始められるように多大な時間と労力を費やしてきました。私たちは「すべてをスクリプト化する」アプローチを採用しており、エンジニアに馴染みのあるやり方でセットアップを可能にしてきました。新入社員が github/github
をクローンし、セットアップとブートストラップのスクリプトを実行すれば、半日でGitHub.comのローカルインスタンスを起動できます。ほとんどの場合はうまくいきましたが、うまくいかないときにはブートストラップスクリプトがGitHubのIssueを作成し、新入社員を社内のサポートにつなぎました。私たちの#friction
というSlackチャンネルには、親切なエンジニアが集まっており、ありとあらゆるシステム構成をデバッグすることができました。
こういった努力にもかかわらずローカル開発はもろいままでした。一見何でもないような変更でも、ローカル環境が使えなくなってしまったり、最悪の場合、復旧のために貴重な開発者の時間が何時間も必要になってしまうこともありました。原因不明のトラブルがあまりにも頻繁に発生していたため、私たちはブートストラップスクリプトに--nuke-from-orbit
オプションを追加しました。このオプションを使うと、できるだけ多くのデータを削除し、ローカル環境を元の良好な状態に戻そうとします。
もちろん、これはソフトウェアエンジニアリングの専門家であれば誰もがすぐに理解できるよくある話です。ローカルの開発環境は壊れやすいものです。たとえ完璧に機能していたとしても、たった1つのコンテキストを持つオーダーメイドのローカル開発環境は、瞬時にどこからでもアクセスできる現在の世界には、ますますそぐわなくなってきています。
複数のプロジェクトに渡って複数のブランチで共同作業をするのは苦痛でした。ブランチに新しい依存関係が追加されたり、スキーマが変更されたり、異なるSHAから分岐したりすると、45分のブートストラップが必要になることが普通でした。私たちのコードベースは非常に頻繁に変更されるため(1日に何百もの変更をデプロイしています)、これはエンジニアリング上の摩擦の原因となっていました。
こう感じていたのは私たちだけではありませんでした。Codespacesを開発するにあたり、同様の問題を解決するためにCodespacesのようなプラットフォームを構築したトップクラスのエンジニアリング組織と協力しました。どんなに大きな規模であっても、このような生産性の損失を取り除くことで、すぐに大きな生産性の向上が期待できます。
開発インフラ
インフラの世界でのベストプラクティスは、サーバをコモディティとして扱うというものです。これは、それぞれのサーバは、唯一無二でも、不可欠でも、かけがえのないものでもないという考え方です。どのサーバも簡単に取り除き、同等のサーバと交換できます。サーバがダウンしても大丈夫です!それを壊して、別のものと交換すれば良いだけです。
しかし、私たちのローカル開発環境は、それぞれがユニークで癖があります。そのため、維持するためには常に気を配る必要があります。次にgit pull
やbootstrap
を実行したら、環境が壊れてしまい、ソフトウェアの開発に集中したいときに復旧作業のために高価なコンテキストスイッチが必要になるかもしれません。インフラのようにスタンバイ環境を用意する慣例はありません。
しかし、開発環境を自分のものとして扱うことには多くの意味があります。開発環境は、私たちが1日の大半を過ごす場所なのですから!私たちは生産性を高めるために作業環境を調整しますが、同時に自分自身を表現するためにも調整します。
Codespacesでは、開発環境をインフラと同じように、交換可能なコモディティとして扱うことができると考えました。その一方で自分自身のワークベンチを設定することも可能です。Visual Studio Code拡張機能、設定の同期、およびdotfilesリポジトリを使うことで、自分好みの環境がもたらされます。このような状況ではワークベンチのトラブルは些細な問題です。なぜならもう一度良好な状態で新しいCodespaceをプロビジョンすることで、仕事に戻ることができるからです。
Codespacesの採用
Codespacesへの移行は、既存の開発環境の欠点を解消し、製品をさらに推し進めるモチベーションとなり、全体的な開発体験を向上させるレバレッジとなりました。
私たちの移行ストーリーはハッピーエンドですが、移行の最初の段階は…困難でした。GitHub.comのリポジトリは13GB近いディスクサイズで、リポジトリのクローンを作成するだけでも20分かかります。依存関係の設定と合わせると、GitHub.com開発用のCodespaceを起動するために45分以上かかりました。また、リポジトリをCodespaceにマウントできた状態でも、アプリケーションは動作しませんでした。
14年間にわたってブートストラッププロセスに組み込まれてきたmacOSを中心とした前提条件を崩さなければなりませんでした。
こうした課題に取り組む過程で、GitHubの良さが引き出されました。過去の決定を見直したり、長年の思い込みを疑ってみたり、ソースレベルでGitHubの開発をmacOSから切り離す作業を手伝ってくれるコントリビューターが社内全体から集まりました。そしてついに(非常にゆっくりではありますが)Codespace上にLinuxホストのGitHub.comをプロビジョンし、Visual Studio Codeから接続して作業をシップできるようになりました。あとは、これをどうやってより多くの開発者に広げられるかを考えなければなりません。
45分から5分へ
私たちが目指しているCodespacesの形は、タスクごとに(1つのブランチに対して1つのCodespaceを作成するような)オンデマンドで開発環境を提供するモデルを採用することです。タスクベースのワークフローをサポートするためには、可能な限り瞬時に環境を作成できるようになる必要があります。45分という時間はタスクベースのワークフローの基準を満たすものではありませんでしたが、最適化に向けて容易に解決できる問題をいくつか見つけることができました。
まず最初に、Codespacesがgithub/githubをクローンする方法を変更しました。プロビジョン時にフルクローンするのではなく、シャロークローンを実行し、最新のコミットでCodespaceが作成された後に、バックグラウンドでリポジトリの履歴をアンシャローするようにしました。これによりクローンにかかる時間は、20分から90秒に短縮されました。
次に、GitHub.comをサポートするソフトウェアとサービス網をキャッシュすることでした。これには従来のGemfileベースの依存関係に加え、CやGoで書かれたサービス、Rubyのカスタムビルドも含まれています。これを実現するためにGitHub Actionを使って、毎晩リポジトリをクローンし、依存関係をブートストラップし、その結果に基づいてDockerイメージをビルドしてプッシュしています。そして公開されたイメージは、github/githubのdevcontainer(Codespacesの環境用のconfig-as-code)のベースイメージとして使用されます。これで私たちのCodespacesは95%以上の初期設定が既に適用された状態になりました。
この2つの変更と、いくつかのアプリケーションやサービスの最適化により、GitHub.com開発用のCodespace作成時間は45分から5分になりました。しかし、5分という時間は依然として「瞬時に作成」からはまだかなり遠いものです。よく知られている研究によると、人はフロー状態が途切れずに待つことができるのはおよそ10秒間までだそうです。飛躍的に進歩したとはいえ、まだまだ先は長いのです。
5分から10秒へ
5分という時間は大幅な改善ですが、これらの変更にはトレードオフもありました。しかし、これが製品のニーズへのヒントとなりました。
シャロークローンのアプローチは、Codespacesをすばやく立ち上げるのには便利でしたが、いつかはフルクローンのコストを払わなければなりません。Codespaces作成後のアンシャローは気が散るような副作用を伴う負荷が発生します。大規模で複雑なプロジェクトであれば、クローンやブートストラップが利用可能なリソースを奪い合うという、同じような問題に直面するでしょう。
エンジニアがCodespaceを作成しようとしたときには、すでにほとんどの作業が終わっているように、事前にリポジトリのクローンを作成しブートストラップできたらどうでしょうか?
プレビルドとは、クローンとブートストラップが完了したCodespacesのプールで、仕事をしようとする開発者から接続されるのを待っている状態のことです。プレビルドへのエンジニアリング投資は、その価値を何倍にも高めてくれました。今では信頼性の高い、設定済みのCodespacesを作成し、GitHub.com開発の準備を10秒で整えることができます。
新入社員は、Slackをインストールするよりも短い時間で、機能する開発環境をゼロから手に入れることができます。エンジニアは、オーバーヘッドなしで新しいCodespaceを切り離し、並行してワークストリームを進めることができます。たとえば環境が古すぎたり、テストデータによって何かが壊れたりといった理由で環境が壊れても、エンジニアはすぐに新しい環境を構築し、次の作業に移ることができます。
より大きなレバレッジ
Codespacesへの移行は、私たちにとって非常に現実的な問題を解決してくれました。ローカル開発環境が壊れやすく、単一の環境しかないというモデルを取り除いただけでなく、GitHubの開発者体験を向上させるための強力な新しいレバレッジポイントを手に入れたのです。
これまでのローカル環境ではコスト(時間と根気の両方)がかかりすぎて考えられなかったような、追加のセットアップや最適化を行うための出発点ができました。たとえばプレビルドでは、言語サーバのキャッシュやgemドキュメントの準備、保留中のデータベースの移行、GitHub.comとGitHub Enterprise Serverの両方の開発モードの有効化などを事前に実行し、通常はブートストラップとセットアップを繰り返し行う必要のある作業を省くことができました。
またCodespacesでは、たった一度の設定変更で、すべてのエンジニアのマシンスペックをアップグレードできます。Codespacesへの移行の初期段階では、8コア・16GB RAMのVMを使っていました。このマシンで十分でしたが、GitHub.comはさまざまなサービス網を実行しており、消費可能なすべてのコアとRAMを使います。そこで私たちは、32コア・64GB RAMのVMに移行しました。たった1行の設定変更で、すべてのエンジニアのマシンをアップグレードできました。
Codespacesはまた、社内で使われている「レビューラボ」という社内のコラボレーターと変更をプレビューするための本番に似た環境のプラットフォームから仕事を奪い始めました。Codespacesが登場する前は、GitHubのエンジニアが同僚と作業を共有するためには、コミットとレビューラボインスタンスのデプロイ(このためのピアレビューも必要です)が必要でした。これは煩雑です。今では、ctrl+クリックでプレビューURLを取得し、それを同僚に送信しています。コミットもプッシュもレビューもデプロイも不要で、Codespaceの80番ポートをライブで見ることができます。
コマンドライン
Visual Studio Codeはすばらしいツールであり、多くのGitHub.comのエンジニアがCodespaceを操作するときに使っています。しかし、VimやEmacsを使っているユーザーにグラフィカルなエディタへの移行を求めるのは良いことではありません。Codespacesが私たちの未来であるならば、私たちは全員一緒でなければなりません。
幸いなことに、GitHubで登録したSSH用公開鍵でsshd
を初期化し、22番ポートを開き、そのポートをCodespaceの外に転送するという変更をプレビルトイメージに適用することで、シェルベースの同僚も利用できるようになりました。
これで、GitHubのエンジニアはVimやEmacs、あるいはやりたければedでさえ使うことができるようになりました。
これは非常にうまくいっています!そして、Dockerイメージのキャッシングがプレビルドにつながったように、次のステップはGitHub.com開発用のCodespaceに対して行ったことを、すべてのCodespaceで利用可能にすることです。
社内での評判
変化は大変ですが、開発環境に関してはその傾向がさらに強くなります。ありがたいことにGitHubのエンジニアたちは好奇心旺盛で親切で、すぐにCodespacesのスーパーファンになってくれました。
- 昨日、私の開発環境が少し壊れていた時にCodespacesを使ったのですが、開発環境の再構築が終わる前にCodespaces上ですべての機能を完成させてしまいました(笑) ~@lindseyb
- 私はこのプロジェクトが始まる前はCodespacesに懐疑的でしたが、今はそうではありません。これこそがあるべき姿です。 ~@iolsen
- 今週のRailsの仕事は今までにないほど生産的でした。すべてがとても速く、信頼できました。 ~@jclem
- Codespacesの立ち上げに尽力してくれた人たちのおかげで、私の最初の週はすばらしいものになりました! ~@bestra
- 私のCPUがRubyをソースからコンパイルすることは二度とないことを厳粛に誓います。 ~@latentflip
いまやCodepacesがGitHub.comのデフォルトの開発環境になりました。先ほどローカル開発環境の問題をデバッグするための#friction
というSlackチャンネルの話をしましたよね?これはアーカイブされる予定です。
私たちは日々、より多くのサービスとより多くのエンジニアをCodespacesに迎え入れており、その過程でCodespacesが生み出す価値についての新しいストーリーを発見しています。しかし、それぞれのストーリーの核心には、すべてのエンジニアの心に響く一貫したテーマがあります。より良いツールを見つけて、より生産的になったので、もう元には戻りません。