• AaronConlon.dev
  • Talk
  • Weekly
  • 工具
  • 编程
  • 翻译
  • 资讯
  • 通识

中大型 Web 项目必看:把 NODE_ENV 和 APP_ENV 彻底分开

2026年04月10上次更新于 1 天前
编程

大家好,今天想和大家讨论和分享一下我对 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 是 运行模式。

很多测试框架会强制设 NODE_ENV=test

当我们在项目里需要判定当前逻辑所处的运行模式是什么的时候,记住:在 Web 开发中, NODE_ENV 其实有着非常明确的约定:

值 含义
development 开发模式
production 生产模式

很多框架和库都会根据 NODE_ENV 自动设定一些行为,比如:

  • 是否输出详细错误信息
  • 是否开启调试日志
  • 是否启用性能优化
  • 是否使用缓存
  • 是否压缩资源

如果你把 NODE_ENV 改成:

NODE_ENV=uat
NODE_ENV=staging

这些库很可能识别不了,从而导致行为异常。

概念误区: 把 NODE_ENV 当环境名

我曾经把 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 + APP_ENV

在前端项目中,NODE_ENV 主要影响构建结果(Vite/Webpack);在后端(如 Node.js/NestJS/Express)中,更建议用 APP_ENV 控制业务配置。

变量 作用
NODE_ENV 控制程序运行模式
APP_ENV 控制部署环境

NODE_ENV:只保留给构建工具使用(vite/rollup 等),严格只有 development / production 两个值。

APP_ENV 或 DEPLOY_ENV:真正代表部署环境(dev/test/staging/prod)。

举个例子:一个电商系统的 Staging 环境

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 的规范

以下几点,我将之写入了自己所有项目的 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 👋🏻

not-by-ainot-by-ai
已读 0%
文章推荐
avatar
一发入魂:巧用 code-inspector-plugin 插件,一键点击直达源代码
2025-11-15 updated.
工具编程
avatar
从入门到放弃再到成功:一个前端程序员的后端 API 调试血泪史
2025-06-10 updated.
编程
GitHubTwitter / XMediumDevJuejin

Friends

Jimmy
Jimmy
老胡
老胡
Submara
Submara
Bruce Song
Bruce Song
Scarsu
Scarsu
宇阳
宇阳
Steven Lynn's Blog
Steven Lynn's Blog
OJ·Jimmy (Other Jimmy)
OJ·Jimmy (Other Jimmy)
liruifengv - Web 开发者,Astro 项目成员,开源爱好者。
liruifengv - Web 开发者,Astro 项目成员,开源爱好者。