参加你附近的 ,了解 VS Code 中的 AI 辅助开发。

教程的问题

2022年3月8日,作者:Burke Holland, @burkeholland

编写一篇出色的教程并非易事。我深有体会——因为我写过很多,但并非每一篇都大获成功。

事实证明,写出好教程的关键不在于你写了什么,而在于开发者是否无需阅读每个字就能成功上手。在本文中,我们将探讨开发容器如何减少用户可能遇到的错误,以及 Laravel PHP 项目 如何在其教程中优雅地实现这一点,并取得了巨大成功。

没人会阅读

我们自己关于如何在 Visual Studio Code 中使用开发容器的教程,完成率一直很低——大约在 4% 到 6% 之间。

dev containers learn module screenshot

为了找出人们在哪个环节放弃,我们进行了用户研究,观察人们尝试完成我们教程的过程。那场面……相当痛苦。

人们无法完成教程的原因显而易见:根本没人阅读。大家直接跳过说明,直奔操作步骤。结果不可避免地,他们会因为犯下某个错误而卡住,而如果他们读了说明,这个错误本可以避免。

宾夕法尼亚州立大学教授 John M. Carroll 在他的开创性著作《The Nurnberg Funnel - Designing Minimalist Instruction for Practical Computer Skill》中谈到了这一点。他写道:“[学习者] 忙于学习,以至于无暇充分利用说明。这就是‘意义建构的悖论’。”

对此我感同身受,你可能也一样。当我在看一篇教程时,我的眼睛会扫描代码块,因为我想通过实践来学习。我真的太忙于学习,以至于没时间阅读说明。

人们不会阅读你的教程。至少不会像你希望的那样仔细阅读。你能做的最好的事情,就是尽可能地消除学习过程中可能出错的地方。其中一种方法是使用预配置的容器环境,完全移除任何环境设置步骤。

容器化开发环境

任何教程的很大一部分通常都专门用于罗列一长串的先决条件和环境设置。我清楚地记得,当初我尝试学习 Ruby on Rails 时,大部分时间都花在如何在 Windows 上正确安装 Ruby 上——搞不清楚“gem”到底是什么,以及为什么它们好像全都缺失了。

容器化开发环境的理念是,你在一个 Docker 容器内部进行开发。这使得拥有一个完全可移植、配置齐全的开发环境成为可能,你可以随时启动或销毁它。然后,你可以将这个环境以一组配置文件的形式交给别人。

但是,你如何在一个容器内部进行开发呢?容器又不像有用户界面,可以让你直接启动 VS Code。

VS Code 的 Dev Containers 扩展正是为此而生。它既包含了将 Docker 容器配置为开发环境的机制,也允许你从 VS Code 连接到该环境。它通过在容器内安装一个小型服务器组件来实现这一点,你本地的 VS Code 会与该组件通信。然后,你就像在本地开发一样,只不过 VS Code 连接的是容器环境,而不是你的本地环境。

The Dev Containers extension screenshot from extension gallery

为了创建一个容器化的开发环境,你通常需要对 Docker 有所了解。很多人确实了解,但也有很多人了解(你看不到我,但我举手了),所以该扩展试图尽可能地将容器设置过程抽象化。我设置了一个新的 Python 容器。一个向导会引导你选择基础镜像和 Python 版本。然后,它让你有机会通过一个选择列表向镜像中添加额外的软件。在这个例子中,我添加了 Azure CLI、Dotnet CLI 和 PowerShell……

Adding a dev container configuration to a Python project

这个过程会在项目中添加一个 .devcontainer 文件夹,其中包含必要的 Dockerfile。它还会添加一个 devcontainer.json 文件,这是一个定义开发容器各方面特性的标准文件,比如应该安装哪些扩展、容器构建后应该运行哪些设置命令等。由于你可以完全控制环境及其设置,你几乎可以自动化所有事情——包括依赖安装、库版本等。

通过这种方式,你完全可以把一个完整、即用型的环境交给别人,而不需要任何额外的设置步骤,也不会让他们因为 Ruby gem 而陷入存在危机。

一些人已经在使用基于开发容器的方法,来帮助他们的用户快速上手那些原本非常复杂的环境。PHP 的 Laravel 框架就是一个很好的例子。

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,也无需解决依赖问题。立刻就能成功。

An example Laravel application running in the browser on localhost

我指定了我们的项目有一个 MySQL 服务器和一个 Redis 缓存,所以当项目启动时,我们实际上得到了三个容器。我们可以使用 VS Code 的 Docker 扩展来看到这一点。

The Docker extension in VS Code

这些容器通过网络连接在一起,这样我们就可以从应用容器中调用 MySQL 或 Redis 缓存容器。

如果你将一个交互式终端连接到 sail-8.1/app 容器,你会在 /var/www/html 文件夹中看到你的项目。Docker 将项目从你的机器“挂载”到容器中,所以你在开发过程中所做的任何更改,刷新后都会反映在应用程序中。

The file structure of the Laravel project in a container

添加 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 步骤。

A notification in VS Code saying "Reopen in container"

VS Code 会附加到容器上,所以你是在容器环境内部进行开发,而不是在你的本地环境。你会知道这一点,因为 VS Code 左下角的远程指示器会告诉你……

The remote indicator in VS Code showing connection to a container

在容器内开发相比于在容器外,有一些明显的优势。

开发上下文与应用上下文一致

当连接到容器时,你进行开发的上下文与应用程序运行的上下文是相同的。所以你的终端就变成了容器的终端……

The VS Code terminal connected to the running container instance

Dev Containers 扩展还为你提供了一个更全面的视图,让你了解正在发生什么,比如哪些端口被转发了——以防你忘记应用程序在哪里运行。

The port forwarding view in VS Code showing port 80 forwarded

Laravel 应用程序会自动启动,应用程序日志会通过管道传输到容器日志。由于你可能想看到应用程序中发生了什么,Dev Containers 扩展在 VS Code 中提供了一个新的视图,在那里你可以看到所有正在运行的容器,并连接以流式传输容器日志。

The Laravel application container logs in 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)