الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوس

جارٍ التحميل…

01 / ملخص الدرس

الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوس

reminder الساعة تسعة بقى بيوصل الساعة عشرة بعد تغيير الساعة، وتقرير النهارده بيحسب يوم الـ server مش يوم العميل، وساعة اختارها user مش موجودة أصلًا، وفيه ساعة بتحصل مرتين، والـ app بيقول You have 1 items، والعربي عنده ستة أشكال للجمع، والين الياباني ما لوش قروش. بنفهم الوقت صح: UTC للتخزين و timestamptz، والـ IANA timezones، والـ DST والـ gaps والـ overlaps، وحدود اليوم، والجدولة على الساعة اللي على الحيطة، و Temporal.

02 / شرح الدرس مكتوبًا

نص شرح المعلم والكود الذي كتبه ونتيجة تشغيله، بنفس ترتيب الدرس.

أهلًا بيك. النهارده الوقت واللغة. حاجتين شكلهم سهل قوي، ومليانين bugs بتبان مرتين في السنة بس، أو لما أول user من بلد تاني يدخل. بص على reminders dot naive dot js. route بيعمل reminder كل يوم، و route لتقرير النهارده، و route للـ cart بيكتب عدد المنتجات والـ total، و route للـ search في الأسماء. الـ reminder: الـ user كتب الساعة تسعة، و new Date فهمها بالـ timezone بتاع الـ server، والـ server في Frankfurt أو في UTC، مش في القاهرة. وكل مرة بنزوّد أربعة وعشرين ساعة. يوم ما الساعة تتغيّر، الـ reminder يبقى الساعة عشرة أو تمانية. التقرير: setHours صفر يعني نص الليل عند الـ server، والعميل في طوكيو يومه مختلف. ومش كل يوم أربعة وعشرين ساعة. الـ cart: إنجليزي بس، والـ plural بحرف s، و toFixed بتاع اتنين، والين مالوش كسور، والدينار الكويتي ليه تلاتة. والـ search: أحمد بالهمزة و احمد من غيرها مش بيلاقوا بعض، والـ sort بيحط Zeina قبل adam و file10 قبل file9.

أول قاعدة في الوقت. فيه حاجتين مختلفين خالص. instant: حاجة حصلت في حتة واحدة في الزمن، زي الـ order اتعمل، أو الـ payment نجح. دي ليها قيمة واحدة في الدنيا كلها، والـ user في القاهرة والـ user في طوكيو شايفينها نفس اللـ instant، بس كل واحد بيقراها على ساعته. وساعة على الحيطة: زي فكّرني الساعة تسعة الصبح، أو المحل بيفتح الساعة عشرة. دي مش instant، دي ساعة في مكان معيّن، ومعناها بيتغيّر حسب المكان والتاريخ. اللـ instant بتتخزّن UTC. والساعة على الحيطة بتتخزّن هي نفسها، ومعاها الـ timezone.

التخزين في Postgres. استخدم timestamptz، مش timestamp. الاسم بيلخبط: timestamptz ما بيخزّنش timezone، هو بيخزّن instant بـ UTC، وبيحوّل اللي داخل واللي خارج حسب الـ timezone بتاع الـ session. أما timestamp من غير tz فهو ساعة على الحيطة من غير مكان، ولو حطيت فيه instants، كل server بيفهمها بطريقة. خلّي الـ session timezone بتاع الـ app بـ UTC، والـ Node process كمان بـ TZ بـ UTC، فالـ logs والحسابات تبقى واحدة على كل الـ servers. وفي الـ API رجّع ISO strings بـ Z في الآخر، أو بـ offset. أبدًا ما ترجّعش string زي ألفين وستة وعشرين، شهر تلاتة، تسعة الصبح، من غير ما تقول فين.

طب الـ timezone نفسه إيه؟ ناس كتير بتفتكر إن القاهرة يعني زائد اتنين. غلط. القاهرة رجعت للتوقيت الصيفي من ألفين وتلاتة وعشرين، فهي زائد تلاتة في الصيف. ومصر غيّرت قرارها ده أكتر من مرة، وأحيانًا بقرار قبلها بأسابيع. الـ offset رقم في instant معيّنة. والـ timezone زي Africa slash Cairo هو تاريخ كامل من القواعد: الـ offset كان كام، وبيتغيّر إمتى. والتاريخ ده موجود في IANA tz database، اللي Node والـ browsers و Postgres بيستخدموها. فخزّن للـ user الـ IANA name، مش زائد اتنين. وفيه zones الـ offset بتاعها بنص ساعة، زي الهند زائد خمسة ونص، ونيبال زائد خمسة وخمستاشر دقيقة. وخلّي الـ Node و Postgres updated، لأن الـ tz data بتتغيّر كل كام شهر. في JavaScript، إزاي تعرف الساعة كام في zone معيّن؟ الـ Date في JavaScript هو instant بس، رقم milliseconds من سنة ألف وتسعمية وسبعين UTC. و getHours بيدّيك الساعة بالـ timezone بتاع الـ process، وده اللي مش عايزه أبدًا على server. الحل Intl dot DateTimeFormat، وتدّيله timeZone. و formatToParts بيرجّعلك الأجزاء منفصلة: السنة، والشهر، واليوم، والساعة. ومنهم تقدر تحسب الـ offset: خد الأجزاء دي كأنها UTC بـ Date dot UTC، واطرح منها اللـ instant الأصلية، والفرق هو الـ offset. وده اللي هتعمله في zonedParts و offsetMinutes. وخلي بالك، إنشاء الـ formatter غالي، فخزّنه في Map لكل zone.

دلوقتي أصعب حتة: الـ daylight saving. في نيويورك، يوم تمانية مارس ألفين وستة وعشرين، الساعة اتنين الصبح بتنط على طول لتلاتة. يعني الساعة اتنين ونص في اليوم ده مش موجودة. ده اسمه gap. ويوم واحد نوفمبر، الساعة اتنين بترجع لواحدة. فالساعة واحدة ونص بتحصل مرتين، مرة بالتوقيت الصيفي ومرة بالشتوي. ده اسمه overlap. فلو user كتب واحدة ونص في اليوم ده، أنهي واحدة يقصد؟ ولو كتب اتنين ونص، تعمل إيه؟ الإجابة اسمها disambiguation، وفيه أربع اختيارات، وهي نفسها اللي في Temporal. compatible، وده الـ default: في الـ overlap خد الأبدري، وفي الـ gap ازق لقدّام بطول الـ gap، فاتنين ونص تبقى تلاتة ونص. ده نفس اللي بيعمله Temporal والـ calendars. earlier: دايمًا الأبدري. later: دايمًا المتأخر. reject: ارمي error، وده كويس لو الـ user بيدخّل ميعاد بنفسه في form، فتقوله الساعة دي مش موجودة اختار تانية. والطريقة في الـ code: احسب الساعة كأنها UTC، وجرّب الـ offset قبلها بيوم والـ offset بعدها بيوم. ده بيدّيك مرشّحين. ارجع بكل مرشّح للـ zone، واللي بيرجّعلك نفس الساعة هو الصح. اتنين صح يبقى overlap. ولا واحد يبقى gap.

ده بيخلّينا لحاجة بتكسر تقارير كتير: اليوم مش دايمًا أربعة وعشرين ساعة. يوم تغيير الساعة في نيويورك في مارس، اليوم تلاتة وعشرين ساعة، وفي نوفمبر خمسة وعشرين. وفي القاهرة يوم أربعة وعشرين أبريل تلاتة وعشرين ساعة. فتقرير مبيعات النهارده لـ merchant في القاهرة: ما تعملش start زائد أربعة وعشرين ساعة. احسب نص الليل النهارده بتوقيته، ونص الليل بكرة بتوقيته، وكل واحد فيهم instant UTC. وبعدين في Postgres: created_at أكبر من أو قد الـ start، وأصغر من الـ end. نص مفتوح من آخره، عشان ما تحسبش نفس الـ order في يومين. وخلي بالك، اليوم بتاع مين؟ تقرير الـ merchant بتوقيت الـ merchant، والـ dashboard الداخلي ممكن يبقى UTC. قرّر واكتبه. والـ group by باليوم؟ في Postgres تقدر تقول date_trunc بـ day على created_at at time zone Africa slash Cairo، و Postgres بيحسب الـ DST صح، لأن عنده نفس الـ IANA data. والأفضل تدّيله الـ timezone كـ parameter من الـ user، مش تكتبه في الـ query. ولو بتعمل cache أو materialized view لتقارير يومية، الـ key لازم يبقى التاريخ والـ timezone مع بعض، لأن يوم خمستاشر في القاهرة غير يوم خمستاشر في طوكيو.

الجدولة. فكّرني كل يوم الساعة تسعة. غلط: تخزّن instant، وكل مرة تزوّد أربعة وعشرين ساعة. بعد تغيير الساعة، تسعة تبقى عشرة. صح: تخزّن القاعدة نفسها: الساعة تسعة، والـ timezone بتاع الـ user. وكل مرة تحسب الـ occurrence الجاية من الأول: النهارده الساعة تسعة بتوقيته، ولو عدّت خد بكرة، وحوّلها لـ UTC، وحطّها في الـ queue. ولو الساعة اللي اختارها في gap، زي اتنين ونص، compatible بيزقها لتلاتة ونص، أحسن من إنها تضيع. ولو في overlap، earlier عشان ما يتبعتش مرتين. ولو الـ user سافر وغيّر الـ timezone بتاعه، تعيد حساب الـ occurrence الجاية، والـ reminders اللي عدّت ما تتلمسش. و Temporal. الـ Date في JavaScript قديم وفيه مشاكل كتير: الشهور من صفر، ومفيش timezones غير UTC والـ local، والـ parsing بتاعه مختلف. و Temporal هو الـ API الجديد، فيه Instant للـ instant، و ZonedDateTime للـ instant في zone، و PlainDate و PlainTime للساعة على الحيطة من غير zone، والـ disambiguation اللي شرحناه. بدأ ينزل في الـ browsers و Node، وفيه polyfills. النهارده هنكتب الحسابات دي بنفسنا بـ Intl، عشان تفهم إيه اللي بيحصل تحت. بس في الـ production، أول ما Temporal يبقى متاح عندك، استخدمه، أو library معروفة زي date-fns-tz أو Luxon. ما تكتبش date library بنفسك في مشروع حقيقي.

ننتقل للغة. الـ browser بيبعت header اسمه Accept-Language، زي ar-SA، وبعده ar بـ q صفر وتسعة، وبعده en بـ q صفر وتمانية. الـ q هو الأولوية. أنت عندك لغات محدّدة، زي en و ar-EG. الـ negotiation: رتّب اللي الـ user عايزه بالأولوية، ولكل واحد دوّر عندك: نفس الـ tag بالضبط، وبعدين نفس اللغة الأساسية. فـ ar-SA يلاقي ar-EG، لأن الاتنين عربي، أحسن من إنه ياخد إنجليزي. q صفر يعني لا. ولو مفيش حاجة، الـ fallback. وبعد كده: لو الـ user اختار لغة بنفسه من الـ settings، دي تكسب على الـ header دايمًا. خزّنها في الـ profile، وما تخمّنش اللغة من الـ IP، لأن فيه مصريين في ألمانيا.

الترجمات. أول حاجة: ما تلزقش strings. You have، وبعدين الرقم، وبعدين item، وبعدين s. ده مستحيل يتترجم، لأن ترتيب الكلام بيختلف من لغة للتانية. الصح: رسالة واحدة كاملة بـ key، فيها placeholders بأسماء، زي عندك count منتج. والمترجم بيحرّك الـ placeholder في المكان اللي يناسب لغته. تاني حاجة: الجمع. الإنجليزي ليه شكلين: one و other. العربي ليه ستة: zero للصفر، و one للواحد، و two للاتنين، و few من تلاتة لعشرة، و many من حداشر لتسعة وتسعين، و other للمِيّة وما بعدها. منتج واحد، منتجين، تلات منتجات، حداشر منتج. وما تكتبش الـ rules دي بإيدك. Intl dot PluralRules بيعرفها لكل لغة. والـ format ده اسمه ICU MessageFormat، وفيه libraries بتعمله كامل. الـ fallback chain: لو الـ user لغته ar-EG، دوّر في ar-EG، وبعدين ar، وبعدين en. فممكن تحط الحاجات المصرية بس في ar-EG، والباقي ييجي من ar. الـ keys الناقصة: لو ترجمة مش موجودة، ما تكسرش الصفحة، اعرض الـ fallback، بس سجّلها. في الـ production دي metric أو log، وفي الـ CI test بيقارن الـ catalogs ببعض، ويفشل لو فيه key في en مش في ar. والـ workflow: المطوّر بيضيف الـ key بالإنجليزي بس، وفيه tool بتطلّع الـ keys الجديدة وتبعتها للمترجمين، زي Crowdin أو Lokalise، وترجع ملفات. وادّي المترجم context: الكلمة دي زرار ولا عنوان، وفيه حد أقصى للطول ولا لأ.

والـ formatting. الأرقام: ألف وتسعمية وتسعة وتسعين ونص، بالإنجليزي فيه comma بين الآلاف و dot للكسور، وفي ألمانيا بالعكس، وفي العربي ممكن تبقى أرقام هندية. Intl dot NumberFormat بيعمل ده. التواريخ: Intl dot DateTimeFormat بالـ locale والـ timeZone مع بعض. والـ API يرجّع ISO، والـ format يحصل عند العرض بس. والفلوس: خزّنها integer بأصغر وحدة، زي ما اتكلمنا في درس الـ payments. بس أصغر وحدة مش دايمًا قرش من مِيّة: الين الياباني مالوش كسور خالص، والدينار الكويتي ليه تلات كسور. فلو قسمت على مِيّة دايمًا، هتعرض ألف وخمسمية ين على إنهم خمستاشر. NumberFormat بـ style currency بيقولك عدد الكسور لكل currency. الـ RTL. العربي من اليمين للشمال، فالـ HTML يبقى فيه dir بـ rtl و lang بـ ar. وفي الـ CSS استخدم margin-inline-start بدل margin-left، فالـ layout يقلب لوحده. والـ backend ليه دور: رجّع اللغة والاتجاه مع الـ response، أو خلّي الـ frontend يعرفهم من الـ locale. وخلي بالك من الكلام المخلوط: اسم منتج إنجليزي جُوّه جملة عربية، أو رقم تليفون. الـ Unicode بيرتّبهم بخوارزمية اسمها bidi، وأحيانًا بيتلخبطوا، فيه characters مخصوص اسمها isolates بتحمي الحتة دي. ده كمان سبب إنك تشوف characters مش باينة في الـ output بتاع NumberFormat بالعربي، زي right-to-left mark.

آخر حاجة: النصوص نفسها. أحمد بالهمزة و احمد من غيرها، أو أحمد بالتشكيل، أو بالتطويل. للإنسان دي نفس الكلمة، للـ computer لأ. وكمان é ممكن تتكتب character واحد، أو e وبعدها accent لوحده، وشكلهم واحد على الشاشة بس الـ bytes مختلفة. الحل الـ Unicode normalization. NFC بيجمعهم في شكل واحد، و NFKD بيفكّهم، فتقدر تشيل الـ marks، يعني التشكيل والهمزة والـ accents. ده للـ search والمقارنة بس، مش للتخزين، خزّن اللي الـ user كتبه زي ما هو، وخزّن جنبه نسخة normalized تعمل عليها index. والترتيب: sort العادي بيرتّب بالـ code points، فالحروف الكبيرة قبل الصغيرة، و file10 قبل file9. Intl dot Collator بيرتّب زي ما الإنسان يتوقع حسب اللغة. وفي Postgres، الـ collations بتعمل نفس الحكاية، وفيه ICU collations. التحدي. عندك lab dot js بيحكي قصة تطبيق reminders عنده users في القاهرة ونيويورك وطوكيو، و test dot js. والمطلوب intl dot js، بـ Intl بس، من غير أي library. كل التفاصيل في الكومنت: zonedParts و offsetMinutes، و zonedToUtc بالأربع disambiguations، و dayRange و startOfDay و addCalendarDays و nextOccurrence، و negotiateLocale، و createTranslator بالـ plurals والـ fallback والـ missing، والفلوس، والـ normalize والـ sort. اقرا الكومنت كويس، ونشغّل test dot js لحد ما تبقى أربعتاشر من أربعتاشر.

تمرين

اكتب intl.js: zonedParts و offsetMinutes بـ Intl.DateTimeFormat، و zonedToUtc بالـ gap والـ overlap (compatible و earlier و later و reject)، و dayRange و startOfDay و addCalendarDays و nextOccurrence، و negotiateLocale بالـ q والـ base، و createTranslator بـ PluralRules والـ fallback chain والـ missing، و minorUnits و formatMoney، و normalizeText و sortByLocale. كل التفاصيل في الكومنت، وشغّل node test.js.

الحل. zonedParts: formatter واحد لكل zone، بـ hourCycle h23 عشان نص الليل يبقى صفر مش أربعة وعشرين. offsetMinutes: الأجزاء كأنها UTC ناقص اللـ instant. zonedToUtc: مرشّحين من الـ offset قبل وبعد بيوم، ونختبر كل واحد. اتنين صح overlap، ولا واحد gap. وكل الحسابات التانية مبنية عليه: نص الليل لليوم ده ولليوم اللي بعده، ونفس الساعة بعد n أيام، والساعة الجاية. ومفيش ولا مرة أربعة وعشرين ساعة متكتوبة. والترجمة: chain من الـ locale والـ base والـ fallback، و PluralRules بالـ locale اللي لقينا فيه الرسالة، مش اللي الـ user طلبه. نشغّل lab dot js.

intl.js
// intl.js: الوقت بالـ timezones والـ DST، واللغة بالـ Accept-Language والترجمات والـ plurals، والفلوس والنصوص بلغة كل user
// lab.js و test.js بيعملوا import من هنا: zonedParts و offsetMinutes و zonedToUtc و dayRange و startOfDay و addCalendarDays و nextOccurrence و negotiateLocale و createTranslator و minorUnits و formatMoney و normalizeText و sortByLocale
const DAY = 86400000;
const fmtCache = new Map();

function partsFormatter(timeZone) {
  if (!fmtCache.has(timeZone)) {
    fmtCache.set(timeZone, new Intl.DateTimeFormat("en-US", {
      timeZone, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit",
      hour: "2-digit", minute: "2-digit", second: "2-digit", weekday: "short",
    }));
  }
  return fmtCache.get(timeZone);
}

export function zonedParts(instant, timeZone) {
  const out = {};
  for (const p of partsFormatter(timeZone).formatToParts(new Date(instant))) {
    if (p.type === "weekday") out.weekday = p.value;
    else if (p.type !== "literal") out[p.type] = Number(p.value);
  }
  return { year: out.year, month: out.month, day: out.day, hour: out.hour, minute: out.minute, second: out.second, weekday: out.weekday };
}

const wallMs = ({ year, month, day, hour = 0, minute = 0, second = 0 }) => Date.UTC(year, month - 1, day, hour, minute, second);

export function offsetMinutes(instant, timeZone) {
  const t = Math.floor(new Date(instant).getTime() / 1000) * 1000;
  return Math.round((wallMs(zonedParts(t, timeZone)) - t) / 60000);
}

export function zonedToUtc(fields, timeZone, { disambiguation = "compatible" } = {}) {
  const local = wallMs(fields);
  const before = offsetMinutes(local - DAY, timeZone) * 60000;
  const after = offsetMinutes(local + DAY, timeZone) * 60000;
  const candidates = [...new Set([local - before, local - after])].sort((a, b) => a - b);
  const valid = candidates.filter((c) => c + offsetMinutes(c, timeZone) * 60000 === local);
  if (valid.length === 2) {
    if (disambiguation === "reject") throw Object.assign(new Error("AMBIGUOUS_TIME"), { code: "AMBIGUOUS_TIME" });
    return new Date(disambiguation === "later" ? valid[1] : valid[0]);
  }
  if (valid.length === 1) return new Date(valid[0]);
  if (disambiguation === "reject") throw Object.assign(new Error("NONEXISTENT_TIME"), { code: "NONEXISTENT_TIME" });
  return new Date(disambiguation === "earlier" ? local - after : local - before);
}

const nextDate = ({ year, month, day }, n = 1) => {
  const d = new Date(Date.UTC(year, month - 1, day + n));
  return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
};

export function dayRange(dateStr, timeZone) {
  const [year, month, day] = dateStr.split("-").map(Number);
  const start = zonedToUtc({ year, month, day }, timeZone);
  const end = zonedToUtc(nextDate({ year, month, day }), timeZone);
  return { start, end, hours: (end - start) / 3600000 };
}

export function startOfDay(instant, timeZone) {
  const { year, month, day } = zonedParts(instant, timeZone);
  return 
…

المخرجات

$ node lab.js
① لحظة واحدة، تلات ساعات حيطة:
  Africa/Cairo: Mon 10:00 (offset 180 دقيقة)
  America/New_York: Mon 3:00 (offset -240 دقيقة)
  Asia/Tokyo: Mon 16:00 (offset 540 دقيقة)
  القاهرة صيف وشتا: 180 / 120

② الساعة اللي مش موجودة والساعة اللي بتتكرر في نيويورك:
  02:30 يوم ما الساعة بتتقدّم ← compatible: 2026-03-08T07:30:00.000Z
  reject ← NONEXISTENT_TIME
  01:30 يوم ما الساعة بترجع ← earlier: 2026-11-01T05:30:00.000Z later: 2026-11-01T06:30:00.000Z

③ اليوم مش دايمًا 24 ساعة:
  2026-03-08 America/New_York: 23 ساعة، من 2026-03-08T05:00:00.000Z لـ 2026-03-09T04:00:00.000Z
  2026-11-01 America/New_York: 25 ساعة، من 2026-11-01T04:00:00.000Z لـ 2026-11-02T05:00:00.000Z
  2026-04-24 Africa/Cairo: 23 ساعة، من 2026-04-23T22:00:00.000Z لـ 2026-04-24T21:00:00.000Z
  2026-06-15 Asia/Tokyo: 24 ساعة، من 2026-06-14T15:00:00.000Z لـ 2026-06-15T15:00:00.000Z
  نفس اللحظة، بداية اليوم في طوكيو: 2026-01-15T15:00:00.000Z وفي نيويورك: 2026-01-15T05:00:00.000Z

④ reminder الساعة تسعة الصبح كل يوم:
  +24h ← 10:00 في نيويورك   |   addCalendarDays ← 9:00
  +24h ← 10:00 في نيويورك   |   addCalendarDays ← 9:00
  الجاي بعد 05:59Z في القاهرة: 2026-06-01T06:00:00.000Z وبعد 06:00Z: 2026-06-02T06:00:00.
…
03 / دروس المسار (122)
  1. 01أهلًا بيك: يعني إيه backend، والكورس ده ماشي إزاي
  2. 02الملفات والفولدرات والـ terminal: تتحرك على جهازك زي المبرمجين
  3. 03Node وأول برنامج: تشغّل وتقرا رسالة الخطأ
  4. 04القيم والمتغيرات والأنواع: const و let و typeof
  5. 05القرارات والتكرار: if و === والـ truthy والـ loops
  6. 06الـ functions: parameters و return والـ arrow functions والـ scope
  7. 07الـ objects والـ arrays: destructuring و spread و map و filter و reduce
  8. 08JSON والنصوص والتواريخ: اللغة اللي الـ backend بيتكلم بيها
  9. 09الـ callbacks والـ closures وأول لمسة async: setTimeout
  10. 10الـ classes والـ errors: throw و try/catch و stack trace
  11. 11git: تحفَظ كل خطوة وترجعلها، وما تسرّبش أسرارك
  12. 12ESM و CommonJS و package exports: Node بيلاقي الـ modules إزاي
  13. 13الـ Promises و async/await: sequential و parallel والأخطاء والإلغاء
  14. 14الـ event loop والـ microtasks: Node بيعمل كل حاجة بـ thread واحد إزاي
  15. 15الـ Streams والـ Backpressure: ملفات أكبر من الـ memory
  16. 16الـ File System والـ paths: قراية وكتابة صح، ومن غير path traversal
  17. 17الـ Buffers والبيانات الـ binary: الـ bytes من جُوّه
  18. 18Crypto APIs والأسرار: random و hash و HMAC و AES-GCM
  19. 19الـ Worker Threads: شغل CPU تقيل من غير ما توقف الـ server
  20. 20الـ Errors على مستوى الـ process: operational و programmer و crash-only
  21. 21الـ Node test runner والـ debugging: tests من غير مكتبات
  22. 22HTTP من جُوّه: الـ methods والـ status codes والـ headers
  23. 23Express والـ Routing: الـ params والـ query والترتيب والـ Router
  24. 24Express 5: إيه اللي اتغيّر، وإزاي تنقل مشروع قديم
  25. 25الـ Middleware ودورة حياة الـ request جُوّه Express
  26. 26Validation للمدخلات بـ Zod: ما تصدّقش أي حاجة جاية من برة
  27. 27تصنيف الأخطاء و Problem Details: شكل واحد لكل الـ errors
  28. 28الـ CORS والـ Cookies: المتصفح بيحمي المستخدم إزاي
  29. 29رفع الملفات وحدود الحجم: multer من غير ثغرات
  30. 30الـ Realtime: Server-Sent Events و WebSockets
  31. 31OpenAPI والـ API Versioning: عقد مكتوب، وتغيير من غير ما تكسر حد
  32. 32PostgreSQL: الـ DDL وأنواع البيانات الصح لكل عمود
  33. 33الـ Constraints: PK و FK و UNIQUE و CHECK — الـ database بتحمي نفسها
  34. 34الـ Normalization والـ ERD: تقسّم البيانات صح من أول يوم
  35. 35الـ JOINs والتجميع: INNER و LEFT و GROUP BY و HAVING و window functions
  36. 36JSONB في PostgreSQL: إمتى تستخدمه، وإزاي تسأله وتعمله index
  37. 37الـ Indexes من جُوّه: B-tree و GIN و BRIN
  38. 38Partial و Covering indexes: indexes أصغر وأسرع
  39. 39الـ Pagination بالـ Keyset: ليه OFFSET بيبطّأ وبيكرر
  40. 40Full Text Search في Postgres: بحث بالكلمات، بالعربي والإنجليزي
  41. 41EXPLAIN ANALYZE والـ Planner: اقرا الـ plan وصلّح الـ query
  42. 42الـ Transactions بـ node-postgres: كله أو ولا حاجة
  43. 43الـ MVCC ومستويات الـ Isolation: transactions كتير في نفس الوقت
  44. 44الـ Locks والـ Deadlocks: مين مستني مين
  45. 45SKIP LOCKED و Job Leasing: Postgres كـ job queue
  46. 46Row Level Security والـ Multi-tenancy: كل عميل يشوف بياناته بس
  47. 47Migrations من غير downtime: expand و backfill و contract
  48. 48الـ Replication والـ Read Replicas: نسخ حية من الـ database
  49. 49الـ Connection Pooling و PgBouncer: connections أقل، requests أكتر
  50. 50الـ Partitioning والـ VACUUM والمراقبة: Postgres على المدى الطويل
  51. 51Redis وأنواع البيانات: strings و hashes و lists و sets و sorted sets
  52. 52Cache-aside والـ TTL: Redis قدام الـ database
  53. 53الـ Cache invalidation والـ Stampede: لما الـ key الشهير يموت
  54. 54Lua والـ Atomicity في Redis: كام أمر كأنهم أمر واحد
  55. 55الـ Rate Limiting في Redis: fixed window و sliding window و token bucket
  56. 56الـ Sessions في Redis: تسجيل دخول تقدر تلغيه في أي وقت
  57. 57Redis Streams والـ Consumer Groups: log بيتوزع على workers
  58. 58Pub/Sub و BullMQ: إشارات بين الـ instances، وطوابير شغل محترمة
  59. 59Redis لما يقع: الـ persistence (RDB و AOF) والـ replication
  60. 60Redis في الإنتاج: الـ ACLs والـ Cluster والمراقبة
  61. 61تخزين الـ Passwords بـ Argon2id: صح من أول مرة
  62. 62Session Fixation والـ Rotation: id جديد مع كل صلاحية جديدة
  63. 63الـ Cookie Flags والـ CSRF: المتصفح بيبعت الـ cookie، فلازم تتأكد مين اللي طلب
  64. 64الـ XSS والـ Content Security Policy: ما تخليش النص يتحوّل لـ code
  65. 65الـ OAuth و OpenID Connect و PKCE: login بـ Google من غير ما تشوف الـ password
  66. 66التأكد من الـ JWT صح: التوقيع والـ claims والـ scopes
  67. 67الـ BOLA و BFLA: الـ token السليم مش معناه إن الطلب مسموح
  68. 68الـ RBAC و ABAC والملكية: policy واحدة بدل ifs متفرقة
  69. 69الـ MFA والـ recovery: التاني factor اللي بجد
  70. 70الـ Passkeys و WebAuthn: login من غير password وضد الـ phishing
  71. 71الـ Threat Modeling بـ STRIDE: فكّر زي المهاجم قبل ما تكتب code
  72. 72الـ SQL Injection والـ Parameterization: الـ data ما تبقاش code
  73. 73الـ SSRF والـ Egress: ما تخليش الـ server بتاعك يزور الشبكة الداخلية
  74. 74الـ Security Headers والـ TLS: كل response يقول للـ browser يحمي المستخدم إزاي
  75. 75توقيع الـ Webhooks والـ Replay: الـ webhook ده جاي فعلًا من Stripe؟
  76. 76تغيير الأسرار والـ HMAC: إزاي تغيّر مفتاح من غير ما كل حاجة تقع
  77. 77سلسلة التوريد والـ SBOM: إنت واثق في ألف package ما تعرفهمش؟
  78. 78الـ Audit والـ Privacy Logs: سجّل مين عمل إيه، من غير ما تسجّل الأسرار
  79. 79اختبار الأمان و OWASP ASVS: الأمن جُوّه الـ CI، مش audit آخر السنة
  80. 80الـ Prototype Pollution والـ Resource DoS: request واحد يخلّي الكل admin أو يوقّع الـ server
  81. 81الـ SLI والـ SLO والـ Error Budget: إمتى تصحّي الـ on-call وإمتى تسيبه نايم
  82. 82الـ Event Loop Delay والـ ELU: لما Node نفسه يتخنق
  83. 83الـ Worker Threads والـ CPU Profiling: الشغل التقيل بره الـ event loop، والوقت راح فين
  84. 84الـ Memory والـ GC والـ Leaks: الـ process اللي بيكبر لحد ما يقع
  85. 85الـ N+1 Queries والـ DataLoader: صفحة واحدة بمِيّتين query
  86. 86الـ HTTP Caching والـ Compression: أسرع request هو اللي ما بيوصلش
  87. 87الـ Load Testing والـ Soak Testing: الـ server بيستحمل قد إيه بجد
  88. 88الـ Concurrency Limits والـ Bulkheads: dependency بطيئة ما توقعش الـ app كله
  89. 89الـ Timeouts والـ Retries والـ Circuit Breakers: إزاي تتعامل مع dependency بطيئة أو واقعة
  90. 90OpenTelemetry والـ Distributed Tracing: الـ request راح فين وضاع وقته فين
  91. 91Docker Layers والـ Build Context: ليه الـ build بياخد دقيقة وفيه الـ .env بتاعك
  92. 92Multi-stage Builds و Non-root: image صغيرة ومن غير root
  93. 93Docker Compose والـ Networks: الـ services بتلاقي بعض إزاي
  94. 94Docker Volumes: الـ data بتتحفَظ فين
  95. 95Healthcheck و Restart و Resource Limits: running مش معناها شغّال
  96. 96BuildKit والـ Build Secrets: الـ token ما يدخلش الـ image
  97. 97Reverse Proxy و TLS: اللي واقف قدام الـ app
  98. 98Multi-arch Images والـ Native Modules: اشتغلت عندي
  99. 99CI/CD و Rollback و Canary: الـ deploy يبقى ممل
  100. 100Kubernetes من الأول: Pods و Deployments و Services و Probes
  101. 101Modular Monolith و DDD: حدود واضحة من غير network
  102. 102Transactional Outbox و Inbox: الـ event ما يضيعش وما يتنفذش مرتين
  103. 103Sagas: عملية واحدة على تلات services من غير transaction واحدة
  104. 104CQRS و Event Sourcing: الـ events هي الحقيقة
  105. 105الـ Queues والـ Brokers: BullMQ و RabbitMQ و Kafka و SQS
  106. 106Realtime على أكتر من instance: Redis pub/sub و presence و resume
  107. 107الـ Object Storage والـ Media: presigned URLs و uploads مباشرة
  108. 108التكامل مع الـ Email والـ Search: retries و webhooks و indexes
  109. 109GraphQL و gRPC في الإنتاج: contracts و DataLoader و limits و deadlines
  110. 110Kafka على أكتر من zone و region: الـ replication والـ ISR والـ acks والـ MirrorMaker والـ failover
  111. 111TypeScript و runtime boundaries: الـ types بتختفي، والـ schemas لأ
  112. 112ORM ولا SQL: query builders و repositories و migrations و transactions
  113. 113الـ Serverless من جُوّه: cold starts و freeze و timeouts و connections
  114. 114الـ Testing Pyramid: unit و integration و e2e على Postgres حقيقية
  115. 115Contract Testing و Property-Based Testing بـ fast-check
  116. 116الـ Payments: idempotency و webhooks و refunds و reconciliation
  117. 117الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوسانت هنا
  118. 118الـ Privacy والـ Data Retention: consent و export و erasure و crypto shredding
  119. 119الـ ADRs والـ Feature Flags: القرارات المكتوبة والـ rollout من غير deploy
  120. 120Multi-region active-active: حل الـ conflicts بـ HLC و CRDTs
  121. 121Readiness و Liveness و Startup Probes: الـ pod جاهز ولا عايش؟
  122. 122Incident Runbooks: من الـ alert للـ postmortem