CI/CD و Rollback و Canary: الـ deploy يبقى ممل
الـ deploy مش مستني الـ tests، وبـ latest فمحدش عارف إيه اللي شغّال، واتنين deploy بيجروا مع بعض، والـ rollback معناه build تاني من commit قديم. بنفهم الـ pipeline من test لـ build لـ push بالـ digest لـ deploy، والـ environments والـ approvals، و blue green، و canary بالـ hash والـ analysis الأوتوماتيك، و rollback في دقيقة. والتحدي: deploy.js، و canary حقيقي على نسختين شغّالين، قصاد أربعتاشر حالة.
نص شرح المعلم والكود الذي كتبه ونتيجة تشغيله، بنفس ترتيب الدرس.
أهلًا بيك. الهدف من الدرس ده إن الـ deploy يبقى حاجة مملة. بتحصل أكتر من مرة في اليوم، ومحدش بيقلق. ولو حاجة وقعت، بترجع في دقيقة، مش ساعة. الـ CI، يعني Continuous Integration: كل push بيتعمله test و build أوتوماتيك. والـ CD، Continuous Delivery أو Deployment: الـ image اللي نجحت بتوصل production بنفس الطريقة كل مرة، من غير حد يكتب أوامر بإيده على الـ server. بص على deploy dot naive dot yml، GitHub Actions workflow. المشاكل: الـ deploy job مالوش needs. يعني بيبدأ مع الـ test في نفس الوقت، والـ code بيوصل production حتى لو الـ tests فشلت. الـ image بـ tag latest. latest مش version، ده اسم بيتنقل. بعد الـ deploy محدش يقدر يقول إيه اللي شغّال بالضبط، ولو عايز ترجع، ترجع لإيه؟ و docker pull على اتنين servers في وقتين مختلفين ممكن يجيبوا حاجتين مختلفتين. مفيش environment، يعني مفيش approval، والـ production secrets متاحة لأي job. مفيش concurrency: اتنين push ورا بعض، والـ deploy التاني ممكن يخلص قبل الأول، فالقديم يكسب. و checkout at main: الـ action نفسها ممكن تتغير تحت رجليك. الـ actions دي code بيشتغل ومعاه الـ secrets بتاعتك.
الـ pipeline الصح: test: lint و unit و integration. لو فشل، كله يقف. build: الـ image بتتبني مرة واحدة بس، وتتعمل push بـ tag هو الـ commit sha، والـ registry بيرجّع الـ digest. والـ digest ده hash للمحتوى نفسه، ما بيتغيرش أبدًا. deploy: بياخد الـ digest من الـ build job، ويعمل deploy بيه. نفس الـ bytes اللي اتعملها test هي اللي في production. مش build تاني. build once، deploy many: نفس الـ image تروح staging، وبعدين production، والفرق في الـ config والـ secrets بس. environment production في GitHub: approval من حد، و secrets ما تبانش غير للـ job ده. concurrency group: deploy واحد في نفس الوقت، والتاني يستنى.
طرق الـ deploy: rolling: بتبدّل الـ containers واحد واحد. سهل، بس فيه وقت النسختين شغّالين مع بعض، والـ rollback هو rolling تاني. blue green: نسختين كاملين. blue شغّال، و green بتقوم جنبه بالنسخة الجديدة، وتتعمل smoke test، وبعدين الـ proxy بيتحوّل مرة واحدة. والـ rollback هو إنك ترجّع الـ proxy، في تانية، لأن blue لسه شغّال. canary: النسخة الجديدة بتاخد نسبة صغيرة من الـ traffic، زي خمسة في المِيّة، وبتقارن الـ errors والـ latency بتاعتها بالقديمة. كويسة ← خمسة وعشرين، وبعدين خمسين، وبعدين كله. وحشة ← صفر، أوتوماتيك. والـ canary لازم sticky: نفس الـ user يفضل على نفس النسخة، بالـ hash بتاع الـ user id. وإلا هيشوف UI قديم وجديد بالتبادل، ولو فيه bug مش هتعرف مين اتضرّ.
الـ canary analysis: بنقارن الـ canary بالـ baseline اللي شغّال في نفس الوقت، مش برقم متفق عليه من الأول. لو الـ database بطيئة النهارده، الاتنين هيبقوا بطيئين، ومش غلطة النسخة الجديدة. محتاج عدد requests كفاية في الناحيتين قبل ما تقرّر. خمس requests وواحدة فيهم فشلت معناها عشرين في المِيّة، ومش معناها حاجة. فلو الـ traffic قليل: wait، واستنى. والـ thresholds: الـ error rate مش أعلى من الـ baseline بأكتر من واحد في المِيّة، والـ p95 مش أبطأ بأكتر من عشرين في المِيّة. الـ rollback لازم يبقى جاهز قبل الـ deploy: النسخة القديمة، بالـ digest بتاعها، والأمر اللي يرجّعها. ومهم: الـ database migrations لازم تبقى compatible مع النسختين، وده درس لوحده في الآخر. ده deploy dot yml الجديد. concurrency group على الـ workflow كله. test بـ actions مربوطة بـ version. build عنده needs test، وبيعمل push بالـ commit sha، وبيطلّع الـ digest كـ output. deploy عنده needs build، و environment production، وبيعمل deploy بالـ digest، و canary بخطوات، و promote، ولو أي step فشلت، step بـ if failure بتعمل rollback. و deploy dot sh: blue green حقيقي على server واحد بـ compose و nginx. الـ idle بيقوم بالـ digest، و compose up wait لحد ما الـ healthcheck من الدرس اللي فات ينجح، و smoke test، والـ switch بـ nginx reload. والـ rollback سطرين، لأن القديم لسه شغّال. و workflow dot js و workflow dot naive dot js نفس الـ YAML كـ JS objects.
lab dot js بيشغّل canary حقيقي: الـ stable شغّال، ونسختين جداد: واحدة فيها bug في حساب الـ EGP، والتانية متصلّحة. و router بيقسم ستة آلاف request في كل خطوة بالـ hash، والـ analysis بيقرّر. وبعدين الـ stickiness، والـ releases، والـ lint. التحدي: اكتب deploy dot js. التفاصيل كلها في الكومنت، و test dot js فيه أربعتاشر حالة.
deploy.js: parseImageRef، و bucketOf و routeFor بالـ sha256، و analyze بـ wait و promote و rollback، و runCanary بالخطوات والـ waits، و createReleaseHistory فيه deploy بالـ digest و rollback و keep، و lintWorkflow بـ ACTION-UNPINNED و LATEST-TAG و NO-DIGEST و DEPLOY-WITHOUT-TESTS و NO-ENVIRONMENT و NO-CONCURRENCY.
الحل. routeFor: الـ hash بيدّي كل user رقم من صفر لتسعة وتسعين، ما بيتغيرش. والـ canary هو اللي رقمه أقل من النسبة. فلما النسبة تكبر، الناس بتدخل بس، محدش بيطلع. analyze: الأول عندنا data كفاية؟ وبعدين نقارن. runCanary: loop على الخطوات، وجُوّه كل خطوة loop للـ waits. الـ release history: array و index. الـ rollback بيحرّك الـ index بس، علشان كده سريع. والـ lint: بنمشي على الـ needs recursive علشان نعرف الـ deploy مستني الـ tests ولو بشكل غير مباشر. نشغّل lab dot js.
// deploy.js: الـ image refs بالـ digest، و canary routing و analysis، و release history للـ rollback، و lint للـ CI workflow
import { createHash } from "node:crypto";
export function parseImageRef(ref) {
const [nameTag, digest = null] = String(ref).split("@");
const slash = nameTag.indexOf("/");
const first = slash === -1 ? "" : nameTag.slice(0, slash);
const hasRegistry = /[.:]/.test(first) || first === "localhost";
const registry = hasRegistry ? first : "docker.io";
let rest = hasRegistry ? nameTag.slice(slash + 1) : nameTag;
const colon = rest.lastIndexOf(":");
let tag = null;
if (colon !== -1) { tag = rest.slice(colon + 1); rest = rest.slice(0, colon); }
if (registry === "docker.io" && !rest.includes("/")) rest = `library/${rest}`;
if (!tag && !digest) tag = "latest";
return { registry, repository: rest, tag, digest };
}
export const bucketOf = (key) => createHash("sha256").update(String(key)).digest().readUInt32BE(0) % 100;
export const routeFor = (key, canaryPercent) => (bucketOf(key) < canaryPercent ? "canary" : "stable");
export function analyze(baseline, canary, { minRequests = 200, maxErrorDelta = 0.01, maxP95Ratio = 1.2 } = {}) {
if (canary.requests < minRequests || baseline.requests < minRequests) return { decision: "wait", reasons: [`محتاجين ${minRequests} request على الأقل في كل ناحية`] };
const reasons = [];
const rate = (s) => (s.requests ? s.errors / s.requests : 0);
const be = rate(baseline), ce = rate(canary);
if (ce - be > maxErrorDelta) reasons.push(`errors ${(ce * 100).toFixed(1)}% قصاد ${(be * 100).toFixed(1)}%`);
if (baseline.p95 > 0 && canary.p95 / baseline.p95 > maxP95Ratio) reasons.push(`p95 ${canary.p95}ms قصاد ${baseline.p95}ms`);
return { decision: reasons.length ? "rollback" : "promote", reasons };
}
export async function runCanary(steps, measure, opts = {}) {
const timeline = [];
for (const step of steps) {
for (let attempt = 0; ; attempt++) {
const m = await measure(step.weight);
const a = analyze(m.baseline, m.canary, { ...opts, minRequests: step.minRequests ?? opts.minRequests });
timeline.push({ weight: step.weight, ...a });
if (a.decision === "promote") break;
if (a.decision === "rollback" || attempt >= (opts.maxWaits ?? 3)) return { result: "rolled-back", weight: 0, timeline };
}
}
return { result: "promoted", weight: 100, timeline };
}
export function createReleaseHistory({ keep = 5 } = {}) {
const releases = [];
let current = -1;
return {
deploy(digest, version) {
if (!/^sha256:[0-9a-f]{64}$/.test(digest)) throw new Error(`deploy بالـ digest بس: ${digest}`);
releases.splice(current + 1); // أي release اتعملها rollback بتتشال من قدام
releases.push({ digest, version, at: releases.length + 1 });
current = releases.length - 1;
while (releases.length > keep) { releases.shift(); current--; }
return releases[current];
},
rollback() {
if (current <= 0) throw Object.a
…المخرجات
$ node lab.js
=== canary 1.6.0 ===
5% ← rollback errors 7.2% قصاد 0.3%
النتيجة: rolled-back، والـ canary على 0%
=== canary 1.6.1 ===
5% ← promote
25% ← promote
50% ← promote
100% ← promote
النتيجة: promoted، والـ canary على 100%
=== sticky: نفس الـ user دايمًا على نفس النسخة ===
mahmoud: stable/stable/stable | sara: stable/stable/stable | youssef: canary/canary/canary | karim: canary/canary/canary
من عشر آلاف user على خمسة وعشرين في المِيّة: 2496 canary
=== releases و rollback ===
دلوقتي: 1.6.0 ← rollback ← 1.5.0
الـ history: 1.5.0 ، 1.6.1 ، 1.7.0*
deploy بالـ digest بس: registry.example.com/shop-api:1.7.1
{"registry":"docker.io","repository":"library/node","tag":"22-slim","digest":null}
{"registry":"ghcr.io","repository":"shop/api","tag":"1.6.1","digest":"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"}
=== lint للـ workflows ===
workflow.naive.js:
WARN ACTION-UNPINNED (test): actions/checkout@main: اربطه بـ version أو commit sha
ERROR LATEST-TAG (deploy): الـ deploy بـ latest: مش هتعرف إيه اللي شغّال ولا ترجع لإيه
ERROR DEPLOY-WITHOUT-TESTS (deploy): الـ deploy مش مستني الـ tests
WARN N
…canary واحد ستة صفر: من أول خطوة، خمسة في المِيّة، الـ errors حوالي سبعة في المِيّة قصاد أقل من نص في المِيّة. rollback، والـ canary رجع صفر. يعني الـ bug وصل لخمسة في المِيّة من الـ users بس، ولوقت قصير. واحد ستة واحد: نجح في الأربع خطوات، و promoted. والـ p95 أبطأ شوية، بس جُوّه الـ threshold. الـ sticky: كل user نفس النسخة في التلات مرات، والنسبة من عشر آلاف قريبة أوي من خمسة وعشرين في المِيّة. الـ releases: rollback رجّعنا واحد خمسة، وبعدين deploy شال واحد ستة صفر من الـ history، و keep تلاتة. والـ deploy بـ tag اترفض. والـ lint مسك الخمس مشاكل في الـ naive. ونشغّل test dot js.
المخرجات
$ node test.js ✅ parseImageRef: docker.io و library و latest ✅ parseImageRef: registry بـ port، و digest من غير tag، و tag و digest ✅ bucketOf و routeFor: من صفر لتسعة وتسعين، و sticky، والنسبة قريبة 1983 ✅ routeFor: الـ users اللي في الـ canary على عشرة لسه فيه على خمسين ✅ analyze: wait لو الـ requests قليلة في أي ناحية ✅ analyze: errors أعلى بأكتر من الـ delta ← rollback ✅ analyze: p95 أبطأ من النسبة ← rollback، والاتنين مع بعض، و baseline فاضي ✅ runCanary: كله promote ← promoted، وفشل في الخطوة التانية ← rolled-back على صفر ✅ runCanary: wait بيعيد نفس الخطوة، ولو الـ waits خلصت ← rolled-back، و minRequests لكل step ✅ releases: digest بس، و rollback للي قبله، و NO_PREVIOUS ✅ releases: deploy بعد rollback بيشيل اللي قدام، و keep ✅ lint: ACTION-UNPINNED و LATEST-TAG و NO-DIGEST ✅ lint: DEPLOY-WITHOUT-TESTS بالـ needs غير المباشرة، و NO-ENVIRONMENT و NO-CONCURRENCY ✅ الـ workflows: naive فيه خمس مشاكل، والجديد نضيف 14/14
كله نجح. كل ده على server واحد بـ compose. لما الـ services تكتر والـ servers تكتر، فيه حاجة بتعمل كل ده لوحدها: Kubernetes. الدرس الجاي: الـ Pods والـ Deployments والـ Services، والـ liveness والـ readiness والـ startup probes، والـ requests والـ limits، والـ rolling update، والأوامر اللي هتحتاجها كل يوم.
# .github/workflows/deploy.naive.yml: الـ deploy مش مستني الـ tests، وبـ latest
name: deploy
on:
push:
branches: ["main"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: "actions/checkout@main"
- uses: "actions/setup-node@v4"
with:
node-version: 22
- run: "npm ci && npm test"
deploy:
runs-on: ubuntu-latest
steps:
- uses: "actions/checkout@v4"
- run: "docker build -t registry.example.com/shop-api:latest . && docker push registry.example.com/shop-api:latest"
- run: "./scripts/deploy.sh registry.example.com/shop-api:latest"# .github/workflows/deploy.yml: test ثم build بالـ digest ثم canary، و rollback لو فشل
name: deploy
on:
push:
branches: ["main"]
concurrency:
group: deploy-production
cancel-in-progress: false
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: "actions/checkout@v4"
- uses: "actions/setup-node@v4"
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
digest: "${{ steps.push.outputs.digest }}"
steps:
- uses: "actions/checkout@v4"
- uses: "docker/setup-buildx-action@v3"
- uses: "docker/login-action@v3"
with:
registry: registry.example.com
username: "${{ secrets.REGISTRY_USER }}"
password: "${{ secrets.REGISTRY_TOKEN }}"
- id: push
uses: "docker/build-push-action@v6"
with:
push: true
tags: "registry.example.com/shop-api:${{ github.sha }}"
cache-from: "type=gha"
cache-to: "type=gha,mode=max"
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: "actions/checkout@v4"
- run: "./scripts/deploy.sh canary 5 registry.example.com/shop-api@${{ needs.build.outputs.digest }}"
- run: "node scripts/canary.js --steps 5,25,50,100"
- run: ./scripts/deploy.sh promote
- if: failure()
run: ./scripts/deploy.sh rollback#!/usr/bin/env bash
# scripts/deploy.sh: blue/green على server واحد بـ compose و nginx. نفس الفكرة في أي platform
set -euo pipefail
cmd=$1
active=$(cat /srv/shop/active) # blue أو green
idle=$([ "$active" = blue ] && echo green || echo blue)
case $cmd in
deploy)
image=$2 # registry.example.com/shop-api@sha256:... (digest، مش tag)
echo "$image" > /srv/shop/$idle.image
IMAGE=$image docker compose -p shop-$idle up -d --wait # --wait: لحد ما الـ healthcheck يبقى healthy
./scripts/smoke.sh "http://127.0.0.1:$( [ $idle = blue ] && echo 3001 || echo 3002 )"
ln -sf /etc/nginx/upstreams/$idle.conf /etc/nginx/upstreams/active.conf
nginx -s reload # الـ switch نفسه: فوري، والـ connections القديمة بتخلص عادي
echo "$idle" > /srv/shop/active
echo "live: $idle ($image). الـ $active لسه شغّال للـ rollback"
;;
rollback)
ln -sf /etc/nginx/upstreams/$idle.conf /etc/nginx/upstreams/active.conf # الـ idle هنا هو القديم
nginx -s reload
echo "$idle" > /srv/shop/active
echo "rolled back to $idle ($(cat /srv/shop/$idle.image))"
;;
esac// workflow.naive.js: نفس .github/workflows/deploy.naive.yml كـ JS object
export default {
name: "deploy",
on: { push: { branches: ["main"] } },
jobs: {
test: {
"runs-on": "ubuntu-latest",
steps: [
{ uses: "actions/checkout@main" },
{ uses: "actions/setup-node@v4", with: { "node-version": 22 } },
{ run: "npm ci && npm test" },
],
},
deploy: {
"runs-on": "ubuntu-latest",
steps: [
{ uses: "actions/checkout@v4" },
{ run: "docker build -t registry.example.com/shop-api:latest . && docker push registry.example.com/shop-api:latest" },
{ run: "./scripts/deploy.sh registry.example.com/shop-api:latest" },
],
},
},
};// workflow.js: نفس .github/workflows/deploy.yml كـ JS object
export default {
name: "deploy",
on: { push: { branches: ["main"] } },
concurrency: { group: "deploy-production", "cancel-in-progress": false },
jobs: {
test: {
"runs-on": "ubuntu-latest",
steps: [
{ uses: "actions/checkout@v4" },
{ uses: "actions/setup-node@v4", with: { "node-version": 22, cache: "npm" } },
{ run: "npm ci" },
{ run: "npm test" },
],
},
build: {
needs: "test",
"runs-on": "ubuntu-latest",
permissions: { contents: "read", packages: "write" },
outputs: { digest: "${{ steps.push.outputs.digest }}" },
steps: [
{ uses: "actions/checkout@v4" },
{ uses: "docker/setup-buildx-action@v3" },
{ uses: "docker/login-action@v3", with: { registry: "registry.example.com", username: "${{ secrets.REGISTRY_USER }}", password: "${{ secrets.REGISTRY_TOKEN }}" } },
{ id: "push", uses: "docker/build-push-action@v6", with: { push: true, tags: "registry.example.com/shop-api:${{ github.sha }}", "cache-from": "type=gha", "cache-to": "type=gha,mode=max" } },
],
},
deploy: {
needs: "build",
"runs-on": "ubuntu-latest",
environment: "production",
steps: [
{ uses: "actions/checkout@v4" },
{ run: "./scripts/deploy.sh canary 5 registry.example.com/shop-api@${{ needs.build.outputs.digest }}" },
{ run: "node scripts/canary.js --steps 5,25,50,100" },
{ run: "./scripts/deploy.sh promote" },
{ if: "failure()", run: "./scripts/deploy.sh rollback" },
],
},
},
};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