Let’s imagine you’re on a large project. You have the following limitation - ES5 Javascript.
Now, what happens in large codebases with 20k+ lines? They will end up with files called config.js
and contact_info.js
and environments.js
, etc. Reference files that are frequently used.
In my case, our project has some code that (for example), shouldn’t run in production, but it should run in a development environment.
We had a file that looked like this:
const ENVs = {
// Arrays of strings. We use the ENV=[environment] condition when running scripts.
// E.G. our CI will run ENV=test node script.js
is_dev: ['development', 'dev_server_2'],
is_prod: ['production'],
is_test: ['test', 'aws_test'],
is_aws: ['aws_test'],
};
module.exports = ENVs;
So here’s how we typically would use this. Let’s say we don’t want a certain part of code to run if we’re in the production
environment (a pretty common need).
Using Node.js to get the ENV
environmental variable, we would check like this:
const environment = require('./environment.js')
const env = process.env.ENV; // Get the ENV environmental variable using Node
if (environment.is_prod.indexOf(env) > -1) {
return; // end the script
}
// one liner. this is what ends up happening in real life
if (environment.is_prod.indexOf(process.env.ENV) > -1) return;
If you’re saying dude, just use .includes()
- remember, ES5!
Okay, so let’s say we have an urge to make this more readable across the codebase. What’s one way to solve this?
Why, sure, let’s introduce some more overhead to this process! :)
Let’s modify that object we worked with earlier.
const ENVs = {
dev: ['development', 'dev_server_2'],
prod: ['production'],
test: ['test', 'aws_test'],
aws: ['aws_test'],
get is() {
// Utility lookup functions added here
}
};
We’ve added a getter to the object called is
. is
is an object that will contain pre-computed responses about the environment we’re working in.
In the end, we want to be able to make a call like this: environment.is.dev
. The logic for a self-aware object is as follows:
get is() {
return Object.keys(this).filter(k => k !== 'is').reduce((accum, key) => {
accum[key] = this[key].indexOf(process.env.ENV) > -1;
return accum;
}, {})
}
The whole thing ends up looking like this.
const ENVs = {
dev: ['development', 'dev_server_2'],
prod: ['production'],
test: ['test', 'aws_test'],
aws: ['aws_test'],
get is() {
return Object.keys(this).filter(k => k !== 'is').reduce((accum, key) => {
accum[key] = this[key].indexOf(process.env.ENV) > -1;
return accum;
}, {})
}
};
module.exports = ENVs;
Our developers can now do something easy when they’re trying to determine where the hell their code is running.
const env = require('./environment.js')
if (env.is.prod) {
return; // don't run in prod!!!
}
if (env.is.dev || env.is.test) {
// do dev or test stuff
}
This sort of self-aware object makes working with constant data much simpler. If you know a cleaner way to do this, email me :) I’ll give you a prize. The prize is me sending you an email back btw.