领域特定语言(DSL):后端世界中那些"不像代码的代码"
前言
在一个真实案例中,工程师 Armin 在新公司用 AI 构建了一套基础设施服务,总计约 4 万行代码(Go + YAML + Pulumi + SDK 胶水代码),其中超过 90% 由 AI 生成。这个案例中出现了许多初学者不熟悉的术语:YAML、Pulumi、HCL、Lua、SDK 胶水代码……它们既不是 Python,也不是 JavaScript,却在后端项目中无处不在。本文将从一个统一的视角——领域特定语言(DSL)——来系统地介绍这些技术。
本文的学习目标
在后端开发中,除了用通用编程语言(Python、Go、Java 等)编写的业务逻辑之外,还存在大量用途各异、语法各异、但都不属于通用编程语言的文件和代码。它们有一个共同的上位概念:DSL(Domain-Specific Language,领域特定语言)。
学完本文后,你将能够:
- 理解 DSL 与通用编程语言(GPL)的本质区别
- 掌握 DSL 的分类体系:数据序列化格式、嵌入式脚本语言、基础设施定义语言
- 区分 XML、JSON、YAML、TOML、CSV、Protobuf 等数据格式的适用场景
- 理解 Lua 等嵌入式脚本语言的设计目的
- 解释 Terraform(HCL)和 Pulumi 的原理与区别
- 理解 OpenAPI 规范与 SDK 自动生成的工作原理
- 判断哪些类型的代码适合交给 AI 生成
| 章节 | 主题 | 核心概念 |
|---|---|---|
| 第 1 章 | DSL 总论 | DSL vs GPL 的定义、分类体系与全景图 |
| 第 2 章 | 数据序列化格式 | XML、JSON、YAML、TOML、CSV、Protobuf 等 |
| 第 3 章 | 嵌入式脚本语言 | Lua 等语言的设计哲学与典型应用 |
| 第 4 章 | 基础设施即代码 | Terraform(HCL)、Pulumi 的原理与对比 |
| 第 5 章 | 胶水代码与 SDK 生成 | OpenAPI 规范与客户端代码自动生成 |
| 第 6 章 | AI 与 DSL 的关系 | 为什么 AI 特别擅长生成 DSL 代码 |
1. DSL 总论:通用语言之外的另一个世界
1.1 什么是 DSL?
DSL(Domain-Specific Language,领域特定语言) 是为某个特定领域或特定任务设计的语言。与之相对的是 GPL(General-Purpose Language,通用编程语言),如 Python、Java、Go、C++ 等——它们被设计为可以解决任意计算问题。
两者的核心区别:
| 维度 | GPL(通用编程语言) | DSL(领域特定语言) |
|---|---|---|
| 设计目标 | 解决任意计算问题 | 解决某个特定领域的问题 |
| 表达范围 | 图灵完备,理论上可以计算任何东西 | 通常有意限制表达范围 |
| 学习成本 | 较高,需要理解完整的语言体系 | 较低,只需理解该领域的概念 |
| 典型代表 | Python、Java、Go、C++、JavaScript | SQL、HTML/CSS、正则表达式、YAML、HCL |
你其实早就在使用 DSL 了:
- SQL 是数据库查询领域的 DSL——你用
SELECT * FROM users WHERE age > 18来查数据,而不是用 Python 手写遍历逻辑 - HTML/CSS 是网页结构与样式领域的 DSL——你用标签和属性描述页面,而不是用 C++ 操作像素
- 正则表达式 是文本模式匹配领域的 DSL——你用
\d{3}-\d{4}匹配电话号码,而不是手写字符比较循环
1.2 DSL 的分类
DSL 可以按照"是否具备图灵完备性"分为两大类:
外部 DSL(External DSL)
拥有独立的语法和解析器,不依附于任何通用编程语言。用户编写的代码由专用的解释器或编译器处理。
- 纯数据描述型:JSON、YAML、XML、TOML、CSV、Protobuf(不含任何逻辑)
- 查询/操作型:SQL、GraphQL、正则表达式(有限的逻辑能力)
- 领域建模型:HCL(Terraform)、Dockerfile、Nginx 配置语法(声明式描述特定领域的状态)
内部 DSL(Internal DSL / Embedded DSL)
寄生在某门通用编程语言内部,利用宿主语言的语法来构建领域专用的表达方式。代码本身是合法的宿主语言代码,但读起来像是一门专用语言。
- Pulumi(用 TypeScript/Python/Go 编写,但 API 设计得像声明式配置)
- Ruby on Rails 的路由定义(
get '/users', to: 'users#index',合法的 Ruby 代码,但读起来像配置) - 测试框架中的断言语法(
expect(value).toBe(42),合法的 JavaScript,但读起来像自然语言)
1.3 后端项目中的 DSL 全景图
在一个典型的后端项目中,你会遇到以下几类 DSL:
后端项目中的 DSL
├── 数据序列化格式(描述数据结构)
│ ├── 文本格式:JSON、YAML、XML、TOML、CSV、INI
│ └── 二进制格式:Protobuf、MessagePack、Avro、BSON
├── 嵌入式脚本语言(可编程的配置层)
│ ├── Lua(游戏引擎、Nginx、Redis)
│ ├── GDScript(Godot 引擎)
│ └── Jsonnet(配置模板生成)
├── 基础设施与运维 DSL(声明式描述系统状态)
│ ├── HCL(Terraform)
│ ├── Dockerfile / Docker Compose YAML
│ └── Nginx / Apache 配置语法
└── 接口描述语言(描述 API 契约)
├── OpenAPI / Swagger
├── Protocol Buffers(.proto 文件)
└── GraphQL Schema理解了这张全景图,后续章节将逐一展开每个分支。
2. 数据序列化格式:用文本描述结构化数据
2.1 什么是数据序列化?
序列化(Serialization) 是指将内存中的数据结构(对象、字典、数组等)转换为一种可存储或可传输的文本/字节流的过程。反过来,从文本/字节流还原为内存中的数据结构,称为反序列化(Deserialization)。
数据序列化格式是 DSL 中最基础的一类——它们属于纯数据描述型外部 DSL,不具备任何逻辑能力,只负责静态地描述"值是什么"。
2.2 为什么需要这些格式?
假设你开发了一个后端服务,数据库地址为 localhost:5432。如果将这个地址硬编码在源代码中,本地开发没有问题,但部署到生产环境时,数据库地址变为 db.prod.company.com:5432,你就需要修改源代码并重新编译。
工程实践中的通用做法是:将可变的参数从代码中分离出来,存放在独立的配置文件中。 程序在启动时读取配置文件,根据其中的值来决定行为。
除了配置之外,数据序列化格式还广泛用于:系统间的数据交换(API 请求/响应)、数据持久化存储、跨语言通信等场景。
2.3 人类可读的文本格式
以下是工程中最常见的文本序列化格式,按历史顺序介绍。
INI
最早期的配置格式,起源于 Windows 系统。结构简单,由节(section)和键值对组成:
[database]
host = localhost
port = 5432
[server]
debug = true优点是可读性强。局限在于不支持嵌套结构和数组类型,无法表达复杂配置。目前主要出现在遗留系统和部分 Linux 配置中(如 php.ini、my.cnf)。
CSV
CSV(Comma-Separated Values,逗号分隔值) 是最简单的表格数据格式:
name,age,city
Alice,30,Beijing
Bob,25,Shanghai每行是一条记录,字段之间用逗号分隔。CSV 广泛用于数据导入导出、电子表格交换、数据分析管道。它的局限是只能表达扁平的二维表格,不支持嵌套结构,且没有类型信息(所有值都是字符串)。
XML
XML(eXtensible Markup Language,可扩展标记语言) 诞生于 1998 年,曾经是数据交换的主流标准:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<database>
<host>localhost</host>
<port>5432</port>
</database>
<server>
<debug>true</debug>
<allowed_origins>
<origin>https://example.com</origin>
<origin>https://app.example.com</origin>
</allowed_origins>
</server>
</config>XML 的表达力非常强,支持嵌套、属性、命名空间、Schema 验证等高级特性。但它的语法冗长——大量的开闭标签导致信噪比低,手动编写和阅读的体验较差。
XML 在以下领域仍然广泛使用:
- Java 生态(Maven 的
pom.xml、Spring 配置、Android 布局文件) - 企业级 Web 服务(SOAP 协议)
- 办公文档格式(
.docx、.xlsx本质上是 ZIP 压缩的 XML 文件集合) - RSS/Atom 订阅源、SVG 矢量图形
JSON
JSON(JavaScript Object Notation) 诞生于 2001 年,因其简洁性迅速取代 XML 成为 Web API 数据交换的事实标准:
{
"database": {
"host": "localhost",
"port": 5432
},
"server": {
"debug": true
}
}优点是结构清晰,几乎所有编程语言都有原生解析支持。主要缺点是不支持注释,且大量的括号和引号在手动编写时容易出错。JSON 同时也是前端项目配置的标准格式(package.json、tsconfig.json)。
YAML
YAML(YAML Ain't Markup Language) 同样诞生于 2001 年,是目前后端和 DevOps 领域使用最广泛的配置格式。Docker Compose、Kubernetes、GitHub Actions 等工具均采用 YAML:
# 数据库配置
database:
host: localhost
port: 5432
# 服务器配置
server:
debug: true
allowed_origins:
- https://example.com
- https://app.example.com优点是支持注释、语法简洁、可表达复杂嵌套结构。缺点是依赖缩进来表示层级关系,缩进错误会导致解析失败,这是初学者最常遇到的问题。
补充:YAML 的全称 "YAML Ain't Markup Language" 是一个递归缩写。
TOML
TOML(Tom's Obvious Minimal Language) 诞生于 2013 年,被 Rust 的包管理器 Cargo 和 Python 的 pyproject.toml 采用:
[database]
host = "localhost"
port = 5432
[server]
debug = true
allowed_origins = [
"https://example.com",
"https://app.example.com"
]TOML 试图兼顾 INI 的简洁性和 YAML 的表达力,同时避免缩进敏感带来的问题。
2.4 二进制序列化格式
上述格式都是人类可读的文本。在对性能和体积有更高要求的场景中,还存在一类二进制序列化格式——它们牺牲可读性,换取更小的体积和更快的解析速度。
| 格式 | 开发方 | 特点 | 典型使用场景 |
|---|---|---|---|
| Protocol Buffers (Protobuf) | 需要预定义 .proto Schema 文件,强类型,体积极小 | gRPC 通信、Google 内部服务、高性能微服务 | |
| MessagePack | 社区 | 类似 JSON 的二进制版本,无需 Schema | Redis 内部编码、跨语言高性能通信 |
| Avro | Apache | 支持 Schema 演进,适合大数据场景 | Hadoop / Kafka 生态的数据序列化 |
| BSON | MongoDB | JSON 的二进制扩展,支持更多数据类型 | MongoDB 数据库内部存储格式 |
以 Protocol Buffers 为例,需要先定义 Schema:
// user.proto
syntax = "proto3";
message User {
string name = 1;
int32 age = 2;
string email = 3;
}然后通过编译器(protoc)自动生成各语言的序列化/反序列化代码。这种"先定义 Schema,再生成代码"的模式与后文将介绍的 OpenAPI SDK 生成思路一致。
2.5 完整对比
| 格式 | 类型 | 诞生年代 | 可读性 | 支持注释 | 典型使用场景 |
|---|---|---|---|---|---|
| INI | 文本 | 1980s | 高 | ✅ | 系统配置、遗留项目 |
| CSV | 文本 | 1972 | 高 | ❌ | 数据导入导出、表格交换 |
| XML | 文本 | 1998 | 中 | ✅ | Java 生态、企业级 Web 服务、文档格式 |
| JSON | 文本 | 2001 | 高 | ❌ | Web API 数据交换、前端配置 |
| YAML | 文本 | 2001 | 高 | ✅ | Docker、K8s、CI/CD、后端服务配置 |
| TOML | 文本 | 2013 | 高 | ✅ | Rust / Python 项目配置 |
| Protobuf | 二进制 | 2008 | 无 | — | gRPC、高性能微服务通信 |
| MessagePack | 二进制 | 2008 | 无 | — | 高性能跨语言通信 |
| Avro | 二进制 | 2009 | 无 | — | Hadoop / Kafka 大数据管道 |
| BSON | 二进制 | 2009 | 无 | — | MongoDB 内部存储 |
要点:所有这些格式的本质功能相同——将结构化数据转换为可存储、可传输的形式。文本格式优先考虑人类可读性和易编辑性;二进制格式优先考虑解析性能和传输体积。选择哪种格式取决于具体场景的需求权衡。
3. 嵌入式脚本语言:可编程的配置层
3.1 概念定义
Python、JavaScript、Go 等语言是通用编程语言(General-Purpose Language),它们可以独立运行,构建完整的应用程序。
与之不同,还有一类语言专门设计为嵌入到其他宿主程序中运行,为宿主程序提供可编程的扩展能力。这类语言被称为嵌入式脚本语言(Embedded Scripting Language)。
它们解决的核心问题是:当静态配置文件(YAML/JSON)的表达力不够,需要引入条件判断、循环等逻辑时,如何在不修改宿主程序源码的前提下实现动态行为。
3.2 Lua:最具代表性的嵌入式脚本语言
Lua(葡萄牙语中"月亮"的意思)是一门极其轻量的脚本语言,整个解释器编译后仅几百 KB。它的设计目标不是独立运行,而是作为可嵌入的扩展层。
Lua 的典型应用场景:
游戏引擎:《魔兽世界》的插件系统、《Roblox》的游戏脚本均使用 Lua。游戏引擎用 C/C++ 实现核心渲染和物理计算,将关卡逻辑、NPC 对话等频繁变动的部分交给 Lua 脚本。这样,策划人员修改游戏内容时不需要重新编译引擎。
Web 服务器:OpenResty 将 Lua 嵌入 Nginx 内部,使运维人员可以用 Lua 脚本实现请求过滤、限流、鉴权等逻辑,而无需修改 Nginx 的 C 源码。
数据库:Redis 支持将 Lua 脚本发送到服务端执行,用于实现需要原子性保证的复合操作(如"先读后写")。
以下是一段嵌入在 Nginx(OpenResty)中的 Lua 脚本示例:
-- 功能:对 /api/secret 路径进行 token 鉴权
local uri = ngx.var.uri
local token = ngx.req.get_headers()["Authorization"]
if uri == "/api/secret" and token ~= "Bearer my-secret-token" then
ngx.status = 403
ngx.say("Access denied")
return ngx.exit(403)
end3.3 其他嵌入式脚本语言
| 语言 | 宿主环境 | 典型用途 |
|---|---|---|
| Lua | 游戏引擎、Nginx(OpenResty)、Redis | 游戏逻辑、网关策略、缓存操作 |
| VimScript / Lua | Vim / Neovim 编辑器 | 编辑器插件开发 |
| Emacs Lisp | Emacs 编辑器 | 编辑器行为自定义 |
| GDScript | Godot 游戏引擎 | 游戏逻辑脚本 |
| Jsonnet | Kubernetes 生态 / 配置生成工具 | 模板化生成大量相似的 JSON/YAML 配置 |
要点:嵌入式脚本语言在 DSL 分类中属于内部 DSL 与外部 DSL 的交界地带——它们是独立的语言(有自己的语法和解释器),但设计目标是嵌入宿主程序运行,而非独立构建应用。它们填补了"静态配置文件"(纯数据描述型 DSL)与"通用编程语言"(GPL)之间的空白:当配置需要表达逻辑(条件判断、循环、函数调用)时,嵌入一门轻量脚本语言是工程上的标准解决方案。
4. 基础设施即代码(Infrastructure as Code)
4.1 什么是"基础设施"
在后端工程中,"基础设施"(Infrastructure)指的是应用程序运行所依赖的底层资源:
- 计算资源:服务器(虚拟机或容器)
- 数据存储:数据库实例、对象存储桶
- 网络:防火墙规则、负载均衡器、DNS 配置
- 中间件:消息队列、缓存集群
在云计算时代,这些资源通过云服务商(如 AWS、阿里云、腾讯云)的控制台以图形界面的方式创建和管理。
4.2 手动管理的局限性
通过控制台手动操作在小规模项目中可行,但随着项目规模增长,会暴露以下问题:
- 不可重复:操作步骤没有记录,无法精确复现同一套环境
- 不可审计:无法追溯"谁在什么时间修改了什么配置"
- 不可协作:操作过程无法纳入版本控制,无法进行代码审查
- 容易出错:手动操作在生产环境中存在误操作风险
基础设施即代码(Infrastructure as Code,简称 IaC) 的核心思想是:用代码来声明式地定义基础设施资源,使其具备版本控制、自动化执行和可重复部署的能力。
4.3 Terraform
Terraform 是目前使用最广泛的 IaC 工具,由 HashiCorp 公司开发。它使用专用的 HCL(HashiCorp Configuration Language) 语言。
Terraform 采用声明式范式:用户描述期望的最终状态,Terraform 自动计算从当前状态到目标状态所需的操作。
# 定义一台云服务器
resource "aws_instance" "my_server" {
ami = "ami-0c55b159cbfafe1f0" # 操作系统镜像
instance_type = "t3.micro" # 实例规格
tags = {
Name = "my-first-server"
}
}
# 定义一个 PostgreSQL 数据库实例
resource "aws_db_instance" "my_database" {
engine = "postgres"
instance_class = "db.t3.micro"
username = "admin"
password = "please-use-secrets-manager"
}执行流程:
terraform plan # 预览将要执行的变更
terraform apply # 确认并执行,自动在云平台创建资源4.4 Pulumi
Pulumi 提供了另一种思路:直接使用通用编程语言(TypeScript、Python、Go 等)来定义基础设施,而非学习专用的 HCL 语法。
同样的服务器定义,用 Pulumi + TypeScript 表达如下:
import * as aws from "@pulumi/aws";
const server = new aws.ec2.Instance("my-server", {
ami: "ami-0c55b159cbfafe1f0",
instanceType: "t3.micro",
tags: { Name: "my-first-server" },
});
const bucket = new aws.s3.Bucket("my-bucket", {
acl: "private",
});
export const serverIp = server.publicIp;由于使用的是通用编程语言,开发者可以利用循环、条件判断、函数抽象等语言特性来处理复杂的基础设施逻辑。
4.5 Terraform 与 Pulumi 的对比
| 维度 | Terraform | Pulumi |
|---|---|---|
| 语言 | HCL(专用语言) | TypeScript / Python / Go 等通用语言 |
| 学习成本 | 需要学习 HCL 语法 | 使用已掌握的编程语言,学习成本较低 |
| 社区生态 | 非常成熟,几乎覆盖所有云服务商 | 快速增长中,但规模小于 Terraform |
| 适用场景 | 运维团队主导的标准化基础设施管理 | 开发者主导的项目,需要复杂逻辑的场景 |
| AI 代码生成适配度 | 高(模式固定) | 很高(本质是通用编程语言代码) |
要点:IaC 工具中的 HCL 是一种典型的外部 DSL——它有独立的语法和解析器,专门用于声明式描述基础设施状态。而 Pulumi 则采用内部 DSL 的策略——用通用编程语言的语法来表达领域特定的概念。两者目标一致(将基础设施管理从手动操作转为代码驱动),路径不同(专用语言 vs 通用语言)。代码可以纳入 Git 版本控制、进行团队审查、自动化执行和回滚。
5. 胶水代码与 SDK 自动生成
5.1 什么是胶水代码
在软件工程中,胶水代码(Glue Code) 指的是本身不包含业务逻辑,仅用于连接两个系统或模块的代码。
典型的胶水代码包括:
- 前端调用后端 API 时编写的 HTTP 请求代码(URL 拼接、请求头设置、响应解析)
- 后端服务 A 调用服务 B 接口时编写的 HTTP 客户端代码
- 不同编程语言之间的接口适配代码
这类代码的特征是:高度重复、模式固定、但不可省略。
5.2 OpenAPI 规范与代码自动生成
既然胶水代码具有高度的模式化特征,工程界的解决方案是:先用标准格式描述 API 接口,再用工具自动生成客户端代码。
OpenAPI 规范(前身为 Swagger)是描述 REST API 的行业标准。它使用 YAML 或 JSON 格式,精确定义 API 的路径、参数、请求体和响应结构:
openapi: 3.0.0
info:
title: 邮件服务 API
version: 1.0.0
paths:
/emails:
post:
summary: 发送邮件
requestBody:
content:
application/json:
schema:
type: object
properties:
to:
type: string
example: "user@example.com"
subject:
type: string
body:
type: string
responses:
'200':
description: 发送成功基于这份规范文件,使用 openapi-generator 等工具可以自动生成多种语言的客户端 SDK:
- Python:
client.emails.send(to="user@example.com", subject="Hi", body="Hello") - TypeScript:
client.emails.send({ to: "user@example.com", subject: "Hi", body: "Hello" }) - Go:
client.Emails.Send(ctx, &SendEmailRequest{To: "user@example.com", ...})
生成的 SDK 封装了 HTTP 请求的所有细节,调用方无需关心 URL 路径、请求方法、序列化格式等底层实现。
5.3 重新理解 Armin 的案例
回到本文开头的案例,现在可以准确理解其中每个组成部分:
| 组成部分 | 性质 | 说明 |
|---|---|---|
| Go | 业务逻辑代码 | 邮件收发服务的核心功能实现 |
| YAML | 配置文件 | 服务配置、CI/CD 流水线定义、OpenAPI 规范文件 |
| Pulumi | 基础设施代码 | 用 Go/TypeScript 定义云资源(服务器、数据库、网络) |
| SDK 胶水代码 | 自动生成的客户端库 | 从 OpenAPI 规范自动生成的 Python 和 TypeScript SDK |
其中 YAML 配置、Pulumi 资源定义、SDK 胶水代码这三类均属于高度模式化、有明确规范约束的代码,这正是 AI 代码生成能力最强的领域。因此"4 万行代码中 90% 由 AI 生成"是合理的。
6. AI 与 DSL 的关系
6.1 AI 代码生成的适用性分析
| 特征维度 | 适合 AI 生成 | 不适合 AI 生成 |
|---|---|---|
| 模式化程度 | 高度重复,存在固定模板 | 需要创造性设计,无先例可循 |
| 规范约束 | 有明确的 schema 或语法规范 | 需求模糊,边界不清晰 |
| 上下文依赖 | 局部自洽,单个定义不依赖全局理解 | 需要理解整个系统的架构意图 |
| 可验证性 | 可被工具自动校验(如 terraform validate) | 只能依靠人工判断设计合理性 |
本文介绍的四类技术——配置文件、嵌入式脚本、IaC 代码、SDK 胶水代码——均具备左列的特征。这解释了为什么 AI 在这些领域的代码生成效果显著优于业务逻辑代码。
6.2 评估框架
在判断某段代码是否适合交给 AI 生成时,可以参考以下三个标准:
- 是否存在现成的规范或 schema? —— 存在则 AI 友好
- 是否属于大量重复的模式? —— 是则 AI 友好
- 生成结果能否被工具自动验证? —— 能则 AI 友好
三项均满足的代码(如从 OpenAPI 规范生成 SDK、用 Terraform 批量定义同构资源),可以高度依赖 AI 生成。三项均不满足的代码(如设计一个新的分布式一致性协议),仍需要工程师自行完成。
7. 术语表
| 术语 | 全称 / 中文 | 定义 |
|---|---|---|
| DSL | Domain-Specific Language / 领域特定语言 | 为特定领域设计的语言,与通用编程语言相对 |
| GPL | General-Purpose Language / 通用编程语言 | 可解决任意计算问题的编程语言,如 Python、Java、Go |
| 外部 DSL | External DSL | 拥有独立语法和解析器的领域特定语言,如 SQL、HCL、YAML |
| 内部 DSL | Internal DSL / Embedded DSL | 寄生在通用编程语言内部、利用宿主语法构建的领域专用表达,如 Pulumi |
| 数据序列化 | Data Serialization | 将内存中的数据结构转换为可存储或可传输的格式的过程 |
| INI | Initialization | 最早期的键值对配置格式,起源于 Windows 系统 |
| CSV | Comma-Separated Values / 逗号分隔值 | 用逗号分隔字段的纯文本表格格式 |
| XML | eXtensible Markup Language / 可扩展标记语言 | 基于标签的文本数据格式,表达力强但语法冗长 |
| JSON | JavaScript Object Notation | 基于键值对的轻量数据交换格式,Web API 的事实标准 |
| YAML | YAML Ain't Markup Language | 基于缩进的配置文件格式,后端和 DevOps 领域广泛使用 |
| TOML | Tom's Obvious Minimal Language | 显式语法的配置格式,Rust 和 Python 生态常用 |
| Protobuf | Protocol Buffers | Google 开发的二进制序列化格式,需预定义 Schema,体积小、速度快 |
| MessagePack | — | 类似 JSON 的二进制序列化格式,无需 Schema |
| Lua | — | 轻量级嵌入式脚本语言,常用于游戏引擎、Web 服务器和数据库扩展 |
| IaC | Infrastructure as Code / 基础设施即代码 | 用代码定义和管理云计算资源的工程实践 |
| Terraform | — | HashiCorp 开发的 IaC 工具,使用 HCL 声明式语言 |
| HCL | HashiCorp Configuration Language | Terraform 使用的专用配置语言 |
| Pulumi | — | 支持通用编程语言的 IaC 工具 |
| OpenAPI | — | 描述 REST API 接口的行业标准规范(前身为 Swagger) |
| SDK | Software Development Kit / 软件开发工具包 | 封装了 API 调用细节的客户端库 |
| 胶水代码 | Glue Code | 不含业务逻辑,仅用于连接两个系统的适配代码 |
总结
后端工程中存在大量非业务逻辑代码。它们有一个共同的上位概念:DSL(领域特定语言)——为特定领域设计的、与通用编程语言相对的语言。
本文介绍的 DSL 可以归为四个类别:
- 数据序列化格式(XML / JSON / YAML / TOML / CSV / Protobuf 等)—— 纯数据描述型外部 DSL,将结构化数据转换为可存储、可传输的形式
- 嵌入式脚本语言(Lua 等)—— 介于配置与通用语言之间,为宿主程序提供可编程的扩展能力
- 基础设施定义语言(HCL / Dockerfile 等)—— 声明式外部 DSL,描述系统期望状态;Pulumi 则以内部 DSL 的方式实现同一目标
- 接口描述语言与胶水代码生成(OpenAPI / .proto)—— 通过规范描述自动生成系统间的连接代码
理解 DSL 这一分类框架后,面对后端项目中各类"不像代码的代码"时,可以快速识别其性质:它属于哪类 DSL、解决什么领域的问题、为什么不用通用编程语言来写。
同时,由于 DSL 代码具有高度模式化、规范驱动、可自动验证的特征,它们也是当前 AI 代码生成技术最有效的应用领域。
