教程的问题
2022年3月8日,作者:Burke Holland,@burkeholland
编写一份出色的教程并不容易。我深有体会——我写过很多教程,并非每一篇都取得了巨大成功。
事实证明,制作一份出色的教程不在于你写了什么,而在于开发者是否无需阅读每一个字就能顺利完成操作。在本文中,我们将探讨开发容器(development containers)如何减少用户可能遇到的错误,以及 Laravel PHP 项目是如何在他们的教程中优雅地实现这一点的,并取得了显著的效果。
没有人会阅读
我们自己关于如何在 Visual Studio Code 中使用开发容器的教程,长期以来完成率一直很低——仅为 4% 到 6% 左右。

为了找出人们放弃的原因,我们进行了用户研究,并观察了人们尝试完成我们教程的过程。那场面……真让人心痛。
人们无法完成教程的原因显而易见:根本没人看。人们直接跳过了说明,直接进入操作步骤。不可避免地,他们会因为犯了如果不阅读说明本可以避免的错误而卡住。
宾夕法尼亚州立大学教授 John M. Carroll 在其开创性著作《纽伦堡漏斗——为实用计算机技能设计极简主义教学》(The Nurnberg Funnel)中谈到了这一点。他写道:“(学习者)太忙于学习,以至于无法充分利用说明书。这就是意义构建的悖论。”
我对此深有同感,你可能也一样。当我阅读教程时,我的眼睛会在代码块中寻找,因为我试图通过实践来学习。我确实太忙于学习,而无暇去阅读说明。
人们不会阅读你的教程。或者至少不会像你希望的那样认真阅读。你所能做的最好的事情,就是尽可能消除读者在学习过程中可能犯错的地方。实现这一点的一种方法是使用预配置的容器环境,完全移除所有环境设置步骤。
容器化开发环境
教程中通常有很大一部分专门用于列出各种先决条件和环境设置。我清楚地记得学习 Ruby on Rails 的经历,大部分时间都在尝试在 Windows 上正确安装 Ruby —— 想着“gem”到底是什么,为什么它们总是莫名其妙地缺失。
容器化开发环境背后的理念是:你在 Docker 容器内部进行开发。这使得拥有一种完全可移植、已完全配置的开发环境成为可能,你可以随意创建或销毁它。然后,你只需将一组配置文件交给他人,即可交付该环境。
但是,你如何在容器内部进行开发呢?容器又没有 UI 界面让你直接启动 VS Code。
VS Code 的 Dev Containers 扩展正是为此而生。它既包含了将 Docker 容器配置为开发环境的机制,又允许你从 VS Code 连接到该环境。它是通过在容器内安装一个小型服务器组件来实现的,你的本地 VS Code 会与该组件通信。你的开发方式就像在本地开发一样,但 VS Code 实际上是挂载在容器环境而非你的本地环境上。

为了创建容器化开发环境,通常你需要了解一些 Docker 知识。许多人懂,但许多人不懂(虽然你看不到,但我现在正举着手),所以该扩展尽可能地抽象化了容器设置过程。我设置了一个新的 Python 容器。向导会引导你选择基础镜像和 Python 版本。然后,它让你有机会通过选择列表为镜像添加额外的软件。在这种情况下,我添加了 Azure CLI、Dotnet CLI 和 PowerShell……

这个过程会向项目中添加一个包含必要 Dockerfile 的 .devcontainer 文件夹。它还会添加一个 devcontainer.json 文件,这是定义开发容器各方面的标准,比如应该安装哪些扩展,容器构建后应该运行哪些设置命令等。由于你对环境及其设置拥有完全的控制权,你可以自动化几乎所有事情——包括依赖项安装、库版本等。
通过这种方式,你可以确实地将一个完整、即用型的环境交给别人,而无需任何额外的设置步骤,也不会再因 Ruby gems 的问题而陷入存在主义危机。
有些人已经在使用基于开发容器的方法,让用户能够快速上手那些极其复杂的环境。Laravel PHP 框架就是一个很好的例子。
Laravel 的解决方案
Laravel 是一个用于 PHP 的开源 MVC 框架。它非常全面,包括对象关系映射(ORM)、直接数据库访问、包管理系统等。Laravel 功能强大。为了体验它,当你开始时,至少需要有一个数据库。通常,这要求用户不仅要安装 PHP,还要安装数据库(通常是 MySQL)。当用户只是想尝试一下你的框架时,这要求太高了。
Laravel 通过容器化开发环境和一个名为 Sail 的工具解决了这个问题。要从零开始使用 Laravel、MySQL 服务器和 Redis 缓存,你只需要运行一个命令……
curl -s "https://laravel.build/example-app?with=mysql,redis" | bash
这会创建一个包含 docker-compose 文件的新项目。该文件设置了三个容器——一个应用程序容器、一个 MySQL 容器和一个 Redis 容器。你不必了解任何关于容器或这三个服务的信息。Sail 为你抽象了所有这些复杂性。然后你运行 Sail 命令来启动环境……
./vendor/bin/sail up
示例应用程序直接运行起来了。无需安装 PHP。无需安装 Laravel。无需处理依赖解析步骤。直接成功。

我指定了我们的项目需要 MySQL 服务器和 Redis 缓存,因此在项目启动时我们实际上得到了三个容器。我们可以使用 VS Code 的 Docker 扩展查看它们。

这些容器通过网络连接在一起,以便我们能够从应用程序容器调用 MySQL 或 Redis 缓存容器。
如果你将交互式终端连接到 sail-8.1/app 容器,你将在 /var/www/html 文件夹中看到你的项目。Docker 将你机器上的项目“挂载”到容器中,因此你在开发过程中所做的任何更改,在刷新页面时都会反映在应用程序中。

添加开发容器
该项目也添加了对 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 左下角的远程指示器(Remote Indicator)看出这一点……

在容器内开发与在容器外开发相比,有一些显著的优势。
开发环境与应用环境保持一致
当连接到容器时,你开发所处的环境与应用程序运行的环境是相同的。因此,你的终端就变成了容器的终端……

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 的教程并不一定比其他教程短,但重要的是,如果你直接跳到代码并运行命令,它就能工作。开发容器使这一切成为可能。现在,如果我们能想出如何为我们自己的使用 Docker 容器作为 Visual Studio Code 开发环境教程制作一个开发容器就好了……
编码愉快!
Burke Holland (@burkeholland)