DEV Community

Cover image for Angular 15: what happened to environment.ts
Gianpiero Errigo for This is Angular

Posted on • Edited on

Angular 15: what happened to environment.ts

TL;DR: Nothing!

Angular 15 simply doesn't ship anymore environment files by default.
You can still create them and configure their replacement based on build target as it was done automatically at project creation in previous versions.


A misunderstood purpose

The origin of the confusion, arising lately about the missing of these files in new project created with ng-cli at version 15, has its roots in the misconception that src/environments/environment.ts and src/environments/environment.prod.ts were some kind of sacred paths hardcoded in the deepest guts of Angular framework.
Reality is that they were just a convenience default choice, with no reference in codebase, and that could have been substituted by different paths and names with no harm to the application.

Original role

The only place in code where you could find them referenced at project creation was main.ts, generated with something like this:

...
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}
...
Enter fullscreen mode Exit fullscreen mode

Their environment.production property was used to check if the build just booted had to enable ProdMode or not.
This function was turning off a flag (yes, the flag is indeed isDevMode, and not isProdMode like one could expect) actually checked in Angular codebase to toggle some "debugging" features, most evident of which is the familiar message logged in console

Angular is running in development mode.
Call enableProdMode() to enable production mode.

When the flag is true, it means we are running a development stage of our app, so we want the framework to be more verbose in warnings and errors, and even to be a bit more "pedantic" checking situations that are not an error per-se, but that could lead to undesired and often erroneous behaviour in production.
Famous one is surely NG0100: Expression has changed after it was checked, responsible of verifying our data-binding follows a unidirectional flow, something that could bring problems during execution, but that will not throw errors on its own at runtime.

Originally Angular had no way to switch this flag other than putting the function in a file parsed at bootstrap.

Black magic of file replacement

People tended to accept the interpretation of the
correct file to load as faith, without questioning how the framework was capable of reading environment.ts or environment.prod.ts accordingly to the build target of choice.
The answer was nothing involving deep understanding of Angular inner mechanisms, but just the use of a nice feature offered by its builder, that while parsing configuration for chosen target, was instructed to take in account fileReplacements array, issuing the substitution defined in its objects.
This was default config for production build some versions ago:

"configurations": {
...
  "production": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.prod.ts"
      }
    ],
...
Enter fullscreen mode Exit fullscreen mode

Nothing was preventing us to add new replacements or change default one but, if the latter, we should have considered to modify our main.ts to check in the right file for the environment.production property it used to toggle production flag.

What changed in version 15

Last major release of Angular leverages a different system to toggle production flag: it lets optimization builder option, by default passed to production build target, set global NgDevMode to false, without having to parse any env file.

This change took off the only reason to have any environment file in the beginning, leading devs to get rid of them completely and obviously removing default fileReplacements occurrences in build targets configuration.

Why people are freaking out

Looking at what we read so far, it looks like this new approach is not something that should affect majority of Angular applications developers, being it more an "internal" of the framework.
Thing is that, being these files already generated at project creation and correctly managed on a build target basis, it became common practice using them to store a bunch of values that need to be switched between production and development build.
Usually these involved address of API servers to contact and Auth providers configurations.

Without finding the files where expected on newly generated projects, people who never had been interested in understanding how they ever worked didn't know where to put these data, unaware of the simplicity of manually reproducing the original setup.

Lazy solution

After the huge amount of complaints about this, Angular devs choose to "restore" on demand something similar to the old behaviour in 15.1 release.

So, from that version onwards, having environment files in place after creating a project is as simple as issuing

ng generate environments
Enter fullscreen mode Exit fullscreen mode

that as explained in docs will create them and configure build and server targets to use them.

In short: it will create src/environments/environment.ts and src/environments/environment.development.ts adding the latter as replacement of the former for build configuration's development target

"development": {
...
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.development.ts"
    }
  ]
Enter fullscreen mode Exit fullscreen mode

Why they didn't think of that before

The habit of putting environment variables inside those files is less straightforward than it could seem.
Even if their name and their use could lead to look at them as the ideal spot for such information, in real case scenario they're far from best solution.
Data like domains, endpoints, ports and alike are not bound only to building target, but often more tightly to deployment context.
That's why many people prefer to keep them outside of building process, and let the app evaluating them at runtime as token injected at deploying, maybe as environment variable of the hosting framework or docker bundling, read by a minimal server side process and exposed as API, or even passing them in dedicated configuration file inserted as assets by pipeline, as explained in this awesome article from good ol' @frederikprijck.


Now it should be clear there's been no change in environment variables management by Angular, just an upgrade that made an old easy convenient configuration not needed anymore, leading to reconsider what had always been an habit that often was a suboptimal solution, but that's available as it always has been.

Cheers.

Top comments (14)

Collapse
 
jdgamble555 profile image
Jonathan Gamble

Angular needs to get rid of this and add .env support out of the box IMHO. Globals are something else.

Collapse
 
gianpiero_errigo profile image
Gianpiero Errigo

Hi jonathan.
How would you like dotenv to be integrated natively in Angular?

Collapse
 
jdgamble555 profile image
Jonathan Gamble

Good question. I'm not sure, but it is in all other frameworks. It works with the Webpack version of Angular, but that is overkill just for env variables. At the very least, it should work natively with Universal.

Thread Thread
 
gianpiero_errigo profile image
Gianpiero Errigo

I got no experience with dotenv or Universal, so maybe I'm missing something basic:
what issue do you face if you try to prepone a script loading the .env, inside build script of your package manager?

Thread Thread
 
jdgamble555 profile image
Jonathan Gamble

It doesn't work. Here is just one article on how to hack it - indepth.dev/tutorials/angular/inje...

Thread Thread
 
gianpiero_errigo profile image
Gianpiero Errigo

Right.
Now I get it.
That really old ticket linked in the article suggests a lot of solutions, but looks like none of them has still been accepted and integrated into cli.

Collapse
 
frederikprijck profile image
Frederik Prijck

Nice article! Thanks for the mention! β™₯️

Collapse
 
gianpiero_errigo profile image
Gianpiero Errigo • Edited

Thanks to you.
Your post has been really useful.

Collapse
 
yogeshk05 profile image
YogeshK05

Really a good articleπŸ™Œ! One stop solution for all the information one might need about the topic.

Collapse
 
gianpiero_errigo profile image
Gianpiero Errigo

Thanks.
I'm glad it's been helpful for you.

Collapse
 
ptletski profile image
ptletski

It's strange how a "highly-opinionated" framework like Angular will suddenly punt on its "highly-opinionated" solution. Their solution provided convenience (as does their whole product). Why use their framework, if not for convenience? God knows it's hard enough to use as it is.

Collapse
 
gianpiero_errigo profile image
Gianpiero Errigo

Hi ptletski.
Not sure I get where's the ambiguity.
The choice of environment files in the beginning was forced by circumstances.
Once those were gone, they dropped default use of the files.
In this case, the whole setup was not due to opinion, but to necessity.

Collapse
 
julgon2012 profile image
Julio πŸ‘Ή

why does angular put us in the trouble of replacing environments when they were so easy to use? What a desire to annoy the user!
How do I know, in the application, what build mode I am in?

Collapse
 
gianpiero_errigo profile image
Gianpiero Errigo

Hi Julio.
None has been obliged to use a different system.
As explained in the article, old environment files replacement per-build works as usual, and since 15.2 they can be even generated on demand with a schematic.
The reason for them not being default anymore is the point of the whole article. :-)
About the last question, I'm not sure if I get it: why should you care about the build mode you're in?