في مقال هندسة المتطلبات Requirements Engineering عرضنا المفاهيم: الحاجة والحل، وأصحاب المصلحة، والأنشطة، والصيغ، والتتبع، وإدارة التغيير، والمشكلات التي تتكرر في كل مشروع، ووعدنا بأن نأخذ طلب العيادة نفسه ونحوله إلى متطلبات حقيقية. وفي هذا المقال سوف نفعل ذلك خطوة بخطوة، لنفرض أن عيادة أسنان فيها ثلاثة أطباء أرسلت لك رسالة من سطر واحد: «نبغى نظام حجز مواعيد لعيادة»، وسوف نخرج في النهاية بوثيقة من صفحة واحدة يوقعها صاحب العيادة، وملف معايير قبول يعمل كاختبار آلي، وطريقة واضحة لاستقبال أي تغيير بعد التوقيع. والعيادة وأسماء الأطباء والأرقام في هذا المقال مثال للتوضيح، أما القوالب والأوامر والمخرجات فحقيقية ويمكنك نسخها كما هي. وسوف نتناول في هذا المقال ما يلي:
- الأسئلة الأولى التي يثيرها الطلب، وخطة المقابلات مع قالب أسئلة جاهز.
- تحويل ملاحظات المقابلات إلى بيان مشكلة Problem Statement وأهداف قابلة للقياس وقاموس مصطلحات.
- خريطة أصحاب المصلحة Stakeholders للعيادة.
- قصص المستخدم User Stories بأولويات MoSCoW، وقواعد العمل Business Rules، ومعايير القبول بصيغة Gherkin، مع تشغيلها كاختبار حقيقي بأداة behave.
- المتطلبات غير الوظيفية بالأرقام: الأداء والتوافرية والأمن والعربية والخصوصية.
- ما هو خارج النطاق، والافتراضات Assumptions، والقيود Constraints.
- نموذج أولي منخفض الدقة Low-fidelity Prototype لشاشتي الحجز.
- وثيقة SRS من صفحة واحدة بالعربية، وقائمة المراجعة والتوقيع.
- طلب تغيير Change Request بعد التوقيع: من التسجيل وتحليل الأثر إلى الاختبار الذي يفشل ثم ينجح.
الطلب كما وصل، والأسئلة الأولى
الجملة «نبغى نظام حجز مواعيد لعيادة» تبدو واضحة، لكن عندما تقرؤها كمطور سوف يرد في ذهنك خلال دقيقة عشرات الأسئلة: من الذي يحجز، المريض بنفسه أم الاستقبال أم الاثنان؟ هل الحجز من موقع أم تطبيق أم واتساب؟ كم مدة الموعد، وهل تختلف حسب نوع العلاج؟ ماذا يحدث إذا لم يحضر المريض؟ هل يدفع مقدماً؟ أين تحفظ البيانات الآن؟ والخطأ الشائع هنا أن تجيب أنت عن هذه الأسئلة بالتخمين لأنها «منطقية»، والسبب أن كل تخمين يدخل الكود دون أن يراه صاحب العيادة، ثم يكتشفه بعد الإطلاق. لذلك نكتب الأسئلة ولا نجيب عنها، ونرتب طريقة الحصول على الإجابات، والصورة التالية تبين الخطوات الثماني التي سوف نمشي عليها في هذا المقال:

خطة المقابلات Interviews وقالب الأسئلة
قبل أي مقابلة نحدد من سوف نكلمه ولماذا، فإذا قابلنا صاحب العيادة وحده فسوف نسمع أهداف العمل فقط، وإذا تركنا الاستقبال فسوف نفقد أكثر من نصف القواعد الحقيقية. والخطة التالية تأخذ يومين تقريباً من وقت الفريق:
| من | الطريقة | المدة | ماذا نريد أن نعرف |
|---|---|---|---|
| مالك العيادة | مقابلة | 45 دقيقة | أهداف العمل، ومقياس النجاح، والميزانية، والموعد، ومن يقرر |
| طبيب من كل تخصص | مقابلة | 30 دقيقة لكل طبيب | مدة كل نوع موعد، والراحة بين المواعيد، والإجازات، وما يريد أن يراه صباحاً |
| موظفتا الاستقبال | ملاحظة Observation نصف يوم، ثم مقابلة | 4 ساعات و30 دقيقة | كيف يتم الحجز الآن فعلاً، والاستثناءات اليومية، وما يضيع وقتهن |
| خمسة مرضى | مكالمة قصيرة | 10 دقائق لكل مريض | كيف يفضلون الحجز، وما الذي يزعجهم في الطريقة الحالية |
| المحاسب | مقابلة | 20 دقيقة | هل يرتبط الموعد بالفاتورة، وماذا يحتاج من تقارير |
| الوثائق الحالية | تحليل وثائق Document Analysis | ساعة | ملف Excel للمواعيد، ونموذج تسجيل المريض، وأي سياسة مكتوبة للإلغاء |
والقالب التالي هو دليل المقابلة Interview Guide الذي نستخدمه، ولاحظ أنه أسئلة مفتوحة تطلب أمثلة حقيقية، وليس قائمة ميزات نسأل «هل تريدونها؟»:
# دليل مقابلة جمع المتطلبات
المشروع: ............ الشخص: ............ الدور: ............
التاريخ: ............ المقابل: ............ مدة المقابلة: ......
## 1. السياق (5 دقائق)
- صف لي يوم عملك العادي من أوله.
- ما الجزء من عملك الذي له علاقة بالمواعيد؟
## 2. الطريقة الحالية (10 دقائق)
- احك لي عن آخر مرة حجزت فيها موعداً لمريض، خطوة بخطوة.
- أين تكتب الموعد الآن؟ أرني الملف أو الدفتر إن أمكن.
- من غيرك يلمس هذه المعلومة؟
## 3. المشكلات (10 دقائق)
- ما الذي يضيع وقتك أكثر شيء في المواعيد؟
- احك لي عن آخر مرة حدث فيها خطأ في موعد. ماذا حدث بعدها؟
- كم مرة يحدث هذا تقريباً في الأسبوع؟
## 4. القواعد والاستثناءات (10 دقائق)
- ما الذي لا يجب أن يحدث أبداً في الحجز؟
- ماذا تفعل إذا ألغى المريض قبل موعده بقليل؟ إذا لم يحضر؟
- ماذا يحدث لمواعيد الطبيب إذا غاب فجأة؟
## 5. النجاح (5 دقائق)
- بعد ستة أشهر من تشغيل النظام، كيف ستعرف أنه نجح؟
- لو استطعت تغيير شيء واحد فقط، ماذا سيكون؟
## 6. البيانات والقيود (5 دقائق)
- ما البيانات التي تحتاجها عن المريض لحجز موعد؟ وما الذي لا تحتاجه؟
- هل هناك نظام آخر يجب أن يتكامل معه؟ أو جهة يجب أن نلتزم بمتطلباتها؟
## 7. الإغلاق
- هل هناك شيء لم أسأل عنه وكان يجب أن أسأل؟
- من غيرك يجب أن أكلمه؟
- هل يمكنني الرجوع إليك بأسئلة بعد كتابة الملاحظات؟
## ملاحظات المقابل
- الكلمات التي استخدمها كما قالها (للقاموس):
- الحلول التي اقترحها (نسأل عنها: لماذا؟):
- الأرقام التي ذكرها (نتحقق منها):في القالب أعلاه لاحظ التالي:
- السؤال «احك لي عن آخر مرة…» أهم سؤال في القالب، والسبب أن الناس عندما يصفون عملهم بشكل عام يصفون الطريقة الرسمية، وعندما يحكون عن مرة حقيقية تظهر الاستثناءات والطرق المختصرة التي يستخدمونها فعلاً.
- قسم «الحلول التي اقترحها» موجود لأن كل حل يقترحه الشخص يجب أن نسأل خلفه «لماذا؟» حتى نصل إلى الحاجة كما شرحنا في الحاجة أم الحل.
- كلمات الشخص كما قالها تدخل قاموس المصطلحات Glossary، فإذا قالت موظفة الاستقبال «حجز مبدئي» فهذا مصطلح له معنى في عملها ويجب أن نفهمه قبل أن نكتب الكود.
من الملاحظات إلى بيان المشكلة Problem Statement
بعد المقابلات تكون لديك صفحات من الملاحظات، والخطوة التالية أن تجمعها في ملاحظات منظمة، ثم في فقرة واحدة تصف المشكلة. وهذه ملاحظات مختصرة من المثال، بعد تنظيفها وتصنيفها:
ملاحظات المقابلات (مثال توضيحي)
[الاستقبال - ملاحظة نصف يوم]
- الحجز في ملف Excel مشترك، وأحياناً يفتحه شخصان فيضيع تعديل أحدهما.
- معظم المكالمات في الصباح لتغيير موعد أو إلغائه، وليس لحجز جديد.
- عند غياب طبيب تتصل الموظفة بكل مرضاه واحداً واحداً.
- "حجز مبدئي" = موعد محجوز لمريض لم يؤكد بعد (يُلغى إذا لم يؤكد قبل يوم).
[المالك]
- الهدف: تقليل المكالمات على الاستقبال، وتقليل المواعيد التي لا يحضر أصحابها.
- يقترح "تطبيق جوال" -> لماذا؟ -> "المرضى يبغون يحجزون بالليل".
- الموعد يجب أن يكون جاهزاً قبل موسم الإجازات.
[الأطباء]
- مدة الموعد تختلف: كشف 30 دقيقة، تنظيف 45، علاج عصب 60.
- الطبيب يعرف مواعيد يومه من ورقة يطبعها الاستقبال صباحاً.
[المرضى]
- 4 من 5 قالوا إنهم يفضلون الحجز من الجوال دون اتصال.
- مريضة: "أبغى أعرف الأوقات الفاضية بدل ما أسأل وقت وقت".ومن هذه الملاحظات نكتب بيان المشكلة بالقالب التالي، وهو قالب قصير يجبرك على أن تقول من يتأثر وكيف تعرف أنك نجحت قبل أن تذكر أي حل:
# بيان المشكلة
المشكلة: [ما الذي يحدث الآن]
تؤثر على: [من يتأثر]
وأثرها: [ماذا يكلف، بالأرقام إن وجدت]
والحل الناجح سوف: [ماذا يتغير، دون ذكر تقنية]
ونعرف أننا نجحنا عندما: [مقياس واحد أو اثنان، مع القيمة الحالية والمستهدفة]# بيان المشكلة: عيادة الأسنان (مثال)
المشكلة: المواعيد تدار في ملف Excel مشترك وعبر الهاتف فقط، والمرضى
لا يعرفون الأوقات المتاحة إلا بالاتصال أثناء الدوام.
تؤثر على: المرضى، وموظفتي الاستقبال، والأطباء.
وأثرها: أغلب وقت الاستقبال الصباحي في مكالمات تغيير وإلغاء، وتعديلات
تضيع عند فتح الملف من جهازين، والطبيب يعرف جدوله من ورقة مطبوعة.
والحل الناجح سوف: يسمح للمريض أن يرى الأوقات المتاحة ويحجز ويلغي بنفسه
في أي وقت، ويعطي الاستقبال والأطباء جدولاً واحداً محدثاً دائماً.
ونعرف أننا نجحنا عندما: تنخفض مكالمات الحجز والتغيير اليومية من [القيمة
الحالية التي نقيسها في أسبوع] إلى النصف خلال ثلاثة أشهر من الإطلاق،
وتنخفض نسبة المواعيد التي لا يحضر أصحابها من [القيمة الحالية] إلى [الهدف].لاحظ أن البيان لا يذكر «تطبيق جوال» رغم أن المالك اقترحه، لأن السؤال «لماذا؟» أظهر أن الحاجة هي الحجز في أي وقت ومن الجوال، وهذا يتحقق أيضاً بموقع يعمل جيداً على الجوال، والقرار بين الاثنين قرار تصميم وكلفة يأتي لاحقاً. ولاحظ أيضاً أن القيم الحالية فارغة، فلا نخترع رقماً، وإنما نكتب أننا سوف نقيسه في أسبوع قبل البدء، لأن هدفاً بلا قيمة بداية لا يمكن أن تعرف هل تحقق.
ومع بيان المشكلة نبدأ قاموساً صغيراً للمصطلحات، وهو نفس فكرة اللغة المشتركة Ubiquitous Language، أي أن الكلمة التي يقولها الاستقبال هي نفسها التي تظهر في الوثيقة والكود والاختبار:
| المصطلح كما يقوله أهل العمل | الاسم في الكود | التعريف |
|---|---|---|
| فترة | Slot | وقت بداية ونهاية عند طبيب معين، قد تكون متاحة أو محجوزة |
| موعد أو حجز | Booking | فترة محجوزة لمريض واحد |
| حجز مبدئي | TentativeBooking | حجز لم يؤكده المريض بعد، ويلغى تلقائياً إذا لم يؤكد قبل يوم |
| عدم حضور | NoShow | موعد مر وقته ولم يحضر المريض ولم يلغ |
| جدول اليوم | DaySchedule | كل مواعيد طبيب في يوم واحد مرتبة بالوقت |
خريطة أصحاب المصلحة Stakeholder Map
الآن نضع كل من ظهر في المقابلات على خريطة بمحورين: قوته في القرار Power، واهتمامه بالنظام Interest، والهدف أن نعرف من نشركه في كل قرار ومن نكتفي بإبلاغه، ومن قد ننساه. والصورة التالية تبين الخريطة للعيادة:

ونحول الخريطة إلى جدول يبقى مع الوثيقة، لأن الصورة تنسى والجدول يحدث:
| صاحب المصلحة | ماذا يريد من النظام | كيف نشركه | من يمثله |
|---|---|---|---|
| مالك العيادة (الراعي Sponsor) | مكالمات أقل، ومواعيد ضائعة أقل، والتزام بالموعد والميزانية | يعتمد الوثيقة وكل طلب تغيير | المالك نفسه |
| الأطباء | جدول يومي دقيق، والتحكم في ساعات العمل والإجازات | مراجعة القصص التي تخصهم والنموذج الأولي | الطبيب المسؤول |
| الاستقبال | حجز سريع لمن يتصل، وتغييرات أقل يدوياً | ملاحظة عملهم، وتجربة النموذج الأولي | موظفة الاستقبال الأقدم |
| المرضى | رؤية الأوقات المتاحة والحجز والإلغاء في أي وقت | مكالمات قصيرة، واختبار سهولة الاستخدام قبل الإطلاق | عينة من خمسة مرضى |
| مسؤول الالتزام والخصوصية | بيانات أقل، وموافقة واضحة، ومكان حفظ معروف | مراجعة المتطلبات غير الوظيفية للخصوصية والأمن | من يعينه المالك |
قصص المستخدم User Stories وقواعد العمل Business Rules
من الملاحظات وبيان المشكلة نكتب قصص المستخدم، كل قصة برقم ودور وحاجة وسبب، ونعطي كل واحدة أولوية بطريقة MoSCoW التي شرحناها في ترتيب الأولويات:
| الرقم | القصة | الأولوية |
|---|---|---|
| US-01 | بصفتي مريضاً، أريد أن أرى الأوقات المتاحة لطبيب وأحجز أحدها، حتى لا أضطر للاتصال أثناء الدوام | Must |
| US-02 | بصفتي مريضاً، أريد أن ألغي موعدي بنفسي، حتى يستفيد غيري من الوقت ولا أضطر للاتصال | Must |
| US-03 | بصفتي موظفة استقبال، أريد أن أحجز لمريض يتصل بالهاتف من نفس الجدول، حتى لا يكون هناك جدولان | Must |
| US-04 | بصفتي طبيباً، أريد أن أحدد ساعات عملي وإجازاتي، حتى لا يحجز أحد في وقت لست فيه | Must |
| US-05 | بصفتي طبيباً، أريد أن أرى جدول يومي محدثاً على جوالي، حتى لا أعتمد على ورقة مطبوعة | Should |
| US-06 | بصفتي مريضاً، أريد رسالة تذكير قبل موعدي بيوم، حتى لا أنسى | Should |
| US-07 | بصفتي موظفة استقبال، أريد أن أنقل مواعيد طبيب غائب إلى طبيب آخر وأبلغ المرضى دفعة واحدة، حتى لا أتصل بكل مريض | Could |
| US-08 | بصفتي مريضاً، أريد أن أدفع مقدماً عند الحجز | Won't this time |
وبجانب القصص نكتب قواعد العمل Business Rules مستقلة برقم، والسبب أن القاعدة الواحدة تخدم أكثر من قصة، فمنع الحجز المزدوج يطبق على حجز المريض وحجز الاستقبال معاً، وإذا كتبتها داخل قصة واحدة فسوف تنساها في الأخرى:
| الرقم | القاعدة | المصدر |
|---|---|---|
| BR-01 | لا تحجز الفترة الواحدة عند نفس الطبيب لأكثر من مريض | الاستقبال والمالك |
| BR-02 | لا يقبل الحجز إذا بقي على الموعد أقل من ساعتين | الأطباء |
| BR-03 | يسمح بالحجز حتى 30 يوماً قادمة فقط | المالك |
| BR-04 | يسمح للمريض بالإلغاء حتى 4 ساعات قبل الموعد، وبعدها بالاتصال فقط | المالك |
| BR-05 | مدة الموعد حسب نوعه: كشف 30 دقيقة، تنظيف 45، علاج عصب 60 | الأطباء |
ولاحظ أن قاعدة BR-01 هي نفس المشكلة التي كتبنا لها سجل قرار معماري كاملاً في مثال منع الحجز المزدوج، فالمتطلب يقول ماذا يجب أن يكون صحيحاً، وسجل القرار يقول كيف ضمنا ذلك في قاعدة البيانات ولماذا، وكلاهما يحمل نفس الرقم.
بطاقة القصة Story Card كاملة مع معايير القبول
القصة في الجدول سطر واحد، وقبل أن تدخل الدورة Sprint نكتبها كبطاقة كاملة بالقالب التالي، وهو ما تضعه في وصف التذكرة Ticket في نظام إدارة المهام، مثل Taiga إن كنت تستخدمه كما شرحنا في دليل Taiga، مع وسوم بأرقام القواعد:
# US-01: حجز موعد متاح عبر الإنترنت
الأولوية: Must الحالة: جاهزة للتطوير الإصدار: 1.0
القواعد المرتبطة: BR-01, BR-02, BR-03, BR-05
## القصة
بصفتي مريضاً،
أريد أن أرى الأوقات المتاحة لطبيب وأحجز أحدها،
حتى لا أضطر للاتصال بالعيادة أثناء الدوام.
## معايير القبول
1. بفرض أن الفترة متاحة، عندما يحجزها المريض، يتأكد الحجز وتختفي
الفترة من الأوقات المتاحة لغيره. (BR-01)
2. بفرض أن مريضاً حجز الفترة، عندما يحاول مريض آخر حجزها، يرفض
الحجز برسالة «هذا الموعد لم يعد متاحاً». (BR-01)
3. لا يقبل الحجز إذا بقي على الموعد أقل من ساعتين. (BR-02)
4. لا يقبل الحجز بعد 30 يوماً من اليوم. (BR-03)
5. كل النصوص والرسائل بالعربية، والشاشة من اليمين إلى اليسار. (NFR-05)
## خارج هذه القصة
- الدفع (US-08)، ورسالة التذكير (US-06).
## أسئلة مفتوحة
- لا يوجد.
## تعريف الجاهزية (قبل دخول الدورة)
- [ ] معايير القبول مكتوبة بأمثلة ومقبولة من المالك
- [ ] النموذج الأولي للشاشة راجعته موظفة الاستقبال
- [ ] القواعد المرتبطة لها أرقام في الوثيقةمعايير القبول Acceptance Criteria بصيغة Gherkin، وتشغيلها كاختبار
معايير القبول المكتوبة بالعربية في البطاقة يقرؤها المالك، والآن نكتب القواعد المهمة منها بصيغة Gherkin حتى تتحول إلى اختبار آلي Automated Test يعمل مع كل تغيير، ونشغلها بأداة behave لبايثون داخل Container من Docker Image الرسمية لبايثون، فلا تحتاج أن تثبت شيئاً على جهازك. وننشئ مجلداً فيه الملفات التالية:
lab/
├── booking.py قواعد الحجز BR-01 إلى BR-04
└── features/
├── booking.feature معايير القبول بصيغة Gherkin
└── steps/
└── booking_steps.py ربط كل خطوة بالكودأولاً ملف معايير القبول features/booking.feature، ولاحظ الوسوم Tags مثل @US-01 و@BR-01 فوق كل سيناريو، فهي التي تربط الاختبار بالقصة والقاعدة:
Feature: Online appointment booking
As a patient
I want to book a free slot with a doctor online
So that I do not have to call the clinic during working hours
Background:
Given the clock is "2026-10-12 09:00"
And doctor "Dr. Huda" has a free slot at "2026-10-14 10:00"
@US-01 @BR-01
Scenario: Book a free slot
When patient "P-100" books doctor "Dr. Huda" at "2026-10-14 10:00"
Then the booking is confirmed
And the slot "2026-10-14 10:00" of doctor "Dr. Huda" is no longer free
@US-01 @BR-01
Scenario: Two patients cannot take the same slot
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00"
When patient "P-200" books doctor "Dr. Huda" at "2026-10-14 10:00"
Then the booking is rejected with "SLOT_TAKEN"
@US-01 @BR-02 @BR-03
Scenario Outline: Booking window
When patient "P-100" books doctor "Dr. Huda" at "<slot>"
Then the result is "<result>"
Examples:
| slot | result |
| 2026-10-12 10:00 | TOO_LATE |
| 2026-10-12 11:00 | CONFIRMED |
| 2026-11-11 09:00 | CONFIRMED |
| 2026-11-11 10:00 | TOO_FAR |
@US-02 @BR-04
Scenario: Cancel early enough and the slot is free again
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00"
And the clock is "2026-10-14 05:00"
When patient "P-100" cancels the booking at "2026-10-14 10:00"
Then the slot "2026-10-14 10:00" of doctor "Dr. Huda" is free
@US-02 @BR-04
Scenario: Cancel too late
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00"
And the clock is "2026-10-14 07:00"
When patient "P-100" cancels the booking at "2026-10-14 10:00"
Then the cancellation is rejected with "CANCEL_TOO_LATE"ثم الكود الذي يطبق القواعد في booking.py، وهو كود بسيط في الذاكرة لأن هدفنا هنا المتطلبات وليس قاعدة البيانات:
# Booking rules of the clinic example (BR-01 .. BR-04 in the SRS).
from datetime import datetime, timedelta
MIN_NOTICE = timedelta(hours=2) # BR-02: book at least 2 hours before the slot
MAX_AHEAD = timedelta(days=30) # BR-03: book at most 30 days ahead
CANCEL_CUTOFF = timedelta(hours=4) # BR-04: cancel until 4 hours before the slot
class BookingError(Exception):
def __init__(self, code):
super().__init__(code)
self.code = code
class Clinic:
def __init__(self, now):
self.now = now
self.bookings = {} # (doctor, start) -> patient
def book(self, doctor, start, patient):
if start - self.now < MIN_NOTICE:
raise BookingError("TOO_LATE")
if start - self.now > MAX_AHEAD:
raise BookingError("TOO_FAR")
if (doctor, start) in self.bookings: # BR-01: one patient per slot
raise BookingError("SLOT_TAKEN")
self.bookings[(doctor, start)] = patient
def cancel(self, doctor, start, patient):
if self.bookings.get((doctor, start)) != patient:
raise BookingError("NOT_FOUND")
if start - self.now < CANCEL_CUTOFF:
raise BookingError("CANCEL_TOO_LATE")
del self.bookings[(doctor, start)]
def is_free(self, doctor, start):
return (doctor, start) not in self.bookingsثم ملف الخطوات features/steps/booking_steps.py الذي يربط كل جملة في ملف Gherkin بدالة بايثون:
from datetime import datetime
from behave import given, when, then
from booking import Clinic, BookingError
DOCTOR = "Dr. Huda"
def at(text):
return datetime.strptime(text, "%Y-%m-%d %H:%M")
@given('the clock is "{now}"')
def step_clock(context, now):
if not hasattr(context, "clinic"):
context.clinic = Clinic(at(now))
context.clinic.now = at(now)
@given('doctor "{doctor}" has a free slot at "{slot}"')
def step_free_slot(context, doctor, slot):
assert context.clinic.is_free(doctor, at(slot))
@given('patient "{patient}" has booked doctor "{doctor}" at "{slot}"')
def step_booked(context, patient, doctor, slot):
context.clinic.book(doctor, at(slot), patient)
@when('patient "{patient}" books doctor "{doctor}" at "{slot}"')
def step_book(context, patient, doctor, slot):
try:
context.clinic.book(doctor, at(slot), patient)
context.result = "CONFIRMED"
except BookingError as e:
context.result = e.code
@when('patient "{patient}" cancels the booking at "{slot}"')
def step_cancel(context, patient, slot):
try:
context.clinic.cancel(DOCTOR, at(slot), patient)
context.result = "CANCELLED"
except BookingError as e:
context.result = e.code
@then('the booking is confirmed')
def step_confirmed(context):
assert context.result == "CONFIRMED", context.result
@then('the booking is rejected with "{code}"')
@then('the cancellation is rejected with "{code}"')
@then('the result is "{code}"')
def step_result(context, code):
assert context.result == code, f"expected {code}, got {context.result}"
@then('the slot "{slot}" of doctor "{doctor}" is no longer free')
def step_not_free(context, slot, doctor):
assert not context.clinic.is_free(doctor, at(slot))
@then('the slot "{slot}" of doctor "{doctor}" is free')
def step_is_free(context, slot, doctor):
assert context.clinic.is_free(doctor, at(slot))الآن نشغل الاختبارات من داخل مجلد lab:
docker run --rm --name lab-req-behave -v "$PWD":/work -w /work -e PYTHONPATH=/work python:3.12-slim sh -c "pip install -q --root-user-action=ignore --disable-pip-version-check behave==1.3.3 && behave --no-color --format progress"والمخرج سوف يكون كما يلي:
USING RUNNER: behave.runner:Runner
features/booking.feature ........
1 feature passed, 0 failed, 0 skipped
8 scenarios passed, 0 failed, 0 skipped
38 steps passed, 0 failed, 0 skipped
Took 0min 0.044sفي الإعداد أعلاه لاحظ التالي:
- السيناريو
Scenario Outlineمع جدولExamplesيختبر الحدود Boundary Values بالضبط: الساعة 10:00 أقل من ساعتين فترفض، و11:00 ساعتان بالضبط فتقبل، و30 يوماً بالضبط تقبل، وبعدها بساعة ترفض. وهذه الحدود هي أكثر ما يختلف عليه الناس، فإذا قال المالك «ساعتين» فهل يقصد ساعتين أو أكثر؟ الجدول يجبرك على أن تسأل، ويجبره على أن يجيب. - القسم
Backgroundيعمل قبل كل سيناريو، لذلك تبدأ كل حالة من ساعة ثابتة هي 2026-10-12 09:00، والساعة الثابتة هي ما يجعل الاختبار يعطي نفس النتيجة اليوم وبعد سنة. - الثمانية سيناريوهات نجحت، والنقاط الثماني في المخرج هي سيناريو لكل نقطة، وعدد الخطوات 38 لأن خطوتي ال
Backgroundتتكرران في كل سيناريو.
والوسوم تعطيك التتبع Traceability مجاناً، فإذا أردت أن تشغل فقط ما يخص قاعدة الإلغاء:
docker run --rm --name lab-req-behave -v "$PWD":/work -w /work -e PYTHONPATH=/work python:3.12-slim sh -c "pip install -q --root-user-action=ignore --disable-pip-version-check behave==1.3.3 && behave --no-color --format progress --tags=@BR-04"والمخرج سوف يكون كما يلي، حيث تخطى behave ستة سيناريوهات لا تحمل الوسم:
USING RUNNER: behave.runner:Runner
features/booking.feature SSSSSS..
1 feature passed, 0 failed, 0 skipped
2 scenarios passed, 0 failed, 6 skipped
12 steps passed, 0 failed, 26 skipped
Took 0min 0.019sوقد يتساءل البعض: هل يجب أن تكتب كل معايير القبول بصيغة Gherkin؟ والإجابة لا، فاكتب بها قواعد العمل التي لها أمثلة وحدود، مثل الحجز والإلغاء والأسعار والصلاحيات، واترك تفاصيل الواجهة مثل لون الزر ومكانه للنموذج الأولي والمراجعة اليدوية، والسبب أن السيناريو الذي يختبر تفاصيل الواجهة ينكسر مع كل تعديل في التصميم دون أن يحمي قاعدة مهمة. ومكان هذه الاختبارات بين بقية الأنواع شرحناه في اختبار القبول Acceptance Test.
المتطلبات غير الوظيفية Non-functional Requirements بالأرقام
في المقابلات لم يذكر أحد سرعة النظام أو توافره أو أين تحفظ البيانات، ولكن هذه الأسئلة يجب أن نسألها نحن، ونكتب لكل إجابة رقماً وطريقة للتحقق منه، كما شرحنا في اكتب الخاصية برقم وليس بكلمة. والأرقام في الجدول التالي هي ما اتفقنا عليه في المثال مع المالك، وأرقام مشروعك تأتي من حجم عيادتك وميزانيتها:
| الرقم | الخاصية | المتطلب القابل للقياس | كيف نتحقق منه |
|---|---|---|---|
| NFR-01 | الأداء Performance | 95% من طلبات عرض الأوقات المتاحة تعود في أقل من ثانية واحدة عند 50 مستخدماً متزامناً | اختبار أحمال Load Test قبل الإطلاق، كما في اختبار الأحمال |
| NFR-02 | التوافرية Availability | 99.5% شهرياً، والصيانة المخططة بين 1:00 و5:00 فجراً فقط | مراقبة خارجية تسجل التوقف كل دقيقة |
| NFR-03 | النسخ الاحتياطي Backup | لا نفقد أكثر من ساعة من الحجوزات RPO، ونستعيد الخدمة في أقل من 4 ساعات RTO | تجربة استعادة كل ثلاثة أشهر، كما في قاعدة 3-2-1 |
| NFR-04 | الأمن Security | كل حسابات الموظفين بتحقق ثنائي MFA، والمريض يرى ويعدل حجوزاته فقط، والتطبيق يحقق المستوى الثاني من OWASP ASVS | اختبارات صلاحيات آلية ومراجعة كما في مقال ASVS |
| NFR-05 | العربية والاتجاه RTL | كل الشاشات والرسائل بالعربية ومن اليمين إلى اليسار، والتواريخ ميلادية مع الهجري بجانبها، والأوقات بتوقيت الرياض | اختبار مرئي للشاشات، وحالات اختبار للتاريخ والوقت كما في مقال التاريخ والوقت |
| NFR-06 | الخصوصية Privacy | نجمع للحجز الاسم ورقم الجوال فقط، ولا نرسل رسائل التذكير إلا بموافقة صريحة يمكن سحبها، ونحذف بيانات الحجوزات بعد مدة الاحتفاظ التي تعتمدها العيادة | مراجعة الحقول قبل الإطلاق، واختبار سحب الموافقة |
| NFR-07 | سجل التدقيق Audit Trail | كل إنشاء وإلغاء وتعديل لموعد يسجل من فعله ومتى ومن أين، ولا يمكن تعديل السجل | اختبار تكامل، كما في مقال سجل التدقيق |
| NFR-08 | سهولة الاستخدام Usability | 4 من 5 مرضى يستخدمون النظام لأول مرة يكملون الحجز من الجوال في أقل من دقيقتين دون مساعدة | جلسة اختبار مع خمسة مرضى على النموذج ثم على النسخة التجريبية |
| NFR-09 | إمكانية الوصول Accessibility | شاشات المريض تحقق مستوى AA في WCAG 2.2 | فحص آلي ثم مراجعة يدوية بقارئ الشاشة |
وفي الجدول أعلاه لاحظ التالي:
- كل متطلب له طريقة تحقق في نفس السطر، فإذا لم تستطع أن تكتب طريقة التحقق فالمتطلب ما زال غامضاً.
- متطلب الخصوصية NFR-06 مكتوب بلغة يمكن اختبارها، وهو مبني على نظام حماية البيانات الشخصية PDPL الذي يمكنك قراءة نصه في منصة حماية البيانات الشخصية، وبيانات المرضى الصحية من البيانات الحساسة فيه، لذلك يراجع مسؤول الالتزام هذا السطر قبل التوقيع ولا يكتبه المطور وحده.
- المتطلبات الأمنية التفصيلية لا تكتب كلها هنا، وإنما نختار مستوى ASVS ونمر على متطلباته عند كتابة كل قصة، ونكشف ما هو خاص بتصميمنا بجلسة نمذجة التهديدات.
خارج النطاق Out of Scope والافتراضات Assumptions والقيود Constraints
أهم قسم يغفل عنه الناس في الوثيقة هو ما لن نبنيه، والسبب أن كل شيء غير مكتوب كخارج النطاق سوف يعتبره أحدهم داخل النطاق يوم التسليم. لذلك نكتبه صراحة:
## خارج النطاق في الإصدار 1.0
- الدفع الإلكتروني عند الحجز (US-08)، ويعاد النظر فيه بعد ثلاثة أشهر.
- الربط مع شركات التأمين والتحقق من التغطية.
- الملف الطبي للمريض والتشخيص والوصفات، فالنظام للمواعيد فقط.
- أكثر من فرع للعيادة.
- تطبيق جوال يثبت من المتجر، فالإصدار الأول موقع يعمل على الجوال.
- نقل مواعيد طبيب غائب دفعة واحدة (US-07) إذا لم يتسع الوقت.ثم الافتراضات، وكل افتراض معه من يتأكد منه ومتى، لأن الافتراض الذي لا يملكه أحد لا يتأكد منه أحد:
| الرقم | الافتراض | من يتأكد | قبل | إذا كان خطأ |
|---|---|---|---|---|
| A-01 | كل المرضى لديهم رقم جوال يستقبل الرسائل | الاستقبال من ملف المرضى | نهاية الأسبوع الأول | نضيف البريد الإلكتروني كخيار |
| A-02 | ملف Excel الحالي يمكن تصديره إلى CSV بأعمدة ثابتة | المطور مع الاستقبال | نهاية الأسبوع الأول | نضيف مهمة تنظيف بيانات يدوية |
| A-03 | مزود الرسائل يدعم العربية واسم المرسل للعيادة | المطور | قبل بدء US-06 | نختار مزوداً آخر |
| A-04 | مدة كل نوع موعد ثابتة لكل الأطباء | الطبيب المسؤول | قبل بدء US-04 | نجعل المدة إعداداً لكل طبيب |
والقيود Constraints هي ما لا نناقشه: الإطلاق قبل موسم الإجازات، والميزانية المعتمدة، وحفظ البيانات على سيرفرات داخل المملكة بقرار من المالك، مع العلم أن نقل البيانات الشخصية خارج المملكة تنظمه لائحة نقل البيانات الشخصية خارج المملكة، وأن يعمل النظام على متصفحات الجوال الحديثة دون تثبيت.
نموذج أولي منخفض الدقة Low-fidelity Prototype
قبل أن نكتب الوثيقة النهائية نرسم الشاشتين الأهم ونعرضهما على موظفة الاستقبال وعلى مريضين، وهذا هو التحقق بالنموذج الأولي Prototyping الذي يذكره دليل SWEBOK. والنموذج منخفض الدقة عمداً: رمادي، بلا ألوان ولا شعار، والسبب أن الشاشة الجميلة تجعل الناس يعلقون على اللون والخط، بينما الشاشة الرمادية تجعلهم يعلقون على ما يهم، أي الخطوات والمعلومات وترتيبها. والصورة التالية تبين النموذج:

وعند عرض النموذج لا تسأل «هل يعجبك؟»، لأن الإجابة دائماً نعم، وإنما أعط الشخص مهمة: «احجزي موعداً للدكتورة هدى يوم الأربعاء صباحاً»، ثم اصمت وراقب أين يتردد، وبعدها اسأل الأسئلة التالية:
- هل هناك معلومة تحتاجها لتختار ولا تراها في الشاشة؟
- ماذا تتوقع أن يحدث بعد الضغط على «تأكيد الحجز»؟
- إذا أردت إلغاء الموعد بعد يومين، من أين ستبدأ؟
- بالنسبة للاستقبال: كيف ستحجزين لمريض على الهاتف من نفس الشاشة؟
وكل ملاحظة من هذه الجلسة إما تعدل قصة أو معيار قبول، أو تضيف سؤالاً مفتوحاً للوثيقة، وهذا هو السهم المتقطع في دورة الأنشطة الذي يعيدنا من التحقق إلى جمع المتطلبات.
وثيقة SRS من صفحة واحدة
الآن نجمع كل ما سبق في وثيقة واحدة قصيرة، والقالب التالي مبني على الأقسام التي يطلبها المعيار ISO/IEC/IEEE 29148 لمواصفة المتطلبات Requirements Specification، لكنه مختصر لمشروع صغير، والتفاصيل تبقى في القصص والجداول التي تشير إليها الوثيقة بالأرقام. انسخه كما هو واملأه:
# مواصفة المتطلبات: [اسم النظام]
الإصدار: 1.0 التاريخ: .... الحالة: مسودة / معتمدة المالك: ....
## 1. الهدف
[بيان المشكلة في 3 إلى 5 أسطر، ومقياس النجاح بالقيمة الحالية والمستهدفة]
## 2. النطاق
داخل النطاق: [القصص Must وShould بأرقامها]
خارج النطاق: [القائمة الصريحة]
## 3. أصحاب المصلحة
[الجدول: صاحب المصلحة، ماذا يريد، كيف نشركه، من يمثله]
## 4. القاموس
[المصطلح كما يقوله أهل العمل، الاسم في الكود، التعريف]
## 5. المتطلبات الوظيفية
[جدول القصص: الرقم، القصة، الأولوية، رابط التذكرة]
## 6. قواعد العمل
[جدول القواعد: الرقم، القاعدة، المصدر]
## 7. المتطلبات غير الوظيفية
[جدول: الرقم، الخاصية، المتطلب بالأرقام، طريقة التحقق]
## 8. القيود
[ما لا يناقش: الموعد، الميزانية، الاستضافة، الأنظمة، التقنية إن وجد سبب]
## 9. الافتراضات والاعتماديات
[جدول: الرقم، الافتراض، من يتأكد، قبل، إذا كان خطأ]
## 10. أسئلة مفتوحة
[كل سؤال، ومن يجيب، ومتى]
## 11. سجل التغييرات
| الإصدار | التاريخ | التغيير | رقم الطلب | اعتمده |
|---|---|---|---|---|
| 1.0 | .... | الإصدار الأول | - | .... |
## 12. الاعتماد
| الدور | الاسم | التاريخ | القرار |
|---|---|---|---|
| الراعي (مالك المنتج) | | | معتمد / بملاحظات |
| ممثل المستخدمين | | | |
| قائد الفريق التقني | | | |وهذه الوثيقة نفسها بعد ملئها لمثال العيادة، ولاحظ أنها تشير إلى الأرقام ولا تكرر الجداول، والجداول تعيش في المستودع أو نظام إدارة المهام:
# مواصفة المتطلبات: نظام حجز المواعيد للعيادة
الإصدار: 1.0 التاريخ: 2026-10-12 الحالة: معتمدة المالك: مالك العيادة
## 1. الهدف
يرى المريض الأوقات المتاحة ويحجز ويلغي بنفسه في أي وقت، ويعمل الاستقبال
والأطباء على جدول واحد محدث. النجاح: مكالمات الحجز والتغيير اليومية إلى
النصف خلال 3 أشهر من الإطلاق (القيمة الحالية تقاس في أسبوع قبل البدء).
## 2. النطاق
داخل النطاق: US-01 إلى US-06. Could: US-07.
خارج النطاق: الدفع، التأمين، الملف الطبي، تعدد الفروع، تطبيق المتجر.
## 3. أصحاب المصلحة انظر جدول أصحاب المصلحة (5 أطراف).
## 4. القاموس فترة Slot، موعد Booking، حجز مبدئي، عدم حضور، جدول اليوم.
## 5. المتطلبات الوظيفية US-01 إلى US-08 مع أولوياتها.
## 6. قواعد العمل BR-01 إلى BR-05.
## 7. غير الوظيفية NFR-01 إلى NFR-09.
## 8. القيود الإطلاق قبل موسم الإجازات، الميزانية المعتمدة،
الاستضافة داخل المملكة، متصفح الجوال دون تثبيت.
## 9. الافتراضات A-01 إلى A-04.
## 10. أسئلة مفتوحة هل يحق للطبيب حجز وقت لمريض خارج ساعات عمله؟
(الطبيب المسؤول، قبل بدء US-04)
## 11. سجل التغييرات 1.0: الإصدار الأول.
## 12. الاعتماد المالك، والطبيب المسؤول، وقائد الفريق التقني.المراجعة Review والتوقيع Sign-off
التوقيع ليس إجراءً شكلياً، وإنما هو اللحظة التي يصبح فيها للمتطلبات خط أساس Baseline، أي نسخة متفق عليها يقاس عليها أي تغيير بعدها. وقد ذكر تقرير مكتب المحاسبة الحكومي الأمريكي GAO-15-238 عن HealthCare.gov أن المتطلبات لم تكن تعتمد دائماً، وكانت إحدى توصياته أن تعتمد المتطلبات بتوقيع ثلاثة أطراف أساسية هي مالك العمل وصاحب صلاحية الاعتماد في الجهة وصاحب صلاحية الاعتماد عند المقاول. وقبل التوقيع نمر على القائمة التالية مع صاحب العمل وممثل المستخدمين وقائد الفريق التقني:
# قائمة مراجعة المتطلبات قبل التوقيع
## الوضوح
- [ ] لا توجد كلمات غامضة: سريع، سهل، آمن، مرن، تقريباً، إلخ، حسب الحاجة
- [ ] كل متطلب جملة واحدة بقرار واحد، وله رقم فريد
- [ ] كل مصطلح في القاموس، ونفس الكلمة تعني نفس الشيء في كل مكان
## الاكتمال
- [ ] كل صاحب مصلحة في الخريطة له متطلبات، أو مكتوب لماذا لا
- [ ] كل قصة Must لها معايير قبول بأمثلة، تشمل الحالة العادية والحدود والأخطاء
- [ ] المتطلبات غير الوظيفية مكتوبة بالأرقام، ولكل واحد طريقة تحقق
- [ ] الخصوصية والأمن والامتثال راجعها المسؤول عنها
- [ ] خارج النطاق مكتوب صراحة
## الصحة
- [ ] كل متطلب يمثل حاجة، وليس حلاً لم نسأل عنه «لماذا؟»
- [ ] لا يوجد متطلبان متعارضان
- [ ] كل متطلب يمكن بناؤه ضمن الموعد والميزانية
## المخاطر
- [ ] كل افتراض له من يتأكد منه وموعد
- [ ] كل سؤال مفتوح له من يجيب وموعد
- [ ] النموذج الأولي راجعه مستخدمون حقيقيون
## الإدارة
- [ ] الوثيقة لها إصدار ومالك ومكان واحد يعرفه الجميع
- [ ] طريقة طلب التغيير مكتوبة ومعروفةطلب تغيير Change Request بعد التوقيع
بعد أسبوعين من التوقيع يرسل المالك رسالة: «نبغى الإلغاء يكون قبل 24 ساعة بدل 4 ساعات، لأن المرضى يلغون متأخر وما نلحق نعطي الموعد لغيرهم». وهذا طلب معقول تماماً، والسؤال ليس هل نقبله، وإنما كيف ندخله دون أن يكسر شيئاً ودون أن يضيع سببه. والصورة التالية تبين الخطوات الست، ونتيجة تحليل الأثر Impact Analysis لهذا الطلب:

الخطوة الأولى والثانية: تسجيل الطلب وتحليل أثره Impact Analysis
نسجل الطلب برقم في نفس المكان الذي تعيش فيه المتطلبات، بالقالب التالي:
# CR-01: الإلغاء قبل 24 ساعة بدل 4 ساعات
طلبه: مالك العيادة التاريخ: 2026-10-26 المصدر: رسالة واتساب (الرابط)
الحالة: مقبول الأولوية: Must للإصدار 1.1
## الطلب
تغيير مهلة إلغاء المريض لموعده من 4 ساعات إلى 24 ساعة قبل الموعد.
## السبب
المرضى يلغون متأخراً فلا يتسع الوقت لإعطاء الموعد لمريض آخر.
## تحليل الأثر
- المتطلبات: BR-04 (تعديل)، US-02 (معايير القبول)، SRS 1.0 -> 1.1
- الكود: ثابت CANCEL_CUTOFF في booking.py
- الاختبارات: سيناريوهان بالوسم @BR-04
- الواجهة والرسائل: نص شاشة التأكيد، ونص رسالة التذكير
- البيانات: لا يوجد تغيير في قاعدة البيانات
- الجهد: نصف يوم مع الاختبار
- سؤال للقرار: المواعيد المحجوزة قبل التغيير، بأي قاعدة تلغى؟
## القرار
مقبول. المواعيد المحجوزة قبل تاريخ الإطلاق تبقى على قاعدة 4 ساعات.
اعتمده: مالك العيادة، 2026-10-27وتحليل الأثر هنا لم يأخذ أكثر من دقائق، لأن الرقم BR-04 موجود في كل مكان، والأمر التالي داخل مجلد lab يعطينا قائمة الأماكن في الكود والاختبارات مباشرة:
grep -rn "BR-04" features booking.pyوالمخرج سوف يكون كما يلي، وهو قائمة الأثر في الكود والاختبارات: سيناريوهان في ملف Gherkin، وثابت واحد في الكود:
features/booking.feature:34: @US-02 @BR-04
features/booking.feature:41: @US-02 @BR-04
booking.py:1:# Booking rules of the clinic example (BR-01 .. BR-04 in the SRS).
booking.py:6:CANCEL_CUTOFF = timedelta(hours=4) # BR-04: cancel until 4 hours before the slotولاحظ سؤال «المواعيد المحجوزة قبل التغيير» في تحليل الأثر، فهو قرار عمل لم يكن ليظهر لو عدل المطور الرقم في الكود مباشرة عندما وصلته الرسالة، ولو لم يسأل لاكتشف المرضى القاعدة الجديدة عندما يحاولون الإلغاء.
الخطوة الرابعة: نعدل معايير القبول Acceptance Criteria قبل الكود
بعد القرار نعدل الوثيقة إلى الإصدار 1.1، ثم ملف Gherkin قبل أي سطر كود، فنضيف الوسم @CR-01 ونغير الساعة في السيناريوهين حتى يعبرا عن القاعدة الجديدة: الإلغاء قبل الموعد ب 25 ساعة مقبول، وقبله ب 23 ساعة مرفوض:
@US-02 @BR-04 @CR-01
Scenario: Cancel early enough and the slot is free again
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00"
And the clock is "2026-10-13 09:00"
When patient "P-100" cancels the booking at "2026-10-14 10:00"
Then the slot "2026-10-14 10:00" of doctor "Dr. Huda" is free
@US-02 @BR-04 @CR-01
Scenario: Cancel too late
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00"
And the clock is "2026-10-13 11:00"
When patient "P-100" cancels the booking at "2026-10-14 10:00"
Then the cancellation is rejected with "CANCEL_TOO_LATE"ونشغل اختبارات الطلب وحدها قبل أن نلمس الكود:
docker run --rm --name lab-req-behave -v "$PWD":/work -w /work -e PYTHONPATH=/work python:3.12-slim sh -c "pip install -q --root-user-action=ignore --disable-pip-version-check behave==1.3.3 && behave --no-color --format plain --no-timings --tags=@CR-01"والمخرج سوف يكون كما يلي، وهذا آخر جزء منه:
Scenario: Cancel too late
Given the clock is "2026-10-12 09:00" ... passed
And doctor "Dr. Huda" has a free slot at "2026-10-14 10:00" ... passed
Given patient "P-100" has booked doctor "Dr. Huda" at "2026-10-14 10:00" ... passed
And the clock is "2026-10-13 11:00" ... passed
When patient "P-100" cancels the booking at "2026-10-14 10:00" ... passed
Then the cancellation is rejected with "CANCEL_TOO_LATE" ... failed
ASSERT FAILED: expected CANCEL_TOO_LATE, got CANCELLED
Failing scenarios:
features/booking.feature:42 Cancel too late
0 features passed, 1 failed, 0 skipped
1 scenario passed, 1 failed, 6 skipped
11 steps passed, 1 failed, 26 skipped
Took 0min 0.026sفي المخرج أعلاه لاحظ التالي:
- الاختبار فشل لأن الكود ما زال يطبق قاعدة 4 ساعات، فقبل الإلغاء قبل الموعد ب 23 ساعة، والرسالة
expected CANCEL_TOO_LATE, got CANCELLEDتقول بالضبط ما الذي يختلف بين المتطلب الجديد والكود الحالي. - السيناريو الأول نجح حتى قبل التعديل، لأن الإلغاء قبل 25 ساعة مقبول في القاعدتين، وهذا طبيعي، فالسيناريو الذي يكشف التغيير هو الذي يقع بين الحدين القديم والجديد.
- هذا الفشل هو ما يسميه SWEBOK في أسلوب ATDD الإذن بتعديل الكود: لا يعدل كود الإنتاج Production Code إلا وهناك اختبار قبول واحد على الأقل يفشل.
الخطوة الخامسة: نعدل الكود حتى تنجح الاختبارات
التعديل في الكود سطر واحد في booking.py، ومعه رقم الطلب في التعليق:
CANCEL_CUTOFF = timedelta(hours=24) # BR-04 (CR-01): cancel until 24 hours before the slotثم نشغل كل الاختبارات وليس اختبارات الطلب وحدها، حتى نتأكد أن التغيير لم يكسر قاعدة أخرى:
docker run --rm --name lab-req-behave -v "$PWD":/work -w /work -e PYTHONPATH=/work python:3.12-slim sh -c "pip install -q --root-user-action=ignore --disable-pip-version-check behave==1.3.3 && behave --no-color --format progress"والمخرج سوف يكون كما يلي:
USING RUNNER: behave.runner:Runner
features/booking.feature ........
1 feature passed, 0 failed, 0 skipped
8 scenarios passed, 0 failed, 0 skipped
38 steps passed, 0 failed, 0 skipped
Took 0min 0.043sوتبقى الخطوة السادسة خارج الكود: نحدث نص شاشة التأكيد ورسالة التذكير، ونضيف سطراً في سجل تغييرات الوثيقة «1.1، CR-01، الإلغاء قبل 24 ساعة، اعتمده المالك»، ونبلغ الاستقبال قبل الإطلاق حتى لا يتفاجأ المرضى. وبهذا الشكل يكون لكل سطر تغير في الكود سبب مكتوب ومن قرره ومتى، وإذا سأل مطور جديد بعد سنة «لماذا 24 ساعة؟» فالبحث عن CR-01 يعطيه القصة كاملة.
وعند الانتهاء لا يبقى شيء من التجربة على جهازك، لأن الخيار --rm يحذف ال Container بعد كل تشغيل، ويمكنك حذف مجلد lab إن لم تعد تحتاجه.
أخطاء شائعة عند تطبيق هذه الخطوات
- كتابة الوثيقة قبل المقابلات «لتوفير الوقت»، ثم استخدام المقابلات لتأكيد ما كتب، فيوافق الناس على ما يقرؤونه بدل أن يحكوا ما يحدث فعلاً.
- ملء القوالب كلها بالتفصيل لمشروع صغير، والقالب أداة تذكير بالأسئلة وليس استمارة يجب أن تمتلئ، فإذا كان قسم لا ينطبق فاكتب «لا ينطبق» وسببه في سطر.
- كتابة اختبارات Gherkin لتفاصيل الواجهة، فتنكسر مع كل تعديل في التصميم ويتوقف الفريق عن تشغيلها.
- قبول طلب التغيير بتعديل الكود مباشرة لأنه «سطر واحد»، فيصبح الكود والوثيقة والاختبار ثلاث روايات مختلفة لنفس القاعدة.
- التوقيع من المالك وحده دون ممثل للمستخدمين، فيوقع على ما يريده هو وليس على ما يحتاجه من سيستخدم النظام.
تدريب عملي
- أضف قاعدة جديدة BR-06 تقول إن المريض لا يحجز أكثر من موعد واحد مفتوح مع نفس الطبيب، واكتب لها سيناريو Gherkin بالوسم
@BR-06، وشغله لترى أنه يفشل، ثم عدلbooking.pyحتى ينجح. - خذ مشروعاً حقيقياً تعمل عليه، واملأ قالب SRS من صفحة واحدة له في ساعة واحدة، ثم عد الأسئلة المفتوحة والافتراضات التي ظهرت، وأرسلها لمن يستطيع الإجابة عنها.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- جملة «نبغى نظام حجز مواعيد» تحولت إلى أسئلة أولاً، ثم إلى خطة مقابلات تشمل الاستقبال والأطباء والمرضى، وليس المالك وحده.
- بيان المشكلة يقول من يتأثر وكيف نعرف أننا نجحنا قبل أن يذكر أي حل، والقيم الحالية تقاس ولا تخترع.
- القصص تحمل السبب، وقواعد العمل تكتب مستقلة برقم لأنها تخدم أكثر من قصة، ومعايير القبول بأمثلة وحدود.
- Gherkin مع behave تحول معايير القبول إلى اختبار يعمل، والوسوم مثل
@BR-01تعطيك التتبع مجاناً. - المتطلبات غير الوظيفية تكتب بالأرقام مع طريقة التحقق، والخصوصية والأمن يراجعها المسؤول عنها.
- خارج النطاق والافتراضات والقيود أقسام مكتوبة صراحة، وكل افتراض له من يتأكد منه وموعد.
- التوقيع يصنع خط الأساس، وكل تغيير بعده يمر بالتسجيل وتحليل الأثر والقرار، ثم الاختبار الذي يفشل قبل تعديل الكود.
سجل التحديثات
- أكتوبر 2026: كتابة المقال، واختبار ملفات Gherkin بأداة behave 1.3.3 على Python 3.12.15 داخل python:3.12-slim.