TLDR: don’t use env vars in class constructors.
Seeing environment variables in a class constructor is a code smell and an
anti-pattern.
For example in TypeScript for Node.js, you might see something like this:
Anti-pattern:
class Foobar {
private niceInjectableDependency: IDependency;
private importantConfig: string;
constructor(niceInjectableDependency: IDependency) {
this.niceInjectableDependency = niceInjectableDependency;
this.importantConfig = process.env.IMPORTANT_CONFIG; // code smell
}
}
This might seem reasonable, as we’re still kind of able to inject the
importantConfig into the class, just not via the constructor. Unfortunately
this injectability is nowhere near as good as the usual kind with function call
arguments.
The reason using env vars in the constructor is an anti-pattern is that we’ve
now created a dependency on global state. Env vars are global state (as is
the current date and time, but that’s another discussion).
Because env vars are global state, having functions reach out and grab env vars
behind the scenes will cause the same problems that any unmanaged global state
will cause.
We can’t see that this class has a hidden dependency on global state when we see
new Foobar() in the code. We have to know about this and make allowances to
work around it if necessary.
This class is now harder to test. You can set values on process.env in
Node.js, but again it’s global, and it will make your tests difficult to
parallelise.
We also can’t choose to have this class behave differently at runtime, which is
what config is for, after all. Maybe we want to be able to configure the
importantConfig to be something different, but we can only do it globally
now. Do we have to run separate processes for each different configuration we
want? That would be difficult to manage and tricky to debug, and could explode
into a large number of processes if we have several of these global env var
dependencies across different classes.
We might want to run several instances of this class with different
configurations. Perhaps this class has a nice implementation of publishing a
message to a queue, but when we have more queues to deal with later, we find we
can’t just instantiate more instances of this class with each configured to
publish to a different queue.
A factory pattern would be nice for this class, but again we’re not able to use
the factory pattern here because of the uncontrollable global state at run time.
In short, don’t use env vars in class constructors.
Config is a dependency like any other, and should be injected into the class
constructor. A factory pattern can handle this in a clear and maintainable way.
Other set up code that is responsible for configuration might have the option of
taking it from env vars, but it shouldn’t be hard-coded into classes whose
responsibility is not about env vars.
This is related to the more general
“Constructor does Real Work”
flaw, which is worth reading about.
View post:
Environment variables in the constructor are a code smell
|