把复制粘贴的代码折成模块
一句话总结
如果同样的代码复制了三次,这就是它应该变成模块的信号。 不过,过早地把它做成模块,比复制粘贴更糟。
为什么需要它
有三个环境,就会有三个目录。
dev/main.tf (300줄)
stage/main.tf (300줄, dev 와 거의 같음)
prod/main.tf (300줄, 인스턴스 크기만 다름)
要修改一条安全组规则,就得改三处,而且一定会漏掉一处。漏掉的那一处,往往就是 prod。
模块的作用
模块是接收输入并创建一组资源的函数。
module "web" {
source = "../modules/web-service"
name = "api"
instance_size = "large" # 환경마다 다른 것만 인자로
replicas = 6
}
三个环境使用同一个模块,只把不同的值作为参数传入。修改规则时,只需修改模块这一处。
好模块的条件
1. 边界自然 “一个 Web 服务所需要的全部内容”是好的边界。“我们公司的所有基础设施”是坏的边界。模块是复用的单位,不是打包的单位。
2. 输入少 有 30 个参数的模块不是模块,而是配置文件。参数增多,说明边界划错了。
3. 输出明确 把其他模块要引用的值(ID、端点)作为输出暴露出来。如果外部还必须知道内部实现,说明封装失败了。
4. 有版本 修改模块,会影响所有使用它的地方。给它加上版本,就能让不同环境使用不同时间点的模块——先在 dev 中试用新版本,prod 以后再升级。
过早的抽象
把三处使用的代码做成了模块,很快出现第四个使用方,于是“只有这种情况不同”就开始了。参数一个个增加,条件语句加了进来,最终变成没有人能看懂的模块。
经验法则如下。
- 重复 2 次:先放着。还不知道模式。
- 重复 3 次:如果共同点很明确,就做成模块。
- 条件增多:把模块拆开,或者让那个使用方不再使用模块。
重复是看得见、也改得了的,而错误的抽象既看不见,也很难改。没有把握时,保持重复反而更好。
在现场相遇的样子
- 模块参数有 40 个 → 边界划错了,必须拆分。
- 模块里堆积了
count = var.is_prod ? 3 : 1这样的环境分支 → 本该作为参数接收的值,模块却在自己做判断。 - 修改模块后,无关的环境坏掉了 → 没有固定版本。
下一项实验要做什么
在接下来的三项实验中,用 OpenTofu 亲自处理漂移和模块。用 plan -refresh-only 读取手工改过的文件,在吸收、回退和排除中做出选择;用 removed 块整理两份状态争夺同一个资源的情形,再用 import 导入现有的值;用 moved 把复制粘贴的两个环境在不替换的情况下改为模块调用,并用 git 标签固定版本。在最后的测验中,判断稳定的边界、版本固定和调用方的责任。