الـ Serverless من جُوّه: cold starts و freeze و timeouts و connections
نقلنا الـ worker لـ Lambda، وأول يوم الـ database وقعت من كتر الـ connections، والإيميلات بقت توصل بعد ساعات أو ما توصلش، ورسالة واحدة بايزة بتخلي الـ batch كله يتعاد لحد ما يروح الـ DLQ، والـ orders بتتحفظ مرتين.
نص شرح المعلم والكود الذي كتبه ونتيجة تشغيله، بنفس ترتيب الدرس.
أهلًا بيك. بص على handler dot naive dot js. ده worker بيستقبل orders من queue. كان شغال على server عادي، وقررنا ننقله serverless، زي AWS Lambda، علشان ما نشيلش هم الـ servers ولا الـ scaling. الـ code شكله بسيط ومعقول: pool للـ database، و loop على الرسايل، ونحفظ، ونبعت إيميل، ونعدّ. وده اللي حصل أول يوم: جه ضغط، والـ platform طلّع مِيّة instance في نفس الوقت، كل واحد فيهم بيعمل pool جديد في كل invoke، فيه لحد عشرة connections. الـ database ليها مِيّة connection بس، فوقعت، والـ API العادي كمان وقع معاها. الإيميلات: ساعات توصل بعد ساعة، وساعات ما توصلش خالص. رسالة واحدة فيها JSON بايز، والـ handler رمى error، فالـ batch كله اترجع للـ queue، والرسايل السليمة اتحفظت تاني وتالت لحد ما الـ batch كله راح الـ DLQ. والـ setInterval اللي بيطبع العدد، ساعات بيطبع أرقام غريبة، وساعات ما بيطبعش خالص. وكل ده لأننا كتبنا الـ code كأنه process واحد عايش على طول. والـ function مش كده خالص.
تعالى نفهم الـ function بتعيش إزاي. الـ code بتاعك بيترفع كـ zip أو container image. ولما يجي أول event، الـ platform بيعمل حاجة اسمها execution environment، خلينا نسميه instance: مكان معزول فيه Node شغال. وده بيمر بمراحل: الـ INIT: الـ platform بينزّل الـ code، ويشغّل الـ runtime، وبعدين يشغّل الـ module بتاعك من فوق لتحت، يعني كل الـ imports وكل حاجة برّه الـ handler. ده الـ cold start، وممكن ياخد من مِيّة millisecond لكام تانية. الـ INVOKE: الـ platform بينادي الـ handler بالـ event. وبعد ما الـ handler يرجّع، الـ instance ما بيموتش، بيتجمّد. الـ process واقف خالص، مفيش CPU، مفيش timers، مفيش network. ولو جه event تاني، الـ platform يصحّي نفس الـ instance، وده warm start، سريع أوي، والـ INIT ما بيتعملش تاني. ولو فضل فاضي كام دقيقة، الـ platform بيقتله، وده الـ SHUTDOWN.
أهم حاجة لازم تفهمها: كل instance بيخدم request واحد بس في نفس الوقت. في Express على server، process واحد بيخدم ألف request في نفس الوقت بالـ event loop. في Lambda، لو جه عشرة events في نفس الوقت، الـ platform بيشغّل عشرة instances. وده معنى الـ concurrency هنا: عدد الـ instances اللي شغالة في نفس الوقت. ولكل account و function فيه حد أقصى. لو وصلت له، الـ events الجديدة بترجع throttled. ودي حاجة كويسة كمان: تقدر تحط reserved concurrency على function، يعني حد أقصى ليها، علشان ما تاكلش الـ database أو الـ account كله. وخلي بالك: الـ instances مش بيشوفوا بعض. كل واحد ليه memory لوحده. فلو حفظت حاجة في variable، هي في الـ instance ده بس.
طب الـ module scope ده نعمل بيه إيه؟ الحاجات اللي غالية وتتعاد بأمان، حطها برّه الـ handler علشان تتعمل مرة واحدة في الـ INIT وتفضل للـ invokes الجاية: الـ config، والـ SDK clients، والـ database connection. وده اسمه connection reuse. بس خلي بالك من حاجتين: ما تحطش state بتاع user في الـ module scope. الـ request الجاي على نفس الـ instance ممكن يكون user تاني، وهيلاقي بيانات الأول. والـ connection ممكن يكون مات وهو متجمّد، الـ database قفلته علشان idle. فلازم تعرف تعمل reconnect. وفيه أسلوب أحسن: lazy init. ما تفتحش الـ connection في الـ INIT نفسه، افتحه أول ما تحتاجه واحفظ الـ promise. كده الـ cold start أقصر، ولو الـ invoke ده مش محتاج database، مش هتفتح.
الـ cold start بييجي منين، وإزاي تقلله؟ بييجي من حجم الـ code، ومن الـ imports، وكل حاجة تقيلة بتتعمل في الـ module scope. ونوع الـ runtime ومقدار الـ memory كمان، لأن الـ CPU بيزيد مع الـ memory. فـ: اعمل bundle صغير بـ esbuild، وما تعملش import لـ SDK كامل وانت محتاج client واحد. خلي الـ init lazy. ولو الـ latency مهمة أوي، زي API قدام users، فيه provisioned concurrency: بتقول للـ platform خلي عشرة instances جاهزين ومعمولهم INIT على طول. بتدفع عليهم حتى لو مفيش traffic، بس مفيش cold start للعشرة الأولانيين. وفيه حلول زي SnapStart بتعمل snapshot بعد الـ INIT. وما تحاولش تعمل ping كل دقيقة علشان تفضل warm. ده بيدفّي instance واحد بس، وأول ما ييجي اتنين في نفس الوقت، التاني cold. الـ timeout. كل function ليها timeout، في Lambda أقصاه ربع ساعة، وخلف API Gateway حوالي تلاتين تانية بس. لو الـ handler عدّاه، الـ process بيتقتل في النص. مش exception تقدر تمسكها، ده قتل. والـ instance بيترمى، والـ event بيتعامل كأنه فشل. فالـ handler لازم يعرف فاضله قد إيه. الـ context فيه getRemainingTimeInMillis. ولو انت بتعالج batch، قبل كل رسالة اسأل: فاضلي وقت كفاية؟ لو لأ، وقّف، ورجّع الرسايل اللي ما اتعملتش على إنها فشلت، علشان تتعاد. ده أحسن بكتير من إنك تتقتل في نص save، ومش عارف إيه اللي اتحفظ وإيه لأ. وكمان أي call لـ service برّه لازم يكون ليه timeout أقل من الوقت الفاضل.
والشغل اللي بعد الـ return. في الـ naive، sendEmail من غير await. على server عادي، ده بيكمّل في الخلفية. هنا الـ handler رجّع، والـ instance اتجمّد فورًا، فالـ request بتاع الإيميل واقف في النص. لو جه event تاني على نفس الـ instance بعد ساعة، الإيميل يكمّل ساعتها. ولو الـ instance اتقتل، الإيميل ضاع خالص. ونفس الكلام للـ setInterval: الـ timer مش شغال وهو متجمّد. الحل: كل حاجة لازم تتعمل، await عليها قبل الـ return. ولو مش عايز الـ user يستنى، ابعتها لـ queue، وخليها function تانية. وبعض الـ platforms زي Vercel و Cloudflare فيها waitUntil، اللي بتقول للـ platform استنى الـ promise ده قبل ما تجمّد. ونفس الفكرة للـ logs والـ metrics: لو بتبعتها batch في الخلفية، لازم تعمل flush قبل الـ return.
الـ event sources. الـ function ممكن تتنادى بطريقتين: sync، زي API Gateway: الـ client مستني الـ response. لو فشلت، الـ client يشوف error. async، زي queue زي SQS، أو events من S3. هنا الـ platform بياخد batch من الرسايل ويديهولك. لو الـ handler رمى، الـ batch كله يرجع للـ queue بعد الـ visibility timeout، ويتعاد. ولو رسالة فشلت أكتر من maxReceiveCount، تروح الـ DLQ. المشكلة: رسالة واحدة بايزة ترجّع الـ batch كله. والحل اسمه partial batch response: الـ handler ما يرميش، يرجّع batchItemFailures فيها الـ ids اللي فشلت بس. والـ platform يرجّع دول بس للـ queue. وخلي بالك إن الـ visibility timeout لازم يكون أكبر من الـ function timeout، وإلا الرسالة هتبان لـ instance تاني وهي لسه بتتعالج. والـ delivery هنا at least once. نفس الرسالة ممكن توصل مرتين: retry بعد timeout، أو الـ queue نفسها بعتتها مرتين. والمرتين ممكن يروحوا على instances مختلفة. فالـ idempotency ما ينفعش تكون Set في الـ memory، لازم تكون في مكان مشترك: table في الـ database بـ unique على الـ message id، أو DynamoDB، أو Redis. والترتيب مهم: الأول تعمل الشغل، وبعدين تعلّم إنه اتعمل. ولو تقدر تعملهم في نفس الـ transaction، زي insert في orders و processed_messages مع بعض، يبقى أحسن، وده اللي عملناه في درس الـ inbox.
الـ database connections، ودي اللي وقّعت الـ database في الأول. كل instance ليه connections لوحده. لو الـ pool عشرة، ومعاك مِيّة instance، يبقى ألف connection. والـ Postgres الـ max_connections بتاعها مِيّة، وكل connection فيه process ومساحة memory. فاحسبها: الـ connections المتاحة، بعد ما تشيل جزء للـ admin والـ migrations والـ API العادي، مقسومة على الـ connections لكل instance، ده أقصى concurrency. والحلول: pool بـ max واحد في كل instance، لأنه أصلًا بيخدم request واحد. reserved concurrency على الـ function علشان ما تعدّيش الرقم ده. ولو محتاج concurrency أكبر، proxy في النص، زي RDS Proxy أو PgBouncer، بيخلي آلاف الـ clients يتشاركوا عدد قليل من الـ connections الحقيقية. أو drivers على HTTP زي بتاعة Neon، مفيهاش connection مفتوح أصلًا.
التكلفة، وإمتى الـ serverless مناسب. بتدفع على عدد الـ invokes، وعلى المدة بالـ millisecond مضروبة في الـ memory. يعني لو مفيش traffic، مفيش فلوس. وده ممتاز لحاجات زي: webhooks، و jobs بتيجي على فترات، و image processing، و APIs الـ traffic بتاعها متقطع. بس فيه حاجات مش مناسبة: traffic عالي على طول: container شغال أربعة وعشرين ساعة بيبقى أرخص بكتير. مهمة طويلة أكتر من الـ timeout. WebSockets و connections مفتوحة. أي حاجة محتاجة state في الـ memory أو cache محلي كبير. والـ latency اللي ما تستحملش cold start. وكمان: الـ disk اللي في tmp مؤقت ولكل instance لوحده، فما تحفظش فيه حاجة مهمة. والـ logs بتتبعت لوحدها من الـ stdout، وكل invoke ليه requestId، حطه في كل log line. علشان نشوف كل ده بعينينا من غير ما نحتاج AWS، هنكتب simulator صغير للـ runtime بـ Node: بيعمل instances، و cold start بوقت، و freeze بعد الـ return، و timeout بيرمي الـ instance، و reap للـ instances الفاضية، و provisioned concurrency. وعليه هنشغّل handler حقيقي للـ orders زي اللي هيتعمل في الإنتاج بالضبط. والـ freeze في الـ simulator معمول بـ context dot defer: أي مهمة تحطها هناك هو الشغل اللي كان هيكمّل بعد الـ return، والـ simulator ما بيشغّلهوش غير لما الـ instance يصحى في الـ invoke الجاي، ولو اتشال قبلها بيضيع. ده بالضبط اللي بيحصل في الحقيقة.
التحدي. عندك lab dot js بيجرّب الـ runtime والـ handler في خمس مواقف، و test dot js. والمطلوب lambda dot js. كل التفاصيل في الكومنت: createRuntime بالـ boot والـ invoke والـ thaw والـ context والـ timeout والـ reap والـ provision، و createOrderHandler بالـ lazy connection والـ reconnect والـ idempotency والـ partial batch والوقوف قبل الـ deadline، و createMemoryStore، و connectionBudget. اقرا الكومنت كويس، وخصوصًا ترتيب الحاجات جُوّه invoke، ونشغّل test dot js لحد ما تبقى أربعتاشر من أربعتاشر.
اكتب lambda.js: createRuntime بـ invoke (اختيار الأدفى، و boot، و THROTTLED، و thaw للـ deferred، والـ context، و race مع الـ timeout) و reap و provision و stats و instances، و createOrderHandler بالـ lazy getDb والـ reconnect والـ idempotency والـ batchItemFailures والـ safetyMs والـ shutdown، و createMemoryStore، و connectionBudget. كل التفاصيل في الكومنت، وشغّل node test.js.
الحل. الـ invoke: reap الأول، وبعدين instance فاضي أدفى واحد، أو boot، أو throttle. وبعد الـ ready، الـ deferred الأول، وبعدين الـ handler في race مع الـ timeout. ولو الـ timeout كسب، الـ instance يتشال من الـ pool خالص، زي الحقيقة. الـ handler: الـ connection lazy ومحفوظ كـ promise، ولو اتقطع يرجع null. وقبل كل رسالة نسأل عن الوقت الفاضل، وبعدين الـ idempotency، والـ parse والـ save جُوّه try، فرسالة بايزة ما تبوّزش الباقي. والـ add بعد الـ save. نشغّل lab dot js.
// lambda.js: simulator لـ serverless runtime (cold و warm و timeout و freeze و reap)، و handler لـ SQS بالـ partial batch والـ idempotency
// lab.js و test.js بيعملوا import من هنا: createRuntime و createOrderHandler و createMemoryStore و connectionBudget
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export function createRuntime({ init, coldStartMs = 0, timeoutMs = 1000, idleMs = 60000, maxInstances = 10, now = Date.now }) {
const pool = [];
const stats = { coldStarts: 0, invocations: 0, throttles: 0, timeouts: 0, reaped: 0 };
let nextInstance = 0, nextRequest = 0;
function boot() {
const inst = { id: `i${++nextInstance}`, busy: true, lastUsed: now(), deferred: [], handler: null, shutdown: null };
stats.coldStarts++;
inst.ready = (async () => {
await sleep(coldStartMs);
const made = await init({ instanceId: inst.id });
if (typeof made === "function") inst.handler = made;
else { inst.handler = made.handler; inst.shutdown = made.shutdown ?? null; }
})();
pool.push(inst);
return inst;
}
function destroy(inst) {
const i = pool.indexOf(inst);
if (i !== -1) pool.splice(i, 1);
inst.deferred = [];
}
async function reap() {
const t = now();
for (const inst of [...pool]) {
if (!inst.busy && t - inst.lastUsed >= idleMs) {
destroy(inst);
stats.reaped++;
if (inst.shutdown) await inst.shutdown();
}
}
}
async function invoke(event) {
await reap();
const idle = pool.filter((x) => !x.busy).sort((a, b) => b.lastUsed - a.lastUsed);
let inst = idle[0], cold = false;
if (inst) inst.busy = true;
else if (pool.length < maxInstances) { inst = boot(); cold = true; }
else { stats.throttles++; return { ok: false, error: "THROTTLED" }; }
stats.invocations++;
const started = now();
let timer;
try {
await inst.ready;
const thaw = inst.deferred.splice(0);
for (const fn of thaw) { try { await fn(); } catch {} }
const deadline = now() + timeoutMs;
const context = {
requestId: `r${++nextRequest}`,
instanceId: inst.id,
deadline,
getRemainingTimeInMillis: () => Math.max(0, deadline - now()),
defer: (fn) => inst.deferred.push(fn),
};
const timeout = new Promise((r) => { timer = setTimeout(() => r({ timedOut: true }), timeoutMs); });
const work = Promise.resolve().then(() => inst.handler(event, context)).then((result) => ({ result }));
const out = await Promise.race([work, timeout]);
if (out.timedOut) {
stats.timeouts++;
destroy(inst);
return { ok: false, error: "TIMEOUT", instanceId: inst.id, cold, durationMs: now() - started };
}
return { ok: true, result: out.result, instanceId: inst.id, cold, durationMs: now() - started };
} catch (e) {
return { ok: false, error: e.message, instanceId: inst.id, cold, durationMs: now() - started };
} finally {
…المخرجات
$ node lab.js
--- 1) cold start و warm ---
init على i1: بنحمّل الـ config ونفتح الـ connections مرة واحدة
invoke 1: cold على i1 في 121ms
invoke 2: warm على i1 في 0ms
invoke 3: warm على i1 في 1ms
init على i2: بنحمّل الـ config ونفتح الـ connections مرة واحدة
init على i3: بنحمّل الـ config ونفتح الـ connections مرة واحدة
init على i4: بنحمّل الـ config ونفتح الـ connections مرة واحدة
أربعة في نفس اللحظة: i1(warm) i2(cold) i3(cold) i4(cold)
stats: {"coldStarts":4,"invocations":7,"throttles":0,"timeouts":0,"reaped":0}
--- 2) الشغل اللي بعد الـ return (fire and forget) ---
الـ handler رجّع، والـ instance اتجمّد، والإيميل لسه ما اتبعتش
بعد تانية جه invoke تاني:
[14:47:21] الإيميل بتاع o1 اتبعت أخيرًا
--- 3) timeout ---
TIMEOUT بعد 101ms، والـ instance i1 اترمى. instances دلوقتي: 0
--- 4) handler الـ orders على queue فيها retries و DLQ ---
connect للـ database (مرة واحدة لكل instance)
round 1: batch [m1,m2,m3,m4] ← failures [m2,m3]
round 2: batch [m2,m3] ← failures [m2]
round 3: batch [m2] ← failures [m2]
saved: [o1,o4,o3] DLQ: [m2]
--- 5) الـ connections والـ database ---
pool 10 × concurrency 1000: محتاج 10000 والمتاح 90 ← مش هينفع، أقصى concur
…الأول: أول invoke cold واخد وقت الـ init، والتانيين warm في صفر تقريبًا. ولما جم أربعة مع بعض، واحد بس لقى instance دافي، والتلاتة التانيين cold، كل واحد بالـ init بتاعه. التاني: الـ handler رجّع، والإيميل ما اتبعتش. واتبعت بعد تانية، لما جه invoke تاني، وده بالضبط اللي كان بيحصل في الإنتاج. التالت: الـ timeout قتل الـ invoke والـ instance اترمى. الرابع: الرسالة البايزة والـ deadlock بس رجعوا. الـ deadlock نجح في المرة التانية، والبايزة راحت الـ DLQ بعد تلات مرات، والـ orders السليمة اتحفظت مرة واحدة بس. والخامس: pool عشرة مع ألف concurrency محتاج عشرة آلاف connection، و pool واحد محتاج ألف، والاتنين مش هينفعوا من غير proxy. ونشغّل test dot js.
03 / دروس المسار (122)
- 01أهلًا بيك: يعني إيه backend، والكورس ده ماشي إزاي
- 02الملفات والفولدرات والـ terminal: تتحرك على جهازك زي المبرمجين
- 03Node وأول برنامج: تشغّل وتقرا رسالة الخطأ
- 04القيم والمتغيرات والأنواع: const و let و typeof
- 05القرارات والتكرار: if و === والـ truthy والـ loops
- 06الـ functions: parameters و return والـ arrow functions والـ scope
- 07الـ objects والـ arrays: destructuring و spread و map و filter و reduce
- 08JSON والنصوص والتواريخ: اللغة اللي الـ backend بيتكلم بيها
- 09الـ callbacks والـ closures وأول لمسة async: setTimeout
- 10الـ classes والـ errors: throw و try/catch و stack trace
- 11git: تحفَظ كل خطوة وترجعلها، وما تسرّبش أسرارك
- 12ESM و CommonJS و package exports: Node بيلاقي الـ modules إزاي
- 13الـ Promises و async/await: sequential و parallel والأخطاء والإلغاء
- 14الـ event loop والـ microtasks: Node بيعمل كل حاجة بـ thread واحد إزاي
- 15الـ Streams والـ Backpressure: ملفات أكبر من الـ memory
- 16الـ File System والـ paths: قراية وكتابة صح، ومن غير path traversal
- 17الـ Buffers والبيانات الـ binary: الـ bytes من جُوّه
- 18Crypto APIs والأسرار: random و hash و HMAC و AES-GCM
- 19الـ Worker Threads: شغل CPU تقيل من غير ما توقف الـ server
- 20الـ Errors على مستوى الـ process: operational و programmer و crash-only
- 21الـ Node test runner والـ debugging: tests من غير مكتبات
- 22HTTP من جُوّه: الـ methods والـ status codes والـ headers
- 23Express والـ Routing: الـ params والـ query والترتيب والـ Router
- 24Express 5: إيه اللي اتغيّر، وإزاي تنقل مشروع قديم
- 25الـ Middleware ودورة حياة الـ request جُوّه Express
- 26Validation للمدخلات بـ Zod: ما تصدّقش أي حاجة جاية من برة
- 27تصنيف الأخطاء و Problem Details: شكل واحد لكل الـ errors
- 28الـ CORS والـ Cookies: المتصفح بيحمي المستخدم إزاي
- 29رفع الملفات وحدود الحجم: multer من غير ثغرات
- 30الـ Realtime: Server-Sent Events و WebSockets
- 31OpenAPI والـ API Versioning: عقد مكتوب، وتغيير من غير ما تكسر حد
- 32PostgreSQL: الـ DDL وأنواع البيانات الصح لكل عمود
- 33الـ Constraints: PK و FK و UNIQUE و CHECK — الـ database بتحمي نفسها
- 34الـ Normalization والـ ERD: تقسّم البيانات صح من أول يوم
- 35الـ JOINs والتجميع: INNER و LEFT و GROUP BY و HAVING و window functions
- 36JSONB في PostgreSQL: إمتى تستخدمه، وإزاي تسأله وتعمله index
- 37الـ Indexes من جُوّه: B-tree و GIN و BRIN
- 38Partial و Covering indexes: indexes أصغر وأسرع
- 39الـ Pagination بالـ Keyset: ليه OFFSET بيبطّأ وبيكرر
- 40Full Text Search في Postgres: بحث بالكلمات، بالعربي والإنجليزي
- 41EXPLAIN ANALYZE والـ Planner: اقرا الـ plan وصلّح الـ query
- 42الـ Transactions بـ node-postgres: كله أو ولا حاجة
- 43الـ MVCC ومستويات الـ Isolation: transactions كتير في نفس الوقت
- 44الـ Locks والـ Deadlocks: مين مستني مين
- 45SKIP LOCKED و Job Leasing: Postgres كـ job queue
- 46Row Level Security والـ Multi-tenancy: كل عميل يشوف بياناته بس
- 47Migrations من غير downtime: expand و backfill و contract
- 48الـ Replication والـ Read Replicas: نسخ حية من الـ database
- 49الـ Connection Pooling و PgBouncer: connections أقل، requests أكتر
- 50الـ Partitioning والـ VACUUM والمراقبة: Postgres على المدى الطويل
- 51Redis وأنواع البيانات: strings و hashes و lists و sets و sorted sets
- 52Cache-aside والـ TTL: Redis قدام الـ database
- 53الـ Cache invalidation والـ Stampede: لما الـ key الشهير يموت
- 54Lua والـ Atomicity في Redis: كام أمر كأنهم أمر واحد
- 55الـ Rate Limiting في Redis: fixed window و sliding window و token bucket
- 56الـ Sessions في Redis: تسجيل دخول تقدر تلغيه في أي وقت
- 57Redis Streams والـ Consumer Groups: log بيتوزع على workers
- 58Pub/Sub و BullMQ: إشارات بين الـ instances، وطوابير شغل محترمة
- 59Redis لما يقع: الـ persistence (RDB و AOF) والـ replication
- 60Redis في الإنتاج: الـ ACLs والـ Cluster والمراقبة
- 61تخزين الـ Passwords بـ Argon2id: صح من أول مرة
- 62Session Fixation والـ Rotation: id جديد مع كل صلاحية جديدة
- 63الـ Cookie Flags والـ CSRF: المتصفح بيبعت الـ cookie، فلازم تتأكد مين اللي طلب
- 64الـ XSS والـ Content Security Policy: ما تخليش النص يتحوّل لـ code
- 65الـ OAuth و OpenID Connect و PKCE: login بـ Google من غير ما تشوف الـ password
- 66التأكد من الـ JWT صح: التوقيع والـ claims والـ scopes
- 67الـ BOLA و BFLA: الـ token السليم مش معناه إن الطلب مسموح
- 68الـ RBAC و ABAC والملكية: policy واحدة بدل ifs متفرقة
- 69الـ MFA والـ recovery: التاني factor اللي بجد
- 70الـ Passkeys و WebAuthn: login من غير password وضد الـ phishing
- 71الـ Threat Modeling بـ STRIDE: فكّر زي المهاجم قبل ما تكتب code
- 72الـ SQL Injection والـ Parameterization: الـ data ما تبقاش code
- 73الـ SSRF والـ Egress: ما تخليش الـ server بتاعك يزور الشبكة الداخلية
- 74الـ Security Headers والـ TLS: كل response يقول للـ browser يحمي المستخدم إزاي
- 75توقيع الـ Webhooks والـ Replay: الـ webhook ده جاي فعلًا من Stripe؟
- 76تغيير الأسرار والـ HMAC: إزاي تغيّر مفتاح من غير ما كل حاجة تقع
- 77سلسلة التوريد والـ SBOM: إنت واثق في ألف package ما تعرفهمش؟
- 78الـ Audit والـ Privacy Logs: سجّل مين عمل إيه، من غير ما تسجّل الأسرار
- 79اختبار الأمان و OWASP ASVS: الأمن جُوّه الـ CI، مش audit آخر السنة
- 80الـ Prototype Pollution والـ Resource DoS: request واحد يخلّي الكل admin أو يوقّع الـ server
- 81الـ SLI والـ SLO والـ Error Budget: إمتى تصحّي الـ on-call وإمتى تسيبه نايم
- 82الـ Event Loop Delay والـ ELU: لما Node نفسه يتخنق
- 83الـ Worker Threads والـ CPU Profiling: الشغل التقيل بره الـ event loop، والوقت راح فين
- 84الـ Memory والـ GC والـ Leaks: الـ process اللي بيكبر لحد ما يقع
- 85الـ N+1 Queries والـ DataLoader: صفحة واحدة بمِيّتين query
- 86الـ HTTP Caching والـ Compression: أسرع request هو اللي ما بيوصلش
- 87الـ Load Testing والـ Soak Testing: الـ server بيستحمل قد إيه بجد
- 88الـ Concurrency Limits والـ Bulkheads: dependency بطيئة ما توقعش الـ app كله
- 89الـ Timeouts والـ Retries والـ Circuit Breakers: إزاي تتعامل مع dependency بطيئة أو واقعة
- 90OpenTelemetry والـ Distributed Tracing: الـ request راح فين وضاع وقته فين
- 91Docker Layers والـ Build Context: ليه الـ build بياخد دقيقة وفيه الـ .env بتاعك
- 92Multi-stage Builds و Non-root: image صغيرة ومن غير root
- 93Docker Compose والـ Networks: الـ services بتلاقي بعض إزاي
- 94Docker Volumes: الـ data بتتحفَظ فين
- 95Healthcheck و Restart و Resource Limits: running مش معناها شغّال
- 96BuildKit والـ Build Secrets: الـ token ما يدخلش الـ image
- 97Reverse Proxy و TLS: اللي واقف قدام الـ app
- 98Multi-arch Images والـ Native Modules: اشتغلت عندي
- 99CI/CD و Rollback و Canary: الـ deploy يبقى ممل
- 100Kubernetes من الأول: Pods و Deployments و Services و Probes
- 101Modular Monolith و DDD: حدود واضحة من غير network
- 102Transactional Outbox و Inbox: الـ event ما يضيعش وما يتنفذش مرتين
- 103Sagas: عملية واحدة على تلات services من غير transaction واحدة
- 104CQRS و Event Sourcing: الـ events هي الحقيقة
- 105الـ Queues والـ Brokers: BullMQ و RabbitMQ و Kafka و SQS
- 106Realtime على أكتر من instance: Redis pub/sub و presence و resume
- 107الـ Object Storage والـ Media: presigned URLs و uploads مباشرة
- 108التكامل مع الـ Email والـ Search: retries و webhooks و indexes
- 109GraphQL و gRPC في الإنتاج: contracts و DataLoader و limits و deadlines
- 110Kafka على أكتر من zone و region: الـ replication والـ ISR والـ acks والـ MirrorMaker والـ failover
- 111TypeScript و runtime boundaries: الـ types بتختفي، والـ schemas لأ
- 112ORM ولا SQL: query builders و repositories و migrations و transactions
- 113الـ Serverless من جُوّه: cold starts و freeze و timeouts و connectionsانت هنا
- 114الـ Testing Pyramid: unit و integration و e2e على Postgres حقيقية
- 115Contract Testing و Property-Based Testing بـ fast-check
- 116الـ Payments: idempotency و webhooks و refunds و reconciliation
- 117الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوس
- 118الـ Privacy والـ Data Retention: consent و export و erasure و crypto shredding
- 119الـ ADRs والـ Feature Flags: القرارات المكتوبة والـ rollout من غير deploy
- 120Multi-region active-active: حل الـ conflicts بـ HLC و CRDTs
- 121Readiness و Liveness و Startup Probes: الـ pod جاهز ولا عايش؟
- 122Incident Runbooks: من الـ alert للـ postmortem