チュートリアルの問題点
2022年3月8日、Burke Holland著、@burkeholland
優れたチュートリアルを書くのは簡単ではありません。私も多くのチュートリアルを書いてきましたが、そのすべてが大成功したわけではないので、よく分かっています。
結局のところ、優れたチュートリアルを作るということは、「何を書くか」ではなく、「開発者が一言一句読む必要なしに、成功できるかどうか」にかかっています。この記事では、Dev Containers(開発コンテナ)を利用することでユーザーが遭遇するエラーをどのように減らせるか、そしてLaravel PHPプロジェクトがどのようにこれをエレガントに実装して大きな成果を上げているかを見ていきます。
誰も読まない
私たち自身のチュートリアルである「Visual Studio CodeでDockerコンテナを開発環境として使用する方法」は、長らく完了率が低い状態でした(約4〜6%)。

どこで読者が諦めているのかを突き止めるために、私たちはユーザー調査を実施し、実際にチュートリアルを完了しようとする人々の様子を観察しました。それは……痛々しいものでした。
なぜ人々がチュートリアルを完了できないのか、理由はすぐに明らかになりました。誰も読んでいなかったのです。人々は説明を読み飛ばし、直接アクションステップへ進んでいました。その結果、説明を読んでいれば起こさなかったはずのエラーを引き起こし、行き詰まってしまうのは必然でした。
ペンシルベニア州立大学のJohn M. Carroll教授は、その記念碑的な著書『The Nurnberg Funnel - Designing Minimalist Instruction for Practical Computer Skill(ニュルンベルクの漏斗 - 実践的なコンピュータスキルのためのミニマリストな指導設計)』の中でこのことについて触れています。彼は「(学習者は)学ぶことに忙しすぎて、指導内容をほとんど活用できていない。これが『意味づけのパラドックス』である」と記しています。
私にも覚えがありますし、おそらくあなたにもあるでしょう。チュートリアルを進める際、私は手を動かしながら学ぼうとするため、コードブロックを探して目を走らせてしまいます。文字通り、学ぶことに忙しすぎて、説明を読む余裕がないのです。
人々はあなたのチュートリアルを読んでくれません。少なくとも、あなたが期待するほどには読みません。できる最善の策は、読者が学習プロセスでエラーを起こす可能性のある箇所を可能な限り排除することです。その方法の一つが、事前構成済みのコンテナ環境を使用して、環境構築のステップを完全に排除することです。
コンテナ化された開発環境
チュートリアルの大部分は、多くの場合、前提条件や環境構築の手順に割かれています。私がRuby on Railsを学ぼうとして、Windows上でRubyを正しくインストールするだけで時間を費やし、「gem」とは一体何なのか、なぜそれらがすべて見つからないのかと悩んだことを鮮明に覚えています。
コンテナ化された開発環境の背景にある考え方は、Dockerコンテナの中で開発するというものです。これにより、完全にポータブルで、完全に設定された開発環境を持つことが可能になり、いつでも立ち上げたり破棄したりできるようになります。環境を構成ファイル一式として誰かに渡すだけで済むようになります。
しかし、どうやってコンテナの中で開発するのでしょうか? コンテナにはVS Codeを起動できるようなUIがあるわけではありません。
VS Code用のDev Containers拡張機能は、まさにこれを実現します。この拡張機能には、Dockerコンテナを開発環境として構成する仕組みと、VS Codeからその環境に接続する機能が含まれています。コンテナ内に小さなサーバーコンポーネントをインストールし、ローカルのVS Codeがそれと通信することで動作します。ローカル環境にいるのと同じ感覚で開発できますが、VS Codeはローカル環境ではなくコンテナ環境に接続されています。

通常、コンテナ化された開発環境を作成するには、Dockerについて多少の知識が必要です。知っている人も多いですが、知らない人も大勢います(私には見えませんが、今、私の手は挙がっています)。そのため、この拡張機能はコンテナのセットアッププロセスを可能な限り抽象化しようとしています。私が新しいPythonコンテナをセットアップした際、ウィザードがベースイメージとPythonバージョンの選択をガイドしてくれました。さらに、選択リストからイメージに追加のソフトウェアを追加する機会も与えられます。この例では、Azure CLI、Dotnet CLI、PowerShellなどを追加しました…

このプロセスにより、プロジェクトに.devcontainerフォルダーと必要なDockerfileが追加されます。また、どの拡張機能をインストールすべきか、ビルド後にどのセットアップコマンドを実行すべきかなど、開発コンテナの側面を定義するための標準であるdevcontainer.jsonファイルも追加されます。環境とそのセットアップを完全に制御できるため、依存関係のインストールやライブラリのバージョン設定など、ほぼすべてを自動化できます。
このように、追加のセットアップ手順も、Rubyのgemによる存在の危機を感じることもなく、完全にすぐに使える環境を誰かに渡すことが文字通り可能になります。
すでに開発コンテナベースのアプローチを使用して、本来は非常に複雑な環境をユーザーがすぐに使い始められるようにしている人々がいます。その素晴らしい例が、PHP用のLaravelフレームワークです。
Laravelのソリューション
Laravelは、PHP用のオープンソースMVCフレームワークです。これは、Object Relational Mapper (ORM)、直接的なデータベースアクセス、パッケージングシステムなどを含む、包括的なフレームワークです。Laravelは多くのことができますが、実際に体験するには、少なくともデータベースが必要です。通常、ユーザーはPHPだけでなく、データベース(多くの場合MySQL)もインストールする必要があります。フレームワークを少し試してみたいという段階のユーザーにとっては、これは非常に高いハードルです。
Laravelは、コンテナ化された開発環境とSailというツールでこれに対処しています。Laravel、MySQLサーバー、Redisキャッシュを使用してゼロから始めるには、コマンドを1つ実行するだけです…
curl -s "https://laravel.build/example-app?with=mysql,redis" | bash
これにより、docker-composeファイルを含む新しいプロジェクトが作成されます。このファイルは、アプリケーションコンテナ、MySQLコンテナ、Redisコンテナの3つをセットアップします。ユーザーはコンテナやそれら3つのサービスについて何も知る必要はありません。Sailがこれらすべてを抽象化してくれます。そして、Sailコマンドを実行して環境を起動します…
./vendor/bin/sail up
サンプルアプリケーションが実行されます。PHPのインストールも、Laravelのインストールも、依存関係解決のステップも必要ありません。すぐに成功が待っています。

プロジェクトにMySQLサーバーとRedisキャッシュが含まれるように指定したため、プロジェクトの起動時には3つのコンテナが生成されます。これはVS Code用のDocker拡張機能を使用して確認できます。

これらのコンテナはネットワークで接続されているため、アプリコンテナからMySQLやRedisキャッシュのコンテナを呼び出すことができます。
sail-8.1/appコンテナにインタラクティブなターミナルを接続すると、/var/www/htmlフォルダーにプロジェクトが表示されます。Dockerはプロジェクトをマシンからコンテナへ「マウント」するため、開発中に加えた変更は、更新時に即座にアプリケーションに反映されます。

Dev Containersの追加
Dev Containers拡張機能のサポートも追加されています。このプロジェクトに適切な開発コンテナ構成を追加するには、同じプロジェクトの雛形作成時に&devcontainerフラグを追加します。
curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash
既存のSail/Laravelプロジェクトにdevcontainerを追加したい場合は、
php artisan sail:install --devcontainerを実行することで可能です。
これにより同じプロジェクト構成が作成されますが、.devcontainerフォルダーが含まれるようになります。VS Codeは自動的にそのフォルダーを検出し、プロジェクトをコンテナ内で再オープンするように促すため、sail upの手順を省略できます。

VS Codeがコンテナに接続されると、ローカルではなくコンテナ環境内で開発することになります。VS Codeの左下隅にあるリモートインジケーターがその旨を伝えてくれます…

外部ではなくコンテナ内で開発することには、いくつかの明確な利点があります。
開発コンテキストとアプリコンテキストの同期
コンテナに接続すると、開発中のコンテキストはアプリケーションが実行されているコンテキストと同じになります。そのため、ターミナルはコンテナのターミナルになります…

Dev Containers拡張機能は、アプリケーションがどこで実行されているか忘れてしまった場合のために、どのポートが転送されているかなど、状況をより包括的に可視化してくれます。

Laravelアプリケーションは自動的に起動し、アプリケーションログはコンテナログにパイプされます。アプリケーションの動作を確認したくなるのは当然なので、Dev Containers拡張機能はVS Code内に新しいビューを提供し、実行中のすべてのコンテナを確認したり、コンテナログをストリーミング接続したりできます。

開発環境セットアップの自動化
最高の開発者体験には、エディターのカスタマイズが含まれます。これにはエディター自体の設定や、デフォルト体験に追加が必要な拡張機能などのサポートが含まれます。
VS CodeとLaravelの場合、拡張機能はdevcontainer.jsonで推奨されていますが、自動的にインストールされないようにコメントアウトされています。これにより、ユーザーはエディターの正しい設定方法を探し回る代わりに、あらかじめ定義された拡張機能セットから選ぶことができます。
...
"extensions": [
// "mikestead.dotenv",
// "amiralizadeh9480.laravel-extra-intellisense",
// "ryannaddy.laravel-artisan",
// "onecentlin.laravel5-snippets",
// "onecentlin.laravel-blade"
],
読む量を減らし、行動を増やす
人は読みません。それでいいのです。Laravelのチュートリアルが他のものより短いわけではありませんが、重要なのは、コードに飛んでコマンドを実行すれば、それが動くということです。Dev Containersがそれを可能にしました。あとは、私たち自身の「Visual Studio CodeでDockerコンテナを開発環境として使用する方法」チュートリアルのために、開発コンテナを作る方法を考え出せればよいのですが……。
ハッピーコーディング!
Burke Holland (@burkeholland)