我为什么不再把环境写进代码里
我为什么不再把环境写进代码里
我以前很容易接受这样的代码:先把 localhost、端口、接口地址和测试数据写进去,确认功能跑通,再想着以后部署时替换。它在当下很省事,但也把下一阶段的麻烦提前埋进了源码。
现在我更愿意把问题拆成一句简单的约束:源码负责业务逻辑,环境负责变化。 只要一个值可能随着本地、测试、预发布和生产环境变化,它就不应该依赖搜索替换才能迁移。
“能跑”不等于“能交付”
Agent 很擅长让当前会话尽快得到一个可运行结果,所以它会自然地把当前环境当作世界本身:请求写成 localhost:3000,输出路径写成某个本机目录,mock 数据直接塞进组件,超时时间和功能开关散落在实现里。
这些做法未必会立刻出错。真正的问题是,代码从 demo 进入真实环境时,我需要重新打开每个文件,猜哪些值必须替换,还要担心遗漏一个隐蔽的地址或开关。交付的成本因此不在功能本身,而在环境差异。
我现在使用的分层
- 业务数据放在数据目录或 fixtures 中,和业务代码分开;
- 环境配置放在配置文件或环境变量中,例如端口、接口地址和超时时间;
- 敏感凭证放在
.env或密钥管理服务中,不进入版本库; - 源码只保留不随环境变化的业务逻辑和常量。
这不是为了追求目录形式上的漂亮,而是为了让“换环境”变成配置切换,而不是代码手术。
默认值也需要诚实
有时我们以为把默认值写在 os.getenv("PORT", "3000") 里已经足够灵活。但如果这个默认值其实只适用于本地开发,它仍然可能把错误配置悄悄带到别的环境。
我更倾向于把默认配置写入明确的 config/default.yaml,而对生产必需值在缺失时直接失败。临时 demo 可以硬编码,但要留下迁移目标和触发条件,而不是让临时代码伪装成长期设计。
这也是给 Agent 的开工约束
每次让 Agent 修改项目时,我会先说明:先进行数据、配置和凭证分层;不要把环境地址、路径、端口和 mock 数据写进业务实现;如果必须临时写死,要明确标记之后迁移到哪里。
这样做的价值不是让 Agent 写得更慢,而是把它的注意力从“这一次能不能跑”拉到“这份产物下一次能不能被接手、测试和发布”。
对我来说,代码与配置分离已经不只是工程规范,而是一种对未来变化负责的写法:让代码承载稳定的判断,让配置承载现实的差异。
