{"id":"l-ds-5-1","courseId":"design-systems","lang":"ar","kind":"lesson","summary":"أدِر contribution model (نموذج مساهمة) بمستويات واضحة للإصلاحات والتحسينات والمكوّنات الجديدة، وفرز الطلبات بمسار قرار محدّد، وجهّز governance خفيفة تقرّر بسرعة وفي العلن.","html":"<p>في أول عام فتحت فيه Northwind باب المساهمات، تلقّينا 112 pull request ودمجنا 41 منها. ومات معظم البقية وهو ينتظر: مراجعة API بلا موعد، أو مصمّمًا كان في مشروع آخر، أو قرارًا لم يشعر أحد أنه مخوّل باتخاذه. وتعلّمت الفرق الدرس سريعًا. بحلول الشهر التاسع كادت المساهمات تتوقّف، وعادت ثلاث فرق إلى مجلّدات مكوّناتها الخاصة. الـ contribution model ليس وثيقة؛ إنه وعد بمدى سرعة فريق النواة وعدله في الاستجابة.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ثلاثة مستويات للمساهمة</h2>\n<p>كان الإصلاح أن نتوقّف عن معاملة كل المساهمات بالطريقة نفسها. لدى Northwind اليوم ثلاثة مستويات، لكلٍّ منها مساره:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>المستوى</th>\n<th>أمثلة</th>\n<th>المسار</th>\n<th>أول ردّ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>إصلاح</td>\n<td>خلل، خطأ في التوثيق، story ناقصة</td>\n<td>pull request، ومراجع واحد من النواة</td>\n<td>يوما عمل</td>\n</tr>\n<tr>\n<td>تحسين</td>\n<td>variant جديد، prop جديد، token جديد</td>\n<td>issue باقتراح قصير، ثم pull request</td>\n<td>أسبوع</td>\n</tr>\n<tr>\n<td>مكوّن جديد</td>\n<td>منتقي تاريخ، جدول بيانات</td>\n<td>RFC، ثم مراجعة API، ثم البناء مع ثنائي من النواة</td>\n<td>أسبوعان حتى القرار</td>\n</tr>\n</tbody>\n</table></div><p>الإصلاح لا يحتاج إذنًا. والتحسين يحتاج حجّة قصيرة مكتوبة: ما المشكلة، وأيّ الفرق، ومقطع الـ API المقترح، لأن الـ props API عام إلى الأبد. أما المكوّن الجديد فيحتاج طلب تعليقات (RFC)، وهو وثيقة تعرض المشكلة، وأدلّة من فريقين على الأقل، ومسوّدة الـ API، ومصفوفة الحالات، وخطة إمكانية الوصول. ويُناقش الـ RFC في مراجعة الـ API الأسبوعية، ويخرج منها أحد ومعه قرار.</p>\n<p>ملف <code dir=\"ltr\">CODEOWNERS</code> يجعل التوجيه آليًا: فريق النواة يملك ملفات الـ tokens وما يُصدَّر من الـ API العام، فيطلب أي تغيير فيها مراجعة من النواة، بينما يمكن لأي مساهم متمرّس أن يوافق على صفحات التوثيق.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الفرز: أين ينتمي الطلب؟</h2>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 260\" role=\"img\" aria-labelledby=\"l-ds-5-1-t1\"><defs><marker id=\"arrow-l-ds-5-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-ds-5-1-t1\">مسار قرار. هل يستطيع مكوّن موجود أن يؤدّي المهمّة، ربما مع توثيق أفضل؟ إن كانت الإجابة نعم، أشِر إليه. وإن كانت لا، فهل يحتاجه فريقان أو أكثر؟ إن كانت لا، ابنِه محليًا من قطع النظام. وإن كانت نعم، ساهم به في النظام مع ثنائي من النواة.</title>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"100\" width=\"130\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"85\" y=\"133\" text-anchor=\"middle\">طلب</text>\n  <rect class=\"d-box\" x=\"190\" y=\"90\" width=\"170\" height=\"76\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"275\" y=\"122\" text-anchor=\"middle\">هل يغطّيه مكوّن</text>\n  <text class=\"d-label\" x=\"275\" y=\"142\" text-anchor=\"middle\">موجود؟</text>\n  <rect class=\"d-box-success\" x=\"190\" y=\"10\" width=\"170\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"275\" y=\"37\" text-anchor=\"middle\">أشِر إليه وأصلح التوثيق</text>\n  <rect class=\"d-box\" x=\"400\" y=\"90\" width=\"150\" height=\"76\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"475\" y=\"122\" text-anchor=\"middle\">هل يحتاجه فريقان</text>\n  <text class=\"d-label\" x=\"475\" y=\"142\" text-anchor=\"middle\">أو أكثر؟</text>\n  <rect class=\"d-box-warn\" x=\"400\" y=\"200\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"475\" y=\"229\" text-anchor=\"middle\">ابنِه محليًا</text>\n  <rect class=\"d-box-accent\" x=\"580\" y=\"100\" width=\"90\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"625\" y=\"124\" text-anchor=\"middle\">RFC</text>\n  <text class=\"d-label\" x=\"625\" y=\"144\" text-anchor=\"middle\">ثم ساهم</text>\n  <path class=\"d-arrow\" d=\"M150 128 L186 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M275 90 L275 58\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"290\" y=\"78\">نعم</text>\n  <path class=\"d-arrow\" d=\"M360 128 L396 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"366\" y=\"118\">لا</text>\n  <path class=\"d-arrow\" d=\"M475 166 L475 196\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"485\" y=\"186\">لا</text>\n  <path class=\"d-arrow\" d=\"M550 128 L576 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"553\" y=\"118\">نعم</text>\n</svg><figcaption>كيف تفرز Northwind طلب مكوّن جديد</figcaption></figure>\n<p>أغلب الطلبات تنتهي عند السؤال الأول. نحو نصف طلبات «المكوّن الجديد» في Northwind يتبيّن أنها مكوّن موجود لم يجعله التوثيق سهل الإيجاد، فيصبح الطلب إصلاحًا في التوثيق.</p>\n<p>السؤال الثاني هو حيث تتوتّر الفرق، لأن «ابنِه محليًا» تبدو رفضًا. وهي ليست كذلك. المكوّن الخاص بفريق واحد والمبني من tokens النظام ومكوّناته يبدو أصيلًا ولا يكلّف النظام شيئًا في الصيانة. تحتفظ Northwind بقائمة بهذه المكوّنات المحلية، مع مالكيها، وتراجعها كل ربع سنة. وحين يحتاج فريق ثانٍ شيئًا من القائمة، يصبح المكوّن الأصلي نقطة انطلاق لـ RFC. ثلاثة من مكوّناتنا الحالية، منها منتقي التاريخ، بدأت بهذه الطريقة.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>قبول كل مساهمة لأنها مجانية</span></p>\n<p>المكوّن المُساهَم به ليس مجانيًا. بمجرّد دخوله النظام، يملك فريق النواة أخطاءه، وإمكانية الوصول فيه، والـ theming الخاص به في كل علامة، وعمليات انتقاله لسنوات. وقبول مكوّن لفريق واحد مجاملةً لذلك الفريق هو بالضبط الطريق الذي تتضخّم به الأنظمة إلى مئتي مكوّن لا يستطيع أحد إبقاءها متّسقة.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Governance بلا بيروقراطية</h2>\n<p>الـ governance (الحوكمة) هي طريقة اتخاذ القرارات حين يختلف الناس. ونسخة Northwind منها تتّسع في صفحة واحدة:</p>\n<ul>\n<li><strong>فريق النواة يقرّر</strong> في كل ما يصبح API عامًا، بعد الاستماع إلى الفرق المتأثّرة.</li>\n<li><strong>القرارات تُتّخذ علنًا</strong>، في مراجعة الـ API الأسبوعية التي يمكن لأي أحد حضورها، أو بشكل غير متزامن في الـ RFC.</li>\n<li><strong>الحجج تستند إلى المبادئ والأدلّة</strong>، مثل أرقام الاستخدام أو نتائج البحث، لا إلى المسمّيات الوظيفية.</li>\n<li><strong>كل قرار يُكتب</strong> في سجلّ قرارات مع أسبابه، حتى تمكن مراجعته حين تتغيّر الأدلّة بدل إعادة الجدال فيه من الذاكرة.</li>\n</ul>\n<p>السرعة مهمّة بقدر العدل. «لا» واضحة خلال أسبوعين أفضل لفريق المنتج من «ربما» لشهرين، لأنه يستطيع التخطيط حول الـ «لا».</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>اجعل المساهمين مرئيّين</span></p>\n<p>تنسب Northwind الفضل للمساهمين بأسمائهم في الـ changelog وفي إعلان الإصدار، ويحتسب مديرو الهندسة المساهمات في النظام ضمن تقييمات الأداء. التقدير لا يكلّف شيئًا، ويحوّل المساهمة من خدمة شخصية إلى إشارة مهنية.</p>\n</aside>\n<p>في التمرين ترتّب مسار مكوّن جديد إلى داخل النظام. وبعدها توسّع معمارية الـ theming لتشمل علامة ثانية.</p>\n","toc":[{"id":"sec-1","text":"ثلاثة مستويات للمساهمة","level":2},{"id":"sec-2","text":"الفرز: أين ينتمي الطلب؟","level":2},{"id":"sec-3","text":"Governance بلا بيروقراطية","level":2}],"takeaways":["طابِق العملية مع حجم المساهمة؛ فإصلاح خطأ إملائي ومكوّن جديد يجب ألا يمرّا من البوابة نفسها.","يدخل المكوّن الجديد النظام حين تحتاجه عدّة فرق، لا حين يبنيه فريق واحد أولًا.","البناء المحلي نتيجة مشروعة للفرز، ويجب أن يجعله النظام سهلًا لا مخجلًا.","تنجح الـ governance حين تكون القرارات سريعة ومكتوبة ومبنيّة على المبادئ، لا على الأقدمية."],"further":[{"title":"Contributing guidelines for a repository (GitHub Docs)","url":"https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors"},{"title":"About code owners (GitHub Docs)","url":"https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners"},{"title":"Contribute to the U.S. Web Design System","url":"https://designsystem.digital.gov/about/contribute/"}],"quiz":[{"q":"<p>يصلح فريق خطأً إملائيًا في توثيق Tabs ويفتح pull request. دليل المساهمة يشترط RFC ومراجعة API لكل تغيير. ما النتيجة المرجّحة مع الوقت؟</p>","options":[{"text":"جودة أعلى، لأن كل تغيير يُراجَع بدقّة.","why":"المراجعة الثقيلة على التغييرات التافهة لا تضيف جودة؛ إنها تضيف تأخيرًا في الغالب."},{"text":"تتوقّف الفرق عن المساهمة بالإصلاحات الصغيرة وتلتفّ على المشكلات محليًا بدلًا من ذلك.","why":"صحيح. حين تتجاوز تكلفة المساهمة تكلفة الحلّ المحلي، يختار الناس الحلّ المحلي."},{"text":"يحصل فريق النواة على وقت أكثر للعمل المهمّ.","why":"يصرف فريق النواة ذلك الوقت في إدارة العملية الثقيلة نفسها."},{"text":"لا شيء يتغيّر؛ الفرق تتقبّل العمليات كجزء من العمل.","why":"الناس يستجيبون للاحتكاك؛ وتكلفة العملية تظهر دائمًا في السلوك."}],"answer":1},{"q":"<p>يطلب فريق الفوترة أن يضيف النظام مكوّنًا باسم InvoiceTimeline. ولا يوجد فريق آخر لديه حاجة مشابهة. بماذا يوصي الفرز في Northwind؟</p>","options":[{"text":"إضافته إلى النظام، لأن المساهمة قدرة مجانية.","why":"كل مكوّن في النظام تكلفة صيانة طويلة الأمد يدفعها فريق النواة، حتى لو كتبه شخص آخر."},{"text":"رفضه وإخبار الفريق أن ينتظر حتى يحتاجه فريق آخر.","why":"فريق الفوترة يحتاجه الآن؛ والانتظار لا يفيد أحدًا."},{"text":"الطلب من فريق النواة أن يبنيه كأولوية.","why":"صرف قدرة النواة على مكوّن لفريق واحد يسحبها من الاحتياجات المشتركة."},{"text":"بناؤه محليًا من tokens النظام ومكوّناته، وإعادة النظر فيه إن احتاجت فرق أخرى شيئًا مشابهًا.","why":"صحيح. يتحرّر الفريق من التعطيل، وتبدو النتيجة أصيلة في النظام، ويتعلّم النظام هل الحاجة مشتركة."}],"answer":3},{"q":"<p>يختلف مصمّمان كبيران حول هل يجب أن يسمح Select بإدخال نص حرّ. أيّ نهج للـ governance يحسم ذلك على أفضل وجه؟</p>","options":[{"text":"طرحه في مراجعة الـ API الأسبوعية، والنقاش بالاستناد إلى المبادئ وأدلّة الاستخدام، وتسجيل القرار وأسبابه.","why":"صحيح. القرارات المتّخذة علنًا والمكتوبة يمكن مراجعتها بأدلّة جديدة بدل إعادة الجدال فيها من الصفر."},{"text":"ترك القرار للمصمّم الأقدم.","why":"الأقدمية تحسم الجدال اليوم وتعلّم الجميع أن الأدلّة لا تهمّ."},{"text":"إطلاق السلوكين خلف prop منطقي.","why":"تجنّب القرار ينقله إلى كل فريق منتج، ويضيف prop يتفاعل مع كل prop آخر."}],"answer":0}],"exercise":{"kind":"order","items":["search","evidence","rfc","review","build","release"],"title":"أدخِل منتقي التاريخ إلى النظام","prompt":"<p>بنى فريق الـ dispatch منتقيًا جيدًا لنطاق التواريخ محليًا. والآن يريد فريقا الفوترة والتقارير واحدًا مثله. رتّب الخطوات التي تُدخله إلى Northwind UI، متّبعًا الـ contribution model من الدرس.</p>\n","hints":["يبدأ الفرز بالتحقّق مما إذا كانت الحاجة مغطّاة أصلًا.","الـ RFC يحتاج أدلّة قبل أن يراجعه أحد، والمراجعة تسبق بناء نسخة النظام."],"explanation":"<p>يتأكّد الفرز أولًا من أن أي مكوّن موجود لا يغطّي الحاجة، ثم يجمع الأدلّة على أن عدّة فرق تتشاركها، وهذا ما ينقل المكوّن من المحلي إلى النظام. ويحوّل الـ RFC هذه الأدلّة إلى مسوّدة API ومصفوفة حالات وخطة لإمكانية الوصول، وتنتج مراجعة الـ API العلنية قرارًا مسجّلًا. وعندها فقط يبني فريق الـ dispatch نسخة النظام مع ثنائي من النواة، وتُطلَق كأي تغيير آخر: stories وتوثيق وchangeset ونسبة الفضل للمساهمين في ملاحظات الإصدار.</p>\n","checkLabels":{},"itemText":{"search":"التأكّد من أن أي مكوّن موجود، أو توثيقًا أفضل لأحدها، لا يغطّي اختيار النطاقات أصلًا","evidence":"جمع حالات الاستخدام من فرق الـ dispatch والفوترة والتقارير","rfc":"كتابة RFC فيه مسوّدة الـ API ومصفوفة الحالات وخطة إمكانية الوصول","review":"مناقشته في مراجعة الـ API الأسبوعية وتسجيل القرار وأسبابه","build":"إعادة بناء المنتقي في مستودع النظام بالعمل الثنائي مع مصمّم ومهندس من النواة","release":"إطلاقه مع stories وتوثيق وchangeset، ونسبة الفضل لفريق الـ dispatch في ملاحظات الإصدار"}},"words":558,"readMinutes":3}