<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marko Colia</title>
    <description>The latest articles on DEV Community by Marko Colia (@marko_colia_1f52ad928cb92).</description>
    <link>https://dev.to/marko_colia_1f52ad928cb92</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4154161%2F23e640b1-fb9d-406b-8459-6905bd943553.png</url>
      <title>DEV Community: Marko Colia</title>
      <link>https://dev.to/marko_colia_1f52ad928cb92</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marko_colia_1f52ad928cb92"/>
    <language>en</language>
    <item>
      <title>How to manage environment variables across development, staging and production</title>
      <dc:creator>Marko Colia</dc:creator>
      <pubDate>Thu, 01 Oct 2026 08:44:43 +0000</pubDate>
      <link>https://dev.to/marko_colia_1f52ad928cb92/how-to-manage-environment-variables-across-development-staging-and-production-238o</link>
      <guid>https://dev.to/marko_colia_1f52ad928cb92/how-to-manage-environment-variables-across-development-staging-and-production-238o</guid>
      <description>&lt;h1&gt;
  
  
  How to Manage Environment Variables Across Development, Staging, and Production
&lt;/h1&gt;

&lt;p&gt;Environment variables are one of those things that look simple at the beginning of a project and become increasingly difficult to manage as the project grows.&lt;/p&gt;

&lt;p&gt;At first, you might have a single &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://localhost:5432/myapp
JWT_SECRET=my-secret
API_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works perfectly for local development.&lt;/p&gt;

&lt;p&gt;Then you deploy the application.&lt;/p&gt;

&lt;p&gt;Now you have production variables.&lt;/p&gt;

&lt;p&gt;A few weeks later, you add a staging environment.&lt;/p&gt;

&lt;p&gt;Then another developer joins the project.&lt;/p&gt;

&lt;p&gt;Then you need different API keys, database URLs, feature flags, email providers, storage credentials, analytics tokens, and third-party integrations depending on the environment.&lt;/p&gt;

&lt;p&gt;Suddenly, environment variables are no longer just a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;They become part of your application's configuration strategy.&lt;/p&gt;

&lt;p&gt;In this article, we'll look at how to manage environment variables across &lt;strong&gt;development, staging, and production&lt;/strong&gt;, what commonly goes wrong, and some practical ways to make the workflow more maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What are environment variables?
&lt;/h2&gt;

&lt;p&gt;Environment variables are key-value pairs used to configure an application without hardcoding configuration directly into the source code.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
NODE_ENV=development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application can then read those values at runtime.&lt;/p&gt;

&lt;p&gt;In Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation is useful because the same application code can run in different environments with different configuration values.&lt;/p&gt;

&lt;p&gt;The code stays the same.&lt;/p&gt;

&lt;p&gt;The configuration changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why do we use environment variables?
&lt;/h2&gt;

&lt;p&gt;Imagine hardcoding your database connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://localhost:5432/myapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might work locally, but your production database will obviously use another URL.&lt;/p&gt;

&lt;p&gt;You could technically write something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://production-server/myapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://localhost:5432/myapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this quickly becomes difficult to maintain.&lt;/p&gt;

&lt;p&gt;It also creates a much bigger problem when credentials are involved.&lt;/p&gt;

&lt;p&gt;You should never hardcode secrets such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API keys
database passwords
JWT secrets
OAuth credentials
SMTP passwords
cloud credentials
payment provider keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside your source code.&lt;/p&gt;

&lt;p&gt;Environment variables allow configuration and secrets to stay separate from your application logic.&lt;/p&gt;




&lt;h1&gt;
  
  
  The three common environments
&lt;/h1&gt;

&lt;p&gt;Most modern applications have at least three environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development&lt;/li&gt;
&lt;li&gt;Staging&lt;/li&gt;
&lt;li&gt;Production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each has a different purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;Development is where developers build and test the application locally.&lt;/p&gt;

&lt;p&gt;Typical values might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=development

API_URL=http://localhost:3000

DATABASE_URL=postgresql://localhost:5432/myapp_dev

LOG_LEVEL=debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The development environment usually prioritizes convenience and debugging.&lt;/p&gt;

&lt;p&gt;For example, you might enable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG=true
LOG_LEVEL=debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while disabling certain production-only integrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Staging
&lt;/h2&gt;

&lt;p&gt;Staging is an environment designed to behave as closely as possible to production without actually affecting production users.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=staging

API_URL=https://staging-api.example.com

DATABASE_URL=postgresql://staging-db/myapp

LOG_LEVEL=info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Staging is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;integration testing&lt;/li&gt;
&lt;li&gt;QA&lt;/li&gt;
&lt;li&gt;testing deployment pipelines&lt;/li&gt;
&lt;li&gt;testing third-party integrations&lt;/li&gt;
&lt;li&gt;validating migrations&lt;/li&gt;
&lt;li&gt;verifying production-like configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideally, the staging environment should be structurally similar to production.&lt;/p&gt;

&lt;p&gt;The actual credentials and resources should still be different.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production
&lt;/h2&gt;

&lt;p&gt;Production is the environment used by real users.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=production

API_URL=https://api.example.com

DATABASE_URL=postgresql://production-db/myapp

LOG_LEVEL=warn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production configuration should receive the most protection because it often contains sensitive credentials.&lt;/p&gt;

&lt;p&gt;Access should be restricted to people and systems that actually need it.&lt;/p&gt;




&lt;h1&gt;
  
  
  The &lt;code&gt;.env&lt;/code&gt; file
&lt;/h1&gt;

&lt;p&gt;A common way to manage environment variables locally is through a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
JWT_SECRET=super-secret-value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Libraries such as &lt;code&gt;dotenv&lt;/code&gt; can load these values into the runtime.&lt;/p&gt;

&lt;p&gt;For Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frameworks like Next.js, NestJS, Laravel, Rails, Django, and many others provide their own approaches or integrations for managing environment variables.&lt;/p&gt;




&lt;h1&gt;
  
  
  Never commit your real &lt;code&gt;.env&lt;/code&gt; file
&lt;/h1&gt;

&lt;p&gt;One of the most important rules when working with environment variables is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not commit files containing secrets to your Git repository.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;.gitignore&lt;/code&gt; should normally contain something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.local
.env.development.local
.env.production.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, credentials could accidentally become part of your repository history.&lt;/p&gt;

&lt;p&gt;And removing a secret from the latest commit does not necessarily remove it from previous commits.&lt;/p&gt;

&lt;p&gt;If a secret is accidentally committed, the safest approach is usually to rotate it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Use &lt;code&gt;.env.example&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;While the real &lt;code&gt;.env&lt;/code&gt; file should not normally be committed, an &lt;code&gt;.env.example&lt;/code&gt; file can be extremely useful.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=
DATABASE_URL=
JWT_SECRET=
API_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file documents which environment variables the project expects without exposing their actual values.&lt;/p&gt;

&lt;p&gt;A developer cloning the project can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then fill in the required values.&lt;/p&gt;

&lt;p&gt;This is much better than making a new team member discover missing environment variables one runtime error at a time.&lt;/p&gt;




&lt;h1&gt;
  
  
  The problem with multiple &lt;code&gt;.env&lt;/code&gt; files
&lt;/h1&gt;

&lt;p&gt;For small projects, you might start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env.development
.env.staging
.env.production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can work well.&lt;/p&gt;

&lt;p&gt;But there is an important limitation:&lt;/p&gt;

&lt;p&gt;you still need a reliable way to distribute the correct values.&lt;/p&gt;

&lt;p&gt;For example, suppose three developers work on the same application.&lt;/p&gt;

&lt;p&gt;Developer A creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_SECRET_KEY=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developer B needs the same value.&lt;/p&gt;

&lt;p&gt;What happens next?&lt;/p&gt;

&lt;p&gt;Many teams end up sending environment variables through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;li&gt;Discord&lt;/li&gt;
&lt;li&gt;email&lt;/li&gt;
&lt;li&gt;WhatsApp&lt;/li&gt;
&lt;li&gt;Notion&lt;/li&gt;
&lt;li&gt;text files&lt;/li&gt;
&lt;li&gt;shared documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates several problems.&lt;/p&gt;

&lt;p&gt;Values become duplicated.&lt;/p&gt;

&lt;p&gt;Nobody knows which version is current.&lt;/p&gt;

&lt;p&gt;Old secrets remain in message history.&lt;/p&gt;

&lt;p&gt;New team members may not know which variables they need.&lt;/p&gt;

&lt;p&gt;And production credentials may accidentally be shared with people who only need development access.&lt;/p&gt;




&lt;h1&gt;
  
  
  Keep environment structure consistent
&lt;/h1&gt;

&lt;p&gt;A good approach is to keep the same variable names across environments whenever possible.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;h3&gt;
  
  
  Development
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://localhost/myapp
API_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Staging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://staging-db/myapp
API_URL=https://staging-api.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Production
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://production-db/myapp
API_URL=https://api.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application always reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It doesn't need to know which environment it is running in.&lt;/p&gt;

&lt;p&gt;The deployment environment provides the correct values.&lt;/p&gt;

&lt;p&gt;This keeps application code simpler.&lt;/p&gt;




&lt;h1&gt;
  
  
  Avoid environment-specific variable names
&lt;/h1&gt;

&lt;p&gt;Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEV_DATABASE_URL=
STAGING_DATABASE_URL=
PRODUCTION_DATABASE_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is often unnecessary.&lt;/p&gt;

&lt;p&gt;It forces your application to determine which one it should use.&lt;/p&gt;

&lt;p&gt;Instead, prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and provide a different value in each environment.&lt;/p&gt;

&lt;p&gt;This makes the configuration portable and predictable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Validate environment variables
&lt;/h1&gt;

&lt;p&gt;One of the biggest mistakes in environment-variable management is assuming that a variable exists.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens if &lt;code&gt;API_KEY&lt;/code&gt; is missing?&lt;/p&gt;

&lt;p&gt;The application may fail much later when that value is finally used.&lt;/p&gt;

&lt;p&gt;A better approach is to validate configuration during startup.&lt;/p&gt;

&lt;p&gt;For example, with Zod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;zod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;

  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="na"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application fails immediately if the configuration is invalid.&lt;/p&gt;

&lt;p&gt;This is much easier to debug than discovering a missing variable after the application has already started.&lt;/p&gt;




&lt;h1&gt;
  
  
  Validate types too
&lt;/h1&gt;

&lt;p&gt;Remember that environment variables are strings.&lt;/p&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not automatically become a JavaScript number.&lt;/p&gt;

&lt;p&gt;If you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So configuration validation is also a good place to transform values.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="na"&gt;ENABLE_ANALYTICS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your application receives correctly typed values.&lt;/p&gt;




&lt;h1&gt;
  
  
  Separate secrets from non-secret configuration
&lt;/h1&gt;

&lt;p&gt;Not every environment variable is a secret.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
NODE_ENV=production
LOG_LEVEL=info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are configuration values, but they are usually not sensitive.&lt;/p&gt;

&lt;p&gt;These are very different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_PASSWORD=...
JWT_SECRET=...
STRIPE_SECRET_KEY=...
AWS_SECRET_ACCESS_KEY=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are secrets.&lt;/p&gt;

&lt;p&gt;Treating everything as equally sensitive may create unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Treating nothing as sensitive is much worse.&lt;/p&gt;

&lt;p&gt;A useful distinction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configuration
↓
values that control application behavior

Secrets
↓
credentials that grant access to external resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction becomes important when deciding where variables should be stored and who should be able to read them.&lt;/p&gt;




&lt;h1&gt;
  
  
  Limit access by environment
&lt;/h1&gt;

&lt;p&gt;Not every developer needs production credentials.&lt;/p&gt;

&lt;p&gt;A team might use permissions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
→ Development
→ Staging

Senior developer
→ Development
→ Staging
→ limited Production access

Operations
→ Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This follows the principle of least privilege.&lt;/p&gt;

&lt;p&gt;People should receive only the permissions they need to perform their work.&lt;/p&gt;

&lt;p&gt;This is especially important as the team grows.&lt;/p&gt;




&lt;h1&gt;
  
  
  Be careful with frontend environment variables
&lt;/h1&gt;

&lt;p&gt;Another common mistake is assuming that an environment variable is secret simply because it lives in a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Frontend applications run in the user's browser.&lt;/p&gt;

&lt;p&gt;Any value bundled into client-side JavaScript can potentially be inspected by the user.&lt;/p&gt;

&lt;p&gt;For example, in Next.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_API_URL=https://api.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is intentionally exposed to the browser.&lt;/p&gt;

&lt;p&gt;You should never put something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_DATABASE_PASSWORD=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_STRIPE_SECRET_KEY=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because those values would no longer be secret.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.env&lt;/code&gt; file protects configuration from source control.&lt;/p&gt;

&lt;p&gt;It does not automatically protect variables from being exposed in a frontend bundle.&lt;/p&gt;




&lt;h1&gt;
  
  
  Environment variables in Docker
&lt;/h1&gt;

&lt;p&gt;Docker applications commonly receive configuration through environment variables.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-api&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env.production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you should still consider where &lt;code&gt;.env.production&lt;/code&gt; comes from.&lt;/p&gt;

&lt;p&gt;Copying production secrets around servers manually can become difficult to manage as infrastructure grows.&lt;/p&gt;

&lt;p&gt;For larger systems, deployment platforms or dedicated secret-management tools are usually a better choice.&lt;/p&gt;




&lt;h1&gt;
  
  
  Environment variables in CI/CD
&lt;/h1&gt;

&lt;p&gt;CI/CD pipelines usually provide secrets through encrypted repository or project settings.&lt;/p&gt;

&lt;p&gt;For example, GitHub Actions allows secrets to be configured and referenced from workflows.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATABASE_URL }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployment pipeline then injects the variable into the application environment.&lt;/p&gt;

&lt;p&gt;This has an important advantage:&lt;/p&gt;

&lt;p&gt;the production secret does not need to live in the Git repository.&lt;/p&gt;




&lt;h1&gt;
  
  
  Avoid manually copying &lt;code&gt;.env&lt;/code&gt; files to servers
&lt;/h1&gt;

&lt;p&gt;A workflow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;developer machine
↓
copy .env.production
↓
SSH
↓
paste file into server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;works.&lt;/p&gt;

&lt;p&gt;But it has several weaknesses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easy to make mistakes&lt;/li&gt;
&lt;li&gt;difficult to audit&lt;/li&gt;
&lt;li&gt;difficult to synchronize&lt;/li&gt;
&lt;li&gt;difficult to automate&lt;/li&gt;
&lt;li&gt;difficult to revoke access&lt;/li&gt;
&lt;li&gt;difficult to track configuration changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more environments and developers you have, the more painful this becomes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Treat environment configuration as part of your deployment process
&lt;/h1&gt;

&lt;p&gt;Your deployment pipeline should answer this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where does the application receive its configuration?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub
↓
CI/CD
↓
production server
↓
environment variables injected at runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation depends on your infrastructure.&lt;/p&gt;

&lt;p&gt;The important point is that configuration delivery should be intentional rather than improvised.&lt;/p&gt;




&lt;h1&gt;
  
  
  Document every variable
&lt;/h1&gt;

&lt;p&gt;A simple but very effective habit is to document each environment variable.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REDIS_URL=
SESSION_SECRET=
EMAIL_FROM=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;maintain documentation like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REDIS_URL

Redis connection URL used for caching and sessions.

Required:
Yes

Example:
redis://localhost:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes increasingly important as the number of variables grows.&lt;/p&gt;

&lt;p&gt;It is also useful when onboarding new developers.&lt;/p&gt;




&lt;h1&gt;
  
  
  Keep naming consistent
&lt;/h1&gt;

&lt;p&gt;Good names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL
REDIS_URL
JWT_SECRET
SMTP_HOST
SMTP_PORT
SMTP_USERNAME
SMTP_PASSWORD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Poor naming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB
REDIS_CONNECTION_THING
JWT
MAILPW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The purpose of a variable should be obvious from its name.&lt;/p&gt;




&lt;h1&gt;
  
  
  Remove unused environment variables
&lt;/h1&gt;

&lt;p&gt;Environment variables tend to accumulate.&lt;/p&gt;

&lt;p&gt;Months later you might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD_API_KEY=
LEGACY_DATABASE_URL=
TEMP_FEATURE_FLAG=
OLD_SMTP_PASSWORD=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but nobody knows whether they are still used.&lt;/p&gt;

&lt;p&gt;Periodically review your configuration.&lt;/p&gt;

&lt;p&gt;Unused variables should be removed.&lt;/p&gt;

&lt;p&gt;Unused secrets should also be revoked when appropriate.&lt;/p&gt;

&lt;p&gt;This reduces configuration complexity and security risk.&lt;/p&gt;




&lt;h1&gt;
  
  
  Rotate secrets
&lt;/h1&gt;

&lt;p&gt;Secrets should not necessarily live forever.&lt;/p&gt;

&lt;p&gt;Credentials may need to be rotated because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;someone leaves the team&lt;/li&gt;
&lt;li&gt;a credential is exposed&lt;/li&gt;
&lt;li&gt;a provider recommends rotation&lt;/li&gt;
&lt;li&gt;a repository accidentally contains the secret&lt;/li&gt;
&lt;li&gt;an employee device is compromised&lt;/li&gt;
&lt;li&gt;security policy requires regular rotation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good environment-management workflow should make it reasonably easy to update a value across the systems that depend on it.&lt;/p&gt;




&lt;h1&gt;
  
  
  How should teams share environment variables?
&lt;/h1&gt;

&lt;p&gt;For very small projects, developers can manually maintain their local &lt;code&gt;.env&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;This is often enough.&lt;/p&gt;

&lt;p&gt;As the team or number of projects grows, it can become useful to have a centralized place where environment variables are organized by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project
↓
Environment
↓
Variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My API

├── Development
│   ├── DATABASE_URL
│   ├── REDIS_URL
│   └── API_KEY
│
├── Staging
│   ├── DATABASE_URL
│   ├── REDIS_URL
│   └── API_KEY
│
└── Production
    ├── DATABASE_URL
    ├── REDIS_URL
    └── API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure makes it much easier to understand which configuration belongs to which environment.&lt;/p&gt;




&lt;h1&gt;
  
  
  This is why I built Envly
&lt;/h1&gt;

&lt;p&gt;I recently started building &lt;strong&gt;Envly&lt;/strong&gt;, a developer tool for organizing environment variables across projects and environments.&lt;/p&gt;

&lt;p&gt;The idea came from a very common workflow problem.&lt;/p&gt;

&lt;p&gt;I often had several projects containing multiple &lt;code&gt;.env&lt;/code&gt; files, and keeping development, staging, and production configuration organized became increasingly annoying.&lt;/p&gt;

&lt;p&gt;The typical project might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-a
project-b
project-c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And every project might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;development
staging
production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of treating environment variables as random files scattered across machines, Envly organizes them around those concepts directly.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Envly

Project
├── Development
├── Staging
└── Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each environment then contains its own variables.&lt;/p&gt;




&lt;h1&gt;
  
  
  A CLI makes this workflow even more useful
&lt;/h1&gt;

&lt;p&gt;I'm also working on an Envly CLI so developers can interact with their environments directly from the terminal.&lt;/p&gt;

&lt;p&gt;For example, the workflow can become something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;envly login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;envly projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And eventually retrieving configuration directly for a project/environment rather than manually moving &lt;code&gt;.env&lt;/code&gt; files around.&lt;/p&gt;

&lt;p&gt;The goal is not to replace environment variables.&lt;/p&gt;

&lt;p&gt;Environment variables are already a good abstraction.&lt;/p&gt;

&lt;p&gt;The goal is to make managing them less fragmented.&lt;/p&gt;

&lt;p&gt;You can find the project here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://envly.dev" rel="noopener noreferrer"&gt;https://envly.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the source code is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/justoverclockl/envly" rel="noopener noreferrer"&gt;https://github.com/justoverclockl/envly&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  A practical environment variable workflow
&lt;/h1&gt;

&lt;p&gt;For a typical application, a reasonable workflow might look like this:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Commit an &lt;code&gt;.env.example&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=
REDIS_URL=
JWT_SECRET=
API_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Ignore local &lt;code&gt;.env&lt;/code&gt; files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your framework, you may want to exclude certain example files from this rule.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Validate variables on startup
&lt;/h2&gt;

&lt;p&gt;Use a schema library such as Zod, Joi, or your framework's validation system.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Separate environments
&lt;/h2&gt;

&lt;p&gt;Maintain different values for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development
Staging
Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while keeping variable names consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Keep secrets out of Git
&lt;/h2&gt;

&lt;p&gt;Never store production credentials directly in the repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Inject production values during deployment
&lt;/h2&gt;

&lt;p&gt;Use your hosting provider, CI/CD pipeline, container runtime, or secret-management system.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Restrict production access
&lt;/h2&gt;

&lt;p&gt;Not every team member needs every secret.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Maintain documentation
&lt;/h2&gt;

&lt;p&gt;Developers should know what every required variable does.&lt;/p&gt;




&lt;h1&gt;
  
  
  Example project structure
&lt;/h1&gt;

&lt;p&gt;A Node.js application might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-project/

├── src/
├── package.json
├── .env
├── .env.example
└── .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.env.example&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=
PORT=
DATABASE_URL=
JWT_SECRET=
API_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=development
PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
JWT_SECRET=local-development-secret
API_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production values would then come from the deployment environment rather than from the repository.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common mistakes
&lt;/h1&gt;

&lt;p&gt;Here are some of the environment-variable mistakes I've seen most often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Committing &lt;code&gt;.env&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Probably the most obvious one.&lt;/p&gt;

&lt;p&gt;Once a secret has entered Git history, assume it may have been exposed.&lt;/p&gt;

&lt;p&gt;Rotate it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sharing secrets through chat
&lt;/h2&gt;

&lt;p&gt;Sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_PASSWORD=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;through Slack or Discord may be convenient, but those credentials can remain in message history indefinitely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using the same credentials everywhere
&lt;/h2&gt;

&lt;p&gt;Avoid using the same database password, API token, or secret in development and production.&lt;/p&gt;

&lt;p&gt;If development credentials leak, production should not automatically be compromised.&lt;/p&gt;




&lt;h2&gt;
  
  
  Giving everyone production access
&lt;/h2&gt;

&lt;p&gt;Convenient?&lt;/p&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;Necessary?&lt;/p&gt;

&lt;p&gt;Usually not.&lt;/p&gt;




&lt;h2&gt;
  
  
  Not validating variables
&lt;/h2&gt;

&lt;p&gt;Missing environment variables should ideally stop the application during startup rather than causing unexpected failures later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exposing secrets in frontend code
&lt;/h2&gt;

&lt;p&gt;Anything delivered to the browser should be considered visible to the user.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping abandoned variables forever
&lt;/h2&gt;

&lt;p&gt;Remove obsolete configuration.&lt;/p&gt;

&lt;p&gt;Your future self will thank you.&lt;/p&gt;




&lt;h1&gt;
  
  
  Environment variables vs secret managers
&lt;/h1&gt;

&lt;p&gt;Environment variables and secret managers solve related but different problems.&lt;/p&gt;

&lt;p&gt;Environment variables are a &lt;strong&gt;configuration delivery mechanism&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A secret manager is typically responsible for securely storing and controlling access to sensitive values.&lt;/p&gt;

&lt;p&gt;A common production architecture might therefore be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Secret manager
↓
deployment system
↓
environment variables
↓
application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application still consumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the value originates from a more secure system.&lt;/p&gt;




&lt;h1&gt;
  
  
  When does a simple &lt;code&gt;.env&lt;/code&gt; file stop being enough?
&lt;/h1&gt;

&lt;p&gt;There is no universal number.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.env&lt;/code&gt; file can remain perfectly reasonable for a solo developer or small application.&lt;/p&gt;

&lt;p&gt;You may want to consider a more structured workflow when you start having:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;several developers&lt;/li&gt;
&lt;li&gt;multiple projects&lt;/li&gt;
&lt;li&gt;development, staging, and production environments&lt;/li&gt;
&lt;li&gt;frequent configuration changes&lt;/li&gt;
&lt;li&gt;sensitive credentials&lt;/li&gt;
&lt;li&gt;onboarding difficulties&lt;/li&gt;
&lt;li&gt;repeated manual sharing of &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;inconsistent configuration between team members&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem usually isn't the &lt;code&gt;.env&lt;/code&gt; format itself.&lt;/p&gt;

&lt;p&gt;The problem is synchronization and organization.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;Environment variables are easy to introduce but surprisingly easy to mismanage.&lt;/p&gt;

&lt;p&gt;For small projects, the basics are often enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.example
.gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the application grows, the workflow should grow with it.&lt;/p&gt;

&lt;p&gt;A solid environment-variable strategy usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate development, staging, and production configuration&lt;/li&gt;
&lt;li&gt;consistent variable names&lt;/li&gt;
&lt;li&gt;startup validation&lt;/li&gt;
&lt;li&gt;no secrets committed to Git&lt;/li&gt;
&lt;li&gt;controlled production access&lt;/li&gt;
&lt;li&gt;documented variables&lt;/li&gt;
&lt;li&gt;secure secret storage&lt;/li&gt;
&lt;li&gt;automated configuration during deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, configuration should be treated as an intentional part of application architecture rather than as a collection of &lt;code&gt;.env&lt;/code&gt; files copied between machines.&lt;/p&gt;

&lt;p&gt;That is also the problem I'm trying to solve with &lt;strong&gt;Envly&lt;/strong&gt;: making environment variables easier to organize across projects and environments without changing the way applications already consume configuration.&lt;/p&gt;

&lt;p&gt;If you're interested, you can check it out here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://envly.dev" rel="noopener noreferrer"&gt;https://envly.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>devops</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
