
大家好,今天想和大家讨论和分享一下我对 web 项目里 NODE_ENV 环境变量的看法。
在很多 web 项目里,环境变量的设计往往是一个“看起来简单,但实际非常容易踩坑”的问题。
最典型的就是:
NODE_ENV 到底应该怎么用?
dev / test / staging / prod 要不要塞进 NODE_ENV?
这几年来我经历过各种大小草台班子,接手过各种 web 项目,看到很多团队图省事,会把所有环境都塞进 NODE_ENV,比如:
NODE_ENV=dev
NODE_ENV=test
NODE_ENV=uat
NODE_ENV=staging
NODE_ENV=prod
这种方式前期看似乎没问题,但随着项目变复杂,需求、环境变多,参与人数变多等等,这种设计基本都会逐渐变成隐患。
今天分享这篇文章,主要想和大家分享一下,我们得搞清楚一下几点:
NODE_ENV 的本质是什么?
为什么不能把它当环境枚举
运行时变量应该如何设计
推荐的实践方案
NODE_ENV 是 运行模式。
很多测试框架会强制设 NODE_ENV=test
当我们在项目里需要判定当前逻辑所处的运行模式是什么的时候,记住:在 Web 开发中, NODE_ENV 其实有着非常明确的约定:
很多框架和库都会根据 NODE_ENV 自动设定一些行为,比如:
是否输出详细错误信息
是否开启调试日志
是否启用性能优化
是否使用缓存
是否压缩资源
如果你把 NODE_ENV 改成:
NODE_ENV=uat
NODE_ENV=staging
这些库很可能识别不了,从而导致行为异常。
我曾经把 NODE_ENV 当成”当前代码运行在哪一个环节“的判定依据:
if (process.env.NODE_ENV === 'dev') {}
if (process.env.NODE_ENV === 'uat') {}
if (process.env.NODE_ENV === 'staging') {}
这里其实混淆了两个基本概念:
运行模式 - mode
是否开启调试:development 开启,production 关闭
是否压缩日志:development 关闭,production 开启
部署环境 - environment
dev 开发环境
test 测试环境
prod 生产环境
或许我们不应该用同一个变量来表示这两种概念。
对吧?运行模式的关注点在于代码如何运行,我们根据这一点来控制是否调试、是否输出细节,启用 source map,运行模式会影响打包结果,并且应该进入构建产物内部,我不在乎这份代码构建后在什么环境下运行。
然而部署环境就不一样了,它的核心关注点是:代码运行在哪里?
举个例子,假设代码运行在 test 环境下,我们正在对代码进行测试,难道这时候你想要让类似 Sentry 这样的第三方服务接入进来吗?
当然不需要!就算是免费的服务,也不需要在测试的时候接进来啊,白嫖也不行!
我们需要用一个变量来标识部署的环境,也可以说是运行的环境。
但现实是很多团队、项目会一把梭 NODE_ENV,某种意义上来说,这个变量传播最早也最广,大家习惯了用它控制一切。
并且,一个变量就能决定“开发时友好,生产时高效”,确实方便。
但我们不能忽略这样做的代价:
语义不清晰:NODE_ENV=production 时,到底是“打包成生产模式”还是“当前部署在生产环境”?经常说不清。
无法灵活组合:我想在 test 环境也用 production mode(为了测性能、测构建产物),或者在 prod 环境临时开启 development mode 调试问题,就很尴尬。
配置爆炸:很多人最后搞出 development, test, staging, production 四种模式,却又在里面嵌套各种 feature flags,配置越来越乱。
在前端项目中,NODE_ENV 主要影响构建结果(Vite/Webpack);在后端(如 Node.js/NestJS/Express)中,更建议用 APP_ENV 控制业务配置。
NODE_ENV:只保留给构建工具使用(vite/rollup 等),严格只有 development / production 两个值。
APP_ENV 或 DEPLOY_ENV:真正代表部署环境(dev/test/staging/prod)。
Staging 环境的定位:
目的是在上线前做最接近真实生产的完整测试(包括性能、功能、集成)。
通常由 QA、产品、运维一起验证,模拟真实用户流量。
核心需求:行为要和生产环境几乎一致,但数据和外部服务是隔离的。
如果不分开(只用一个变量,比如 NODE_ENV 或 ENV),可能有人会这样写:
# 很多人会这么写
ENV=staging
# 或者
NODE_ENV=staging
然后代码里这样判断:
if (process.env.NODE_ENV === 'production') {
// 生产行为:压缩、关闭调试、无彩色日志等
} else {
// 开发行为:详细日志
// source map、React DevTools 等
}
在 staging 环境下会出现以下问题:
想用 production mode 的优化,但环境变量是 staging。
结果是用了 development 模式:代码没压缩、日志超级详细、source map 没去掉、性能比生产差很多,无法准确验证性能和稳
定性。
想用 development mode 方便调试,但又怕污染生产行为。
如果强行设 NODE_ENV=development,那整个 staging 就失去了“类生产”的意义,测出来的结果不可信。
配置冲突:数据库要用 staging 专属的库(不能连 prod DB)
支付、短信、推送等第三方服务要用测试账号,但日志、监控、告警又希望和生产保持一致(格式、级别、压缩方式)
最终结果:要么 staging 测不准,要么调试很便秘,花费的时间大大增加,然后?导致加班 ?🤣
现在就以这个为例,我们试试看拆开成两个变量:
# 运行模式(决定代码如何运行、打包结果)
NODE_ENV=production
# 或 APP_MODE=production
# 部署环境(决定连哪个数据库、用什么密钥、开哪些功能)
# dev | test | staging | prod
APP_ENV=staging
其他场景下,NODE_ENV 基本都设置为 production!部署环境则根据 test、staging、prod 来切换逻辑。
在这个 Staging 环境下,运行模式是 production,构建后:
代码是压缩的、tree-shaking 后的
肯定没有 source map
日志是结构化的,压缩的,不至于打印多余的彩色日志,详细堆栈也不会暴露
框架的开发警告全部关闭
性能和生产环境一致
同时,部署环境是 staging:
根据部署环境连接 staging 数据库(当然也可以在 CI 的时候专门注入数据库链接)
测试支付渠道走的是沙箱
独立的 staging 域名、监控 Tag、开放的功能
代码示例:
const mode = process.env.NODE_ENV; // production
const env = process.env.APP_ENV; // staging
// 构建/运行时行为
const isProdMode = mode === 'production';
// 业务和基础设施配置
const config = {
db: {
host: env === 'prod' ? 'prod-db' : `${env}-db`,
},
logging: {
level: isProdMode ? 'info' : 'debug',
pretty: !isProdMode,
},
features: featureFlags[env], // staging 可以有独立的 feature flags
};
甚至配置文件也可以按 APP_ENV 拆分:
.env.local
.env.dev
.env.test
.env.staging
.env.prod
这样一来我们就可以去做一次性构建,多环境部署。
在 CI/CD 的过程中,只需要构建一次代码,再部署到多个环境即可。多环境下多次重复构建就显得有点浪费时间了。
以下几点,我将之写入了自己所有项目的 README 中
NODE_ENV 只允许:development / test / production
APP_ENV 用于区分部署环境:local / dev / test / staging / prod
除本地开发和自动化测试外,所有服务运行在 NODE_ENV=production
禁止使用 NODE_ENV 判断 dev/test/staging/prod
所有环境差异统一通过 APP_ENV + 配置文件管理
如果你正在做一个中大型后端系统,这一条设计,值得你在一开始就定对。否则后面重构成本会非常高。
如果你已经在用 NODE_ENV=dev/staging/prod,也可以逐步引入 APP_ENV,把职责拆清楚,就能平滑过渡。
最近 AI 的发展实在是太快了,很多人已经完全不再编写代码,Vibe coding 堪比许愿,在 Agent 帮助下,连自己review 都省了。
但是,我还是觉得 AI 只是拓展开发者能力的工具,至少目前还是!我们可以借助 AI 来做很多原本自身做不到的事情,或者需要花很多时间精力才能做到的事情。但是在开发这件事情上,一切的根基还是我们自己。
感谢大家看到这里,如果你喜欢我分享的内容,欢迎给我三连支持,你的支持是我更新下去的动力!
下次见,Bye 👋🏻
