نحوه صحیح استفاده از متغیرهای محیطی (Environment Variables)

یکشنبه 21 دی 1399

متغیرهای محیطی (Environment variables) یکی از مفاهیم اساسی برای توسعه‌دهندگان برنامه است. آن‌ها چیزی هستند که ما روزانه از آن‌ها استفاده می‌کنیم. متغیرهای محیطی مزایای زیادی دارند که شامل پیکربندی و امنیت برنامه است.

نحوه صحیح استفاده از متغیرهای محیطی (Environment Variables)

متغیرهای محیطی بسیار عالی هستند. با این حال همه چیز هزینه‌بر است و این متغیرها اگر بی‌احتیاط استفاده شوند، می‌توانند تاثیرات مضری بر کد  و برنامه‌های ما داشته باشند.

متغیرهای محیطی

اگر متغیرهای محیطی به نوشتن کد ایمن‌تر و پیکربندی راحت‌تر برنامه‌ها در محیط‌های مختلف به ما کمک می‌کنند، چگونه ممکن است چیز بدی باشند؟

متغیرهای محیطی سراسری و خارجی هستند، که از طریق آن‌ها توسعه‌دهندگان برنامه قادر به تزریق پیکربندی‌ها و مدیریت آن‌ها در مکانی هستند که سازش آن‌ها دشوارتر است.

همه ما به عنوان توسعه‌دهنده می‌دانیم که موقعیت‌های سراسری برای برنامه‌های ما می‌تواند بد باشد. در این مقاله ما بر روی دو نقص اصلی تمرکز خواهیم کرد که بیشتر در هنگام سر و کار داشتن با متغیرهای محیطی با آن‌ها رو به رو می‌شویم:

عدم انعطاف‌پذیری / تست‌پذیری ضعیف

خوانایی / درک کد

نحوه استفاده صحیح از متغیرهای محیطی

به طور مشابه نحوه سر و کار داشتن با متغیرهای سراسری یا الگوهای سراسری (مثل singleton) که در مکان‌های بدی اعمال می‌شوند، سلاح مورد علاقه ما تزریق وابستگی است.

قرار نیست دقیقا همان کاری باشد که ما برای وابستگی‌های کد انجام می‌دهیم، اما اصول یکسان است. به جای استفاده مستقیم از متغیرهای محیطی (وابستگی‌ها) آن‌ها را در مکان‌هایی که واقعا استفاده می‌شوند (callsites) تزریق می‌کنیم. این رابطه را از " callsites وابسته" به " callsites مورد نیاز" معکوس کنید.

تزریق وابستگی این مسائل را با استفاده از موارد زیر حل می‌کند:

به توسعه‌دهندگان اجازه می‌دهد پیکربندی‌ها را به راحتی در زمان تست تزریق کنند.

خوب چطور این اصول را اعمال کنیم

ما از یک مثال Node.js استفاده می‌کنیم تا ببینیم چطور می‌توانیم یک کد پایه را ریفکتور کنیم و متغیرهای محیطی جداگانه را از بین ببریم.

بیاید بگوییم ما یک برنامه ساده با یک endpoint داریم که برای همه TODOها در یک دیتابیس PostGres کوئری می‌زند. اینجا ماژول دیتابیس ما با متغیرهای محیطی جداگانه آمده است:

const { Client } = require("pg");

function Postgres() {
  const c = new Client({
    connectionString: process.env.POSTGRES_CONNECTION_URL,
  });
  this.client = c;
  return this;
}

Postgres.prototype.init = async function () {
  await c.connect();
  return this;
};

Postgres.prototype.getTodos = async function () {
  return this.client.query("SELECT * FROM todos");
};

module.exports = Postgres;

و این ماژول از طریق ورودی برنامه به HTTP controller ما تزریق می‌شود:

const express = require("express");
const TodoController = require("./controller/todo");
const Postgres = require("./pg");

const app = express();

(async function () {
  const db = new Postgres();
  await db.init();
  const controller = new TodoController(db);
  controller.install(app);

  app.listen(process.env.PORT, (err) => {
    if (err) console.error(err);
    console.log(`UP AND RUNNING @ ${process.env.PORT}`);
  });
})();

با نگاهی به فایل نقطه ورود (entrypoint) بالا، ما راهی برای تعریف نیازهای برنامه برای متغیرهای محیطی (به طور کلی پیکربندی environment به صورت سراسری) نداریم.

ریفکتورینگ کد

اولین قدم برای بهبود کد که قبلا مشخص شده، شناسایی تمام مکان‌هایی است که مستقیما از متغیرهای محیطی استفاده می‌شود.

برای موارد خاص ما در بالا، خیلی سر راست است زیرا کد پایه کوچک است. اما برای کدهای بزرگ‌تر، می‌توانید از ابزارهایی مثل eslint برای اسکن همه مکان‌هایی که مستقیما از متغیرهای محیطی استفاده می‌کنند، استفاده کنید. فقط یک قانون تنظیم کنید، مثلا منع متغیرهای محیطی (مثل node/no-process-env از eslint-plugin-node).

اکنون زمان آن رسیده که استفاده مستقیم از متغیرهای محیطی را از ماژول‌های برنامه خود حذف کنیم و این پیکربندی‌ها را به عنوان بخشی از نیازهای ماژول لحاظ کنیم:

...
function Postgres(opts) {
  const { connectionString } = opts;
  const c = new Client({
    connectionString,
  });
  this.client = c;
  return this;
}
...

این پیکربندی‌ها فقط از نقطه ورود برنامه ما تولید می‌شوند:

...
const db = new Postgres({
  connectionString: process.env.POSTGRES_CONNECTION_URL,
});
...

با توجه به نقطه ورود، کاملا مشخص است که شرایط محیطی برنامه ما اکنون چیست. این از مشکلات بالقوه متغیرهای محیطی که فراموش می‌شوند تا افزوده شوند جلوگیری می‌کند.

اما ممکن است در اینجا برای شما سوالی پیش بیاید:

چرا از یک ماژول/فایل مرکزی استفاده نمی‌کنیم؟

ما چندین راه‌حل برای حل این مشکل با استفاده از یک مکان مرکزی برای ترسیم این مقادیر دیده‌ایم (مانند ماژول/فایل config.js برای پروژه‌های Node).

اما این روش بهتر از استفاده از متغیرهای محیطی ارائه شده توسط runtime برنامه نیست (مثل process.env) زیرا همه چیز هنوز هم تا حدی در موقعیت سراسری است (یک آبجکت پیکربندی که در سراسر برنامه استفاده می‌شود).

در حقیقت این می‌تواند حتی بدتر باشد زیرا اکنون ما مکان دیگری را برای کد معرفی می‌کنیم.

جمع‌بندی

در این مقاله سعی کردیم تا با یک مثال ساده نحوه استفاده صحیح از متغیرهای محیطی را برای شما بیان کنیم. همیشه به خاطر داشته باشید که استفاده نادرست از این متغیرها می‌تواند عواقبی برای برنامه‌یتان داشته باشد.

برنامه نویسان

نویسنده 3204 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید