从过程式、命令式到声明式:编程与运维的一次“权力转移”
Posted on Tue 27 January 2026 in Method
| Abstract | 从过程式、命令式到声明式编程和运维的转变 |
|---|---|
| Authors | Walter Fan |
| Category | 方法论 / 工程实践 |
| Status | v1.0 |
| Updated | 2026-01-27 |
| License | CC-BY-NC-ND 4.0 |
开篇:你以为你在“部署”,其实你在“追自己留下的脚印”
我见过最经典的一类线上事故,不是代码写错了,也不是机器挂了,而是四个字:“跟上次不一样”。
你上周用一串命令把服务推到线上:
kubectl set image deploy/api api=...:v12
kubectl scale deploy/api --replicas=6
kubectl patch deploy/api -p '...'
这周你想复现“上周那个稳定状态”,但你突然发现:
- 你记不清当时到底 patch 了哪些字段
- 同事在你不知情的情况下又改了几处(还挺合理)
- 线上对象已经长得跟你的记忆完全不一样
于是你开始在群里问:“谁动了我的 deployment?”
然后大家开始一起演一出“没有监控也能复盘”的职场魔幻剧。
这就是为什么我说:从过程式/命令式到声明式的转变,本质是一次“权力转移”——把“怎么做”的权力从人手里交给系统,让人只对“想要什么”负责。
一、三种范式,三种对“控制”的态度
先把概念说清楚,别上来就被术语劝退。
1) 过程式(Procedural):我亲自下场,一步一步来
过程式最像“写菜谱”:先切葱再下锅,火候几分钟,步骤是核心。
- 你关心的是 How:每一步怎么执行
- 你写的是流程:
if/for/while、函数调用顺序、状态如何变化
优点:清晰、可控、性能调优空间大
缺点:当系统变复杂、并发变多,“步骤”容易变成灾难现场
2) 命令式(Imperative):你照我说的做
命令式不一定写在代码里,也可能写在命令行、脚本、控制台里。
- 你关心的是 做什么动作:create / update / delete / scale
- 你通常在操作“现在的对象”(live state)
它的最大问题不是“不好用”,而是不可回放:
- 同样一串命令在不同初始状态下,结果可能不一样
- 命令执行的副作用(顺序、失败重试、部分成功)很难完全复盘
3) 声明式(Declarative):我只说我想要什么
声明式最像“点外卖”:你说要一份麻辣香锅,不必告诉商家先洗菜还是先开火。
- 你表达的是 What:目标状态/约束条件/不变量
- 系统负责 How:规划、调度、重试、收敛(reconcile)
这里有个关键提醒:
声明式不是“没有过程”,而是“过程不由你显式写出来”。
过程仍然存在,只是被下沉到框架/控制器/引擎里了。
二、为什么声明式越来越火:因为我们在管理“复杂系统”,不是单机程序
声明式流行,不是因为它更“高级”,而是因为它更适合几个现实约束。
1) 规模化:人类不擅长记住 200 个细节
当资源少、环境简单时,命令式挺爽:改一行、重启一下就好了。
但当你要管的是:
- 多集群、多环境(dev/staging/prod)
- 多租户、多团队协作
- 还要合规、要审计、要可追溯
命令式就会把人逼成“记忆型动物”:靠脑子记、靠聊天记录找。
2) 幂等与可重复:声明式天然更接近“可重放”
声明式通常要求你描述一个收敛目标,系统可以反复执行直到达成。
这带来两件工程上的好处:
- 幂等:同一个声明反复 apply,结果应该一致
- 可复制:你可以把目标状态放进版本库,让环境可重建
3) 协作与审计:Git 比微信群靠谱
现实里最可靠的“变更记录”,不是谁在群里说了一句“我改了下”,而是:
- 有 diff
- 有 commit message
- 有 reviewer
- 有回滚点
声明式天然适合和 Git 绑定,这也是 GitOps 能跑起来的原因。
三、运维世界的转变:从“我手动敲命令”到“系统帮我收敛”
运维的演进其实很像软件工程的演进:从“个人英雄主义”走向“系统化协作”。
1) 命令式运维:手工命令 + 脚本(最常见,也最容易背锅)
早期的典型画面:
- SSH 上去改配置、重启服务
- 写个脚本跑一遍,希望别炸
- 炸了就再 SSH 上去修
这套方式的问题在于:操作和结果绑定得太紧,而环境差异、顺序差异会把你拖进泥潭。
2) IaC(基础设施即代码):把“基础设施”变成可版本化的声明
Terraform 是一个典型代表:你写配置描述目标基础设施,工具负责 plan/apply,并通过状态(state)管理“现实长什么样”。官方文档里对 Terraform 的定位就是“infrastructure as code”。
你把它看成一句话就行:
把基础设施从“手工操作对象”,升级成“可版本化的目标状态”。
Terraform 文档入口(可扩展阅读):https://developer.hashicorp.com/terraform/docs
3) GitOps:把“部署动作”变成“状态同步”
当你把目标状态放在 Git 里时,“部署”就不再是 CI 里跑一堆命令,而更像:
- Git 是事实来源(source of truth)
- 集群里有个控制器持续对比:现实 vs 目标
- 不一致就收敛(同步),一致就躺平(保持)
Argo CD 的官方文档里就明确把它定义为“declarative, GitOps continuous delivery”。
文档入口:https://argo-cd.readthedocs.io/en/stable/
这类系统最关键的变化是:你不再“推送”变更到集群,而是让集群“拉取并对齐”到 Git 的目标状态。
四、Kubernetes 是声明式运维的“教科书”
K8s 的核心设计就是“声明式 + 控制器收敛”。你写一个 Deployment,真正有用的不是 YAML 的格式,而是这句话:
你告诉它你想要的副本数、镜像版本、策略;它负责把系统状态收敛到你想要的样子。
Kubernetes 官方文档把对象管理分成三类:命令式、命令式配置、声明式配置,其中声明式推荐用 kubectl apply 管理目录级配置。
参考:https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/
声明式的“进阶版”:Server-Side Apply(SSA)
当多个系统/人都在“声明式地”改一个对象时,就会出现一个现实问题:谁说了算?
SSA 的思路是:让 apiserver 记录“字段归属”(谁管理哪个 field),冲突就显式报出来,而不是默默互相覆盖。
参考:https://kubernetes.io/docs/reference/using-api/server-side-apply/
这背后其实是在解决一个更大的问题:声明式协作的边界。
五、从编程视角看:声明式不是“新语言”,而是一种抽象层级
声明式并不只存在于运维。
1) SQL:你说“要什么数据”,优化器决定“怎么查”
SQL 经常被拿来当声明式的例子:你写 SELECT ... WHERE ...,并不指定索引怎么用、join 顺序怎么排。
PostgreSQL 文档入口:https://www.postgresql.org/docs/15/sql.html
2) UI 世界:你描述“界面应该长什么样”,框架负责更新
React 这类框架的心智模型也是声明式:你描述 UI = f(state)。
你不需要手动做 DOM diff(那是框架的事),你只负责“目标长相”。
3) 但别神化:声明式的代价是真实存在的
声明式把“怎么做”下沉给系统,代价就是:
- 你要理解系统的收敛机制(reconcile loop)
- 你要接受一定的“黑盒感”
- 排错时得学会观察“目标/现实/差异”三件套
六、怎么落地:把声明式用成“省心”,而不是用成“更大的 YAML 地狱”
我自己总结的落地顺序是:先定边界,再定事实来源,再定收敛方式。
1) 先问一句:哪些东西适合声明式?
适合声明式的通常是:
- 资源类:机器、网络、权限、部署对象(可描述的目标状态)
- 可收敛:系统能通过重试/调度把它对齐
不太适合直接声明式化的通常是:
- 需要复杂流程编排且强顺序依赖的(当然也能做,但成本更高)
- 本质是一次性动作/事务的(比如“把这批数据迁移完”)
2) 让 Git 成为“事实来源”,别让 Slack/微信群当数据库
- 目标状态进 Git
- 变更走 PR
- 重要环境的 apply/sync 尽量自动化
3) 给“收敛”留出口:观察、回滚、保护阈值
声明式系统最怕两件事:
- 漂移(drift):现实被手工改了,目标没更新
- 自动收敛误伤:目标写错了,系统很勤奋地把你拉进坑里
所以你需要配套:
- diff(变更预览)
- rollback(回滚路径)
- guardrail(例如审批、保护分支、变更窗口、限速)
七、常见坑:声明式不是银弹,它只会把坑换个形状
- 把命令式当声明式:明明每次都
kubectl patch,还以为自己在 GitOps - 一半声明式一半手工改:最后一定会遇到“到底谁是事实来源”的战争
- 状态文件/字段归属没管好:Terraform state、K8s managedFields 这类东西,都是“系统记忆”,丢了就会失忆
- 把一切都 YAML 化:复杂逻辑硬塞 YAML,往往是“用错抽象层级”
TL;DR(给只想快速抓重点的人)
- 过程式:我写步骤(How)
- 命令式:我发命令(Do)
- 声明式:我描述目标(What),系统负责收敛(Reconcile)
- 声明式流行不是审美,而是复杂系统时代对 幂等、协作、审计、可重复 的刚需
可执行清单(Checklist)
- [ ] 先划清边界:哪些资源/配置要声明式管理,哪些保留流程化
- [ ] 选定事实来源:Git 是唯一目标状态(至少对 prod 如此)
- [ ] 设计收敛机制:谁负责对齐(人、CI、控制器、GitOps 工具)
- [ ] 给变更加护栏:diff、审批、回滚、限速
- [ ] 规定“禁止手工改”的范围(或明确“手工改必回填”)
- [ ] 监控漂移:现实与目标不一致要能被发现、被告警
互动提问
你现在团队里最“命令式”的一块运维工作是什么?是 Kubernetes 的 kubectl patch/scale,还是云资源的控制台点点点?我可以基于你的场景,给一条更具体的迁移路线(先收敛哪几类资源最划算、怎么避免一上来就掉进 YAML 地狱)。
Reference
- Kubernetes: Declarative object configuration:
https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/ - Kubernetes: Server-Side Apply:
https://kubernetes.io/docs/reference/using-api/server-side-apply/ - Terraform docs:
https://developer.hashicorp.com/terraform/docs - Argo CD docs:
https://argo-cd.readthedocs.io/en/stable/ - PostgreSQL: The SQL Language:
https://www.postgresql.org/docs/15/sql.html
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。