Skip to content

Instantly share code, notes, and snippets.

@markerikson
Last active January 9, 2023 15:24
Show Gist options
  • Save markerikson/6776848172c33aaa4db882627c689e18 to your computer and use it in GitHub Desktop.
Save markerikson/6776848172c33aaa4db882627c689e18 to your computer and use it in GitHub Desktop.
Summary of process.env.NODE_ENV and its use with Webpack

[02:06 PM] acemarke: @Steven : a couple other thoughts on the whole NODE_ENV thing. First, per my comments, it really is a Node concept. It's a system environment variable that Node exposes to your application, and apparently the Express web server library popularized using its value to determine whether to do optimizations or not
[02:08 PM] acemarke: Second, because of its use within the Node ecosystem, web-focused libraries also started using it to determine whether to they were being run in a "development" environment vs a "production" environment, with corresponding optimizations. For example, React uses that as the equivalent of a C #ifdef to act as conditional checking for debug logging and perf tracking. If process.env.NODE_ENV is set to "production", all those if clauses will evaluate to false.
Third, in conjunction with a tool like UglifyJS that does minification and removal of dead code blocks, a clause that is surrounded with if(process.env.NODE_ENV !== "development") will be dead code and stripped out, reducing bundled code size and execution time
However: because the NODE_ENV environment variable and the corresponding process.env.NODE_ENV runtime field are strictly Node-specific concepts, by default that value does not exist in client-side code
which is where things like the Webpack DefinePlugin come in. Since Webpack is doing transformation of your code anyway, it can force the existence of global values such as process.env.NODE_ENV.
[02:14 PM] acemarke: But, here's the key: it could set that value to anything, based on any condition you want, as you are generating your Webpack config. For example, I could have a webpack.production.config.js Webpack config file that always uses DefinePlugin to set that to "production". It wouldn't have to be checking the actual current value of the "real" process.env.NODE_ENV variable while generating the Webpack config, because I would know that any time I'm doing a "production" build, I would want to set that value in the client code to "production'.
But, this is where the "code I'm running as part of my build process" and "code I'm outputting from my build process" worlds come together
[02:17 PM] acemarke: Because your build script is itself most likely to be Javascript code running under Node, it's going to have process.env.NODE_ENV available to it as it runs. Because so many tools and libraries already share the convention of using that field's value to determine their dev-vs-production status, the common convention is to use the current value of that field inside the build script as it's running to also determine the value of that field as applied to the client code being transformed
hopefully that clears things up slightly
I do want to address a few of your thoughts
first, while obviously everyone has had different paths in their programming careers and learning experiences, I would guess that "many" React devs are coming to it from a Node background, and would already have at least a passing familiarity with some of its concepts
and at least partly because of that, most Webpack tutorials jump straight into "here's how you set up your Webpack config", and don't talk about the Node-oriented aspects
I will say that the relationship between the server-side "value of NODE_ENV in your build script" and the client-side "value of NODE_ENV as applied to your code" is not immediately made clear by most articles I've glanced at
on the flip side, simply searching for node_env turns up discussions like http://stackoverflow.com/questions/16978256/what-is-node-env-in-express and http://apmblog.dynatrace.com/2015/07/22/the-drastic-effects-of-omitting-node_env-in-your-express-js-applications/
which at least give a fairly decent description of what it is
[02:25 PM] acemarke: Ultimately, this all comes down to a few key points:

  1. NODE_ENV is a system environment variable that Node exposes into running scripts
  2. It's used by convention to determine dev-vs-prod behavior, by both server tools, build scripts, and client-side libraries
  3. It's commonly used inside of Webpack config generation as both an input value and an output value, but the tie between the two is still just convention
  4. It's not a Webpack-specific concept, and it's not up to the Webpack team to document it
    all that said, I will agree that I'm not seeing articles that specifically tie all that information together

[05:07 PM] acemarke: yeah, per my comment today, the key for optimizing numerous client-side libs is to use DefinePlugin to specify process.env.NODE_ENV as "production" when the client-side code is transformed, and then UglifyJS will zap the now-dead code branches
[05:08 PM] Steven: are you talking about tree shaking?
[05:08 PM] acemarke: but then there's the usual assumption that the client-side version of that value is coming from whatever it is inside of the build script
not quite
[05:08 PM] Steven: i know that's the new hotness in webpack 2
[05:09 PM] acemarke: so let's say that inside of a React file, you have:

function implementSomeReactBehavior() {  
    // do actual work part 1  
  
    if(process.env.NODE_ENV !== "production") {  
        // do debug-only work, like recording perf stats  
    }  
  
    // do actual work part 2  
}  

like, in React itself
[05:09 PM] Steven: yeah
[05:10 PM] acemarke: so the standard unminified file has that if branch in there, and will happily run it, including the (possibly expensive) debug logging, perf recording, whatever
when you use DefinePlugin to set process.env.NODE_ENV to "production", that if statement is now always false
in fact, I think it actually replaces the verbatim variable name entirely
so that the statement becomes if("production" !== "production")
so now you have a flat-out dead branch of code
and when UglifyJS analyzes the code, it says "oh hey, dead branch, I can wipe that out entirely"
[05:11 PM] Steven: yeah i understand the concept. i wrote a framework for Flash called Gaia years ago and I had optimization options to comment out chunks of code for features you weren't using to reduce the footprint of the framework core.
for some reason, i thought that babel's transpilation from ES2015 to ES5 did a JS minification already. i didn't know you had to explicitly add UglifyJS plugin into the chain for production.
[05:14 PM] acemarke: yeah. Babel transpiles code, Webpack transforms code, Uglify transforms code.... they just do different parts of it :)
technically, I think calling webpack -p from the commandline specifies use of UglifyPlugin, but most of the time a real-world config specifies it manually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment