السلام عليكم يا جماعة، معكم أخوكم أبو عمر.
قبل كم سنة، كان معنا في الفريق شب اسمه “جهاد”. الله يسهل عليه، جهاد ما كان مجرد مبرمج، كان فنان. كود نظيف، حلول ذكية، وكان هو الشخص اللي بنرجعله في كل كبيرة وصغيرة، خصوصاً في النظام المعقد اللي كان هو أبوه الروحي. كان كل “السّر” عنده، وإحنا كنا مرتاحين ومبسوطين بوجوده.
في يوم من الأيام، دخل جهاد على مكتبي وقال لي بصوت هادئ: “أبو عمر، أنا جاييني عرض ما بقدر أرفضه… وبدي أقدم استقالتي”. شعرت كأن دلواً من الماء البارد قد سُكب فوق رأسي. طبعاً، باركت له وتمنيت له كل الخير، لكن في داخلي كان ناقوس الخطر يدق. خلال الأسبوعين الأخيرين له، حاولنا نعمل “نقل معرفة” (Knowledge Transfer)، لكن كيف يمكن أن تنقل عقل إنسان في أسبوعين؟
بعد رحيله بشهر واحد فقط، وقعت الواقعة. ظهرت مشكلة حرجة في نظام الدفع، وهو الجزء الذي بناه جهاد من الصفر. توقف كل شيء. اجتمع الفريق، فتحنا الكود… ووجدنا أنفسنا في بحر لا نعرف أوله من آخره. لماذا استخدم هذه المكتبة الغريبة (Library) بدلاً من تلك المشهورة؟ ما سر هذا الـ “باترن” (Design Pattern) المعقد الذي لا يبدو له أي داعٍ؟ لماذا يتصل النظام بخدمة خارجية لم نسمع بها من قبل؟
قضينا أسبوعين كاملين، ليل نهار، ليس في حل المشكلة، بل في محاولة فهم “لماذا” تم بناء النظام بهذه الطريقة. كنا كمن يحاول فك شيفرة قديمة بدون مفتاح. كانت تلك الأيام جحيماً حقيقياً، وكلفنا الأمر الكثير من المال والجهد وسمعة الشركة. هذه التجربة القاسية هي التي عرفتني وجهاً لوجه على وحش اسمه “عامل الحافلة”.
ما هو “عامل الحافلة” (The Bus Factor)؟
المصطلح قد يبدو غريباً أو حتى morbid، لكنه يعبر عن فكرة بسيطة وخطيرة جداً في أي فريق عمل.
“عامل الحافلة” هو مقياس لعدد الأشخاص الرئيسيين في فريقك الذين إذا “صدمتهم حافلة” (بشكل مجازي طبعاً، كأن يتركوا العمل فجأة، أو يمرضوا لفترة طويلة)، سيتوقف المشروع تماماً أو سيتعطل بشكل كارثي.
إذا كان عامل الحافلة في فريقك هو “1” (يعني شخص واحد فقط)، فأنت في خطر حقيقي. كان “جهاد” هو الـ “1” في فريقنا. عندما رحل، كأن الحافلة قد صدمت المشروع بأكمله.
المشكلة ليست في وجود خبراء في الفريق، هذا شيء ممتاز. المشكلة هي عندما تتحول الخبرة إلى “معرفة قبلية” (Tribal Knowledge) محصورة في عقول أفراد، لا يمكن الوصول إليها إلا من خلالهم.
الحل السحري الذي أنقذنا: سجلات قرارات المعمارية (ADRs)
بعد كارثة رحيل جهاد، عقدت العزم على ألا يتكرر هذا الأمر مرة أخرى. قرأت كثيراً، وبحثت في تجارب الشركات الأخرى، إلى أن وجدت مفهوماً بسيطاً في مظهره، ولكنه عميق جداً في تأثيره: Architecture Decision Records (ADRs).
ببساطة، الـ ADR هو ملف نصي صغير وموحد الصيغة، يسجل قراراً معمارياً واحداً مهماً تم اتخاذه في المشروع. الفكرة ليست في توثيق “ماذا” فعلنا، فالكود نفسه يوثق ذلك. الفكرة هي في توثيق “لماذا” فعلنا ذلك، وما هي الخيارات الأخرى التي فكرنا بها وتجاهلناها.
لماذا الـ ADRs فعّالة جداً؟
- تسجيل السياق (Context): الـ ADR يجيب على سؤال “لماذا؟”. لماذا اخترنا قاعدة بيانات PostgreSQL بدلاً من MySQL؟ لماذا استخدمنا RabbitMQ بدلاً من Kafka؟ هذا السياق هو الكنز المفقود الذي كنا نبحث عنه في كود جهاد.
- خفيفة وبسيطة: هي مجرد ملفات Markdown بسيطة. لا تحتاج لبرامج معقدة أو أنظمة إدارة محتوى. يمكن لأي شخص أن يقرأها ويكتبها.
- جزء من الكود: أفضل مكان لحفظ الـ ADRs هو في مجلد داخل مستودع الكود نفسه (e.g.,
docs/adrs/). بهذا تصبح جزءاً من تاريخ المشروع، وتخضع لنفس نظام إدارة الإصدارات (Version Control) مثل Git. - تعزيز المشاركة: عندما يصبح اتخاذ القرارات عملية موثقة، يصبح من الأسهل على أعضاء الفريق الجدد والقدامى المشاركة في النقاش وفهم تاريخ المشروع.
كيف تكتب ADR فعال؟ (مثال عملي)
جمال الـ ADRs في بساطتها. لا تحتاج إلى تعقيد الأمور. هذا هو القالب (Template) الذي اعتمدناه في فريقنا، وهو مستوحى من قوالب مشهورة.
قالب ADR بسيط
يمكنك إنشاء ملف جديد باسم 001-record-architecture-decisions.md كأول ADR لك.
# 1. تسجيل قرارات المعمارية
* **الحالة:** مقبول (Accepted)
* **التاريخ:** 2023-10-26
## السياق (Context)
نعاني في المشروع من مشكلة "عامل الحافلة". المعرفة بتاريخ القرارات التقنية المهمة تتركز في عقول عدد قليل من المبرمجين القدامى. عندما يغادر أحدهم أو يكون في إجازة، يواجه باقي الفريق صعوبة في فهم أسباب وجود بعض أجزاء النظام، مما يؤدي إلى بطء في التطوير وخوف من التعديل.
## القرار (Decision)
سنقوم بتبني "سجلات قرارات المعمارية" (Architecture Decision Records - ADRs) كطريقة لتوثيق القرارات التقنية الهامة. كل قرار سيتم توثيقه في ملف Markdown بسيط ومستقل يوضع في مجلد `docs/adrs` في مستودع الكود.
يجب أن يحتوي كل ADR على السياق، القرار المتخذ، والعواقب المترتبة على هذا القرار.
## العواقب (Consequences)
* **إيجابيات:**
* ستصبح عملية اتخاذ القرارات شفافة وموثقة.
* يمكن لأي عضو في الفريق (جديد أو قديم) فهم تاريخ تطور البنية التحتية للمشروع.
* يقل الاعتماد على الأفراد ("عامل الحافلة").
* يسهل مراجعة القرارات المستقبلية في ضوء القرارات السابقة.
* **سلبيات:**
* يضيف خطوة إضافية لعملية اتخاذ القرار (كتابة الملف).
* قد ينسى الفريق تحديث السجلات أو كتابتها إذا لم تصبح جزءاً من ثقافة العمل.
* هناك خطر من الإفراط في التوثيق وكتابة ADRs لقرارات غير مهمة.
مثال على قرار تقني حقيقي
لنفترض أننا نريد اختيار مكتبة للتعامل مع التواريخ والأوقات في مشروع JavaScript.
# 002. استخدام مكتبة Day.js للتعامل مع الوقت والتاريخ
* **الحالة:** مقبول (Accepted)
* **التاريخ:** 2023-11-01
## السياق (Context)
يحتاج تطبيقنا إلى عرض وتنسيق والتعامل مع التواريخ والأوقات في مناطق زمنية مختلفة. التعامل مع كائن `Date` الأصلي في JavaScript معروف بصعوبته وكثرة مشاكله. نحن بحاجة إلى مكتبة موثوقة وصغيرة الحجم لحل هذه المشكلة. الخيارات المطروحة كانت `Moment.js`, `date-fns`, و `Day.js`.
## القرار (Decision)
قررنا اعتماد مكتبة `Day.js` كحل قياسي للتعامل مع التواريخ والأوقات في جميع مشاريع الواجهات الأمامية (Frontend).
**لماذا `Day.js`؟**
1. **حجم صغير جداً:** حوالي 2KB فقط (مقارنة بـ `Moment.js` الذي يعتبر ضخماً ولم يعد يُنصح به). هذا يؤثر إيجاباً على سرعة تحميل الصفحة.
2. **API مشابه لـ `Moment.js`:** العديد من أعضاء فريقنا لديهم خبرة سابقة مع `Moment.js`، لذا سيكون منحنى التعلم قصيراً جداً.
3. **نظام إضافات (Plugins):** المكتبة الأساسية صغيرة، ويمكننا إضافة الميزات التي نحتاجها فقط (مثل التعامل مع المناطق الزمنية) عبر إضافات، مما يحافظ على صغر الحجم.
4. **نشطة ومُصانة جيداً:** المشروع يحظى بشعبية كبيرة ويتم تحديثه باستمرار.
تم استبعاد `date-fns` لأنه على الرغم من قوته، إلا أن أسلوب البرمجة الوظيفية (Functional Programming) الخاص به يتطلب منحنى تعلم أطول للفريق الحالي.
## العواقب (Consequences)
* سيكون لدينا طريقة موحدة وموثوقة للتعامل مع التواريخ في كل مكان.
* أداء التطبيق سيتحسن بسبب الحجم الصغير للمكتبة.
* الفريق سيحتاج إلى التأكد من تضمين الإضافات الصحيحة عند الحاجة لميزات متقدمة (مثل `timezone` أو `relativeTime`).
* أي كود قديم يستخدم `Moment.js` يجب إعادة كتابته تدريجياً لاستخدام `Day.js`.
نصائح أبو عمر الذهبية لتبني الـ ADRs
من تجربتي، هذه بعض النصائح العملية لتجعل هذه العملية تنجح في فريقك:
- ابدأ بسيطاً: لا تفرط في هندسة العملية. القالب الذي عرضته أعلاه أكثر من كافٍ. الأهم هو المحتوى وليس شكل القالب.
- لا توثّق كل شيء: الـ ADRs ليست لتوثيق كل دالة كتبتها. هي للقرارات الكبيرة والمؤثرة التي يصعب تغييرها لاحقاً. القاعدة الجيدة هي: “هل سيحتاج مبرمج بعد سنة أن يعرف لماذا اتخذنا هذا القرار؟”. إذا كانت الإجابة “نعم”، فاكتب ADR.
- اجعلها عملية جماعية: لا تجعل شخصاً واحداً هو المسؤول عن كتابة الـ ADRs. عندما يقترح مبرمج قراراً جديداً، اطلب منه أن يكتب مسودة ADR ويطرحها للنقاش عبر Pull Request. هذا يضمن أن الجميع يشارك في القرار ويفهم أبعاده.
- الالتزام هو المفتاح: في البداية، قد تبدو كتابة ADRs عبئاً إضافياً. لكن مع مرور الوقت، ستجدون أنفسكم تعودون لهذه السجلات مراراً وتكراراً. شجع فريقك على الالتزام بها حتى تصبح عادة وجزءاً من ثقافة الفريق.
الخلاصة… والزبدة
رحيل “جهاد” كان درساً قاسياً، لكنه كان ضرورياً. علمنا أن الأبطال الخارقين في البرمجة يشكلون خطراً على استمرارية المشروع بقدر ما هم مفيدون. المعرفة يجب أن تكون ملكاً للفريق، لا حكراً على فرد.
سجلات قرارات المعمارية (ADRs) لم تكن مجرد أداة تقنية، بل كانت تحولاً ثقافياً في طريقة عملنا. حولتنا من فريق يعتمد على ذاكرة الأفراد إلى فريق يعتمد على ذاكرة مؤسسية موثقة ومتينة. لم نعد نخاف من رحيل أي شخص، لأن المعرفة الأهم – “لماذا” – أصبحت محفوظة للجميع. ✅
نصيحتي لك: لا تنتظر حتى تقع الكارثة. ابدأ اليوم. اكتب أول ADR لفريقك، حتى لو كان عن قرار تبني الـ ADRs نفسها. ستشكرني لاحقاً. 😉