Deno برای اجرای JavaScript و TypeScript در بیرون مرورگر ساخته شده است. اگر سرویس شما با Node.js اجرا میشود، انتخاب Deno به معنی تعویض یک نام در دستور اجرا نیست؛ باید سازگاری وابستگیها، مجوزهای برنامه و روش استقرار را کنار هم ببینید. عنوان قبلی این مطلب از انتشار Deno 3 و سازگاری صددرصدی خبر میداد. فهرست انتشارهای رسمی Deno در زمان بازبینی این متن نسخههای خانواده 2 را نشان میدهد و مبنایی برای آن خبر وجود ندارد. مقایسهای که به تصمیم تیم کمک میکند باید روی رفتار واقعی برنامه تمرکز کند، نه شماره نسخه فرضی یا درصد سرعت بدون روش اندازهگیری.
Runtime چه چیزی را عوض میکند؟
کد سمت سرور به محیطی نیاز دارد که فایل بخواند، اتصال شبکه برقرار کند و درخواست HTTP را پاسخ دهد. Runtime این امکانات را در اختیار برنامه میگذارد. بخشی از کد شما از زبان JavaScript استفاده میکند و بخش دیگر به APIهای محیط وابسته است. یک تابع جمع معمولاً جابهجا میشود؛ برنامهای که فرایند فرزند ایجاد میکند، از افزونه بومی استفاده میکند یا رفتار دقیق یک ابزار خط فرمان را فرض گرفته است، بررسی بیشتری میخواهد.
برای جدا کردن این دو دسته، از یک مسیر کوچک شروع کنید. مثلاً تبدیل داده یک فایل گزارش را از ارسال آن به سرویس خارجی جدا کنید. تابع تبدیل با ورودی مشخص کار میکند و آسانتر قابل استفاده مجدد است. لایه خواندن فایل و ارسال شبکه همان جایی است که تفاوت محیطها ظاهر میشود. چنین جداسازی حتی اگر مهاجرت را کنار بگذارید، خطاهای برنامه را قابل فهمتر میکند.
در یک تیم، Runtime روی محیط توسعه و عملیات هم اثر دارد. کسی که مسئول پشتیبانی سرویس است باید بداند چگونه برنامه متوقف میشود، گزارش خطا کجا میرود و چه چیزی برای راهاندازی مجدد لازم است. تصمیمی که فقط روی لپتاپ نویسنده نمونه موفق است هنوز تصمیم استقرار نیست. یک پروژه کوچک و مستقل فرصت میدهد این سؤالها را بدون جابهجایی همزمان تمام سرویسها پاسخ دهید.
سازگاری با npm را برای وابستگی خودتان بخوانید
Deno از بستههای npm و بسیاری از APIهای Node پشتیبانی میکند، اما این پشتیبانی را نباید تضمین رفتار یکسان همه بستهها دانست. راهنمای رسمی Node و npm Compatibility درباره package.json، node_modules، CommonJS و افزونههای بومی توضیح میدهد. بستهای که فقط محاسبه انجام میدهد با بستهای که در نصب، کد بومی میسازد شرایط یکسانی ندارد. پیش از انتخاب، فهرست وابستگیهای مستقیم و وابستگیهای مهم آنها را تهیه کنید.
برای هر بسته، نقش آن را بنویسید. آیا فقط هنگام build به آن نیاز دارید یا در هر درخواست اجرا میشود؟ آیا به binary خارجی متکی است؟ آیا فایل را با مسیر نسبی از پوشه کاری باز میکند؟ این اطلاعات کمک میکند خطای ناسازگاری را از تنظیم اشتباه جدا کنید. نام یک بسته در فهرست پشتیبانی برای نتیجه گرفتن درباره ترکیب خاص نسخههای پروژه کافی نیست.
تغییر همزمان Runtime و نسخه تمام کتابخانهها عیبیابی را دشوار میکند. اگر خروجی گزارش تفاوت پیدا کند، معلوم نیست تغییر از کدام لایه آمده است. مهاجرت محدود را با ورودیهای شناختهشده انجام دهید و خروجی مورد انتظار را ثبت کنید. پس از روشن شدن رفتار، ارتقای وابستگیها را به تغییر جداگانه تبدیل کنید. Lockfile هم باید همراه کد نگهداری شود تا نصب بعدی تصمیم تازه و پنهانی درباره نسخهها نگیرد.
مجوزها بخشی از قرارداد اجرا هستند
برنامه Deno بهطور پیشفرض مجوز آزاد برای عملیات حساس مانند خواندن فایل و دسترسی شبکه ندارد. مجوز را میتوان به مسیر یا میزبان مشخص محدود کرد. مستندات Security and permissions شرح میدهد که کدهای روی یک thread مجوز مشترک دارند؛ این مدل، مجوز جداگانه برای هر کتابخانه ایجاد نمیکند. استفاده از allow-all نیز محدودیت مورد انتظار را برمیدارد. بنابراین امنیت پیشفرض جای بررسی وابستگی را نمیگیرد.
فرض کنید برنامه فقط فایلهای یک پوشه گزارش را میخواند. دادن دسترسی خواندن به همان پوشه، نیاز برنامه را روشن میکند. اگر بعدها کتابخانهای تلاش کند فایل دیگری باز کند، شکست عملیات اطلاعات مفیدی درباره تغییر رفتار است. با باز کردن همه دسترسیها، همین علامت از بین میرود. مجوز مورد نیاز را کنار فرمان راهاندازی مستند کنید تا اجرای سرویس وابسته به پاسخ تعاملی یک نفر نباشد.
محدودیت مسیر هم بدون طراحی پوشه کافی نیست. قرار دادن فایل حساس و داده عمومی در یک پوشه، معنای مجوز محدود را ضعیف میکند. جدا کردن ورودی، خروجی و تنظیمات حساس به تیم کمک میکند محدوده دسترسی را قابل توضیح نگه دارد. در گزارش خطا نیز محتوای راز یا داده شخصی را چاپ نکنید؛ برای فهمیدن علت شکست معمولاً نام عملیات و شناسه داخلی کافی است.
TypeScript و ابزارهای همراه
Deno اجرای فایل TypeScript را فراهم میکند، اما اجرای موفق به معنی بررسی کامل typeها نیست. راهنمای TypeScript تفاوت اجرای کد و type checking را توضیح میدهد. بررسی صریح با deno check را در گردش کار نگه دارید. ورودی JSON از شبکه هم فقط به خاطر annotation به داده معتبر تبدیل نمیشود؛ مقدار واقعی باید هنگام ورود اعتبارسنجی شود.
Formatter، linter و test runner همراه Runtime باعث میشوند برای شروع یک اسکریپت کوچک، ابزارهای کمتری تنظیم کنید. با این حال تیمی که قواعد اختصاصی یا ابزار موجود دارد باید هزینه تغییر آن قواعد را حساب کند. هدف یکسان بودن ظاهری همه پروژهها نیست؛ کسی که کد را مرور میکند باید از فرمانهای مشخص به همان نتیجه برسد. فایل deno.json محل ثبت تنظیمات و taskهای پروژه است.
نمونه مستقل یک سرویس HTTP
نمونه زیر به بسته خارجی، پایگاه داده یا فایل تصویر نیاز ندارد. فایل را با نام server.ts ذخیره کنید. با نسخه نصبشده از خانواده Deno 2، فرمان deno run --allow-net=127.0.0.1:8000 server.ts را اجرا کنید. سرویس روی همان نشانی محلی گوش میدهد و فقط درخواست GET به مسیر health را پاسخ موفق میدهد. این یک نمونه آموزشی برای روشن کردن قرارداد HTTP است و احراز هویت یا API کسبوکار ندارد.
const hostname = "127.0.0.1";
const port = 8000;
Deno.serve({ hostname, port }, (request: Request): Response => {
const url = new URL(request.url);
if (request.method !== "GET") {
return Response.json({ error: "method_not_allowed" }, {
status: 405,
headers: { Allow: "GET" },
});
}
if (url.pathname !== "/health") {
return Response.json({ error: "not_found" }, { status: 404 });
}
return Response.json({ status: "ok" });
});
Response.json بدنه JSON و header مناسب آن را میسازد. تفکیک پاسخ method نامعتبر از مسیر ناموجود کمک میکند مصرفکننده بداند کدام بخش درخواست را اصلاح کند. در برنامه واقعی وضعیت سلامت باید از آمادگی سرویس برای کار تفکیک شود؛ صرف زنده بودن فرایند ثابت نمیکند اتصال لازم به پایگاه داده برقرار است. راهنمای HTTP Server مرجع API استفادهشده در این نمونه است.
اگر این مثال را به سرویس واقعی گسترش میدهید، زمان انتظار عملیات خارجی و اندازه مجاز ورودی را مشخص کنید. یک handler که منتظر پاسخ نامحدود میماند، ظرفیت سرویس را مصرف میکند. پاسخ خطا نیز باید اطلاعات لازم برای کاربر را داشته باشد و جزئیات داخلی را به گزارش سرور بسپارد. انتخاب Runtime این تصمیمها را از شما نمیگیرد؛ آنها بخشی از طراحی API هستند.
مهاجرت را با یک قرارداد قابل بازگشت جلو ببرید
برای شروع، کاری انتخاب کنید که ورودی و خروجی محدود دارد: مثلاً تولید یک گزارش داخلی، تبدیل داده یا سرویس کوچک خواندنی. قرارداد خروجی، وضعیتهای خطا و نیازهای دسترسی را قبل از جابهجایی بنویسید. اگر سرویس قبلی هنوز کار میکند، مسیر بازگشت را نگه دارید. تغییر Runtime نباید در همان روز شکل داده ذخیرهشده را هم عوض کند؛ این دو تغییر، بازگشت را به هم گره میزنند.
تیم باید تفاوت فرمانهای توسعه و استقرار را بداند. Watch mode برای راحتی توسعه مناسب است، اما سرویس مستقر به مدیریت فرایند، دریافت سیگنال توقف و ثبت خطا نیاز دارد. فایل تنظیمات و مستند راهاندازی را بخشی از تغییر حساب کنید. درباره کنترل این گردش کار، مقاله CI/CD برای پروژههای فرانتاند به تفکیک مراحل نصب و انتشار کمک میکند؛ اصول تفکیک مرحلهها در پروژه سرور هم کاربرد دارد.
در ارزیابی عملکرد، درخواستها و داده یکسان لازم است. زمان آغاز فرایند، حافظه در بار پایدار و زمان پاسخ، پرسشهای متفاوتی هستند. یک نمونه بسیار کوچک جواب همه آنها نیست. بهجای نقل یک درصد عمومی، بنویسید کدام نیاز پروژه برآورده شده و کدام محدودیت باقی مانده است. اگر وابستگی مهم یا محیط استقرار شما دردسر تازه ایجاد میکند، ماندن روی Node.js تصمیم قابل دفاعی است.
تصمیم نهایی را در یادداشتی کوتاه ثبت کنید: محدوده استفاده از Deno، وابستگیهای بررسیشده، مجوزهای لازم و مسئول نگهداری. برای سرویس بعدی همین یادداشت را بازبینی کنید، چون موفقیت یک ابزار گزارشگیری تضمین مناسبت آن برای تمام محصولات نیست. قدم بعدی عملی، انتخاب همان کار محدود و تهیه قرارداد ورودی و خروجی آن است؛ این سند باید قبل از تغییر فرمان اجرای سرویس آماده باشد.
هنوز دیدگاهی ثبت نشده
اولین نفری باشید که نظر میدهد!