CLAUDE.md الجيد ليس README — بل يلتقط الثوابت التي لا يستنتجها Claude من الكود. 6 أمور اكتبها، 4 تجنّبها، 5 أسئلة.
مشروعي Pickful فيه نظام هيئة محلّفين مجتمعي لامركزي، ومدفوعات عملات مشفّرة عبر x402، وSign-In with Ethereum، وقواعد بيانات متعددة، وإشعارات فورية — كلها تقنيات ظهرت خلال آخر عامين. Claude ينجز هذه الميزات بسرعة ودقة.
لكن إذا فتحت ملف CLAUDE.md الخاص بالمشروع، ستلاحظ: نظام هيئة المحلّفين وx402 لم تُذكرا ولا مرة واحدة.
هذا ليس سهواً. الغرض من CLAUDE.md لم يكن يوماً "وصف الميزات"، بل كتابة الأشياء التي لا يستطيع Claude اكتشافها مهما قرأ الكود.
من يكتب CLAUDE.md للمرة الأولى غالباً ما يعامله كـ README ويشرح كل ميزة رئيسية:
هذه المحتويات يستطيع Claude قراءتها بدقة أكبر منك بمجرد فتح topic_review_service.rb / x402.rb / like_points_service.rb. وصف ألف كلمة عن منطق العمل يقرأه Claude بمئات الـ tokens من الكود مباشرة، ودون أي انحراف في التفسير — الكود هو الحقيقة، والوصف مصدر ثانوي.
ما يجعل Claude يتعثّر فعلاً هو الفئات الستّ التالية.
في CLAUDE.md الخاص بـ Pickful يوجد أسطر كهذه:
Propshaft (not Sprockets)
ImportMap (no JavaScript bundler)
Hotwire: Turbo Frames, Turbo Streams, Stimulus
Lexxy gem overrides ActionText:
config.lexxy.override_action_text_defaults = false
كل سطر يقاوم التخمين الافتراضي. حين يرى Claude مشروع Rails، افتراضاته الأساسية هي:
بدون هذه الأسطر في CLAUDE.md، لو طلبت من Claude إضافة ميزة JS جديدة، فسيغلب عليه تثبيت Webpacker، وتعديل package.json، وكتابة إعدادات bundler — كل ذلك خطأ، وخطأ صامت (التطبيق يعمل، لكن خط الأصول يتلوّث).
هذه الأسطر في CLAUDE.md تقول لـ Claude: لا تخمّن، القرار اتُّخذ.
PostgreSQL with 4 separate databases:
- primary - Main application data
- cache - Solid Cache storage
- queue - Solid Queue jobs
- cable - Action Cable subscriptions
كتابة بسيطة، لكنها توفّر مطبّات بقيمة ليلة كاملة. التعدّد الافتراضي لقواعد البيانات في Rails 8 سلوك جديد، و Claude لا يذهب من تلقاء نفسه للتحقق من عدد القواعد. migration يبدو بريئاً يحطّ في قاعدة خاطئة، والتطوير لا يعطي خطأ (الأربعة كلها PostgreSQL، والـ schema يمرّ في أيّها)، لكن في الإنتاج تختلط جداول job الخاصة بـ Solid Queue مع نسخ primary الاحتياطية، أو يستعلم نموذج primary من قاعدة cache — أعطال تستغرق أياماً لتظهر.
سطران في CLAUDE.md مقابل يوم كامل من تصحيح خطأ إنتاجي.
/p-{slug} - Short post URLs (4-5 char alphanumeric)
/t-{slug} - Topic URLs (3-4 char alphanumeric)
/s-{code} - Short URL redirects (3-4 char alphanumeric)
/r-{referral} - Referral links
المسارات نفسها يراها Claude في routes.rb، لكن اتفاقيات الطول (4-5 حرفاً، 3-4 أحرف) مدفونة داخل منطق توليد الـ slug في النماذج أو الخدمات. اطلب من Claude إضافة نوع رابط قصير جديد، وعلى الأرجح سيولّد slug من 6 خانات، أو بنمط UUID، أو أرقاماً خالصة — خارج انسجام النظام البصري.
سمة هذه "الاتفاقيات": مخالفتها لا تنتج خطأً، لكن من يقرأ الكود لاحقاً يشعر أن شيئاً ما غير منسجم. يجب كتابتها.
VIP status at 400+ points
Posts with 15+ likes are "hot" posts
كلا الرقمين موجود في الكود في مكان ما (User#vip?، و Post#hot? scope). المشكلة أن Claude حين يعدّل شيئاً قريباً — تعديل مكافآت النقاط، إضافة إشعار "قريباً VIP"، كتابة مهمّة تُثبّت "المنشورات الساخنة" — لا يذهب تلقائياً لمحاذاة العتبات في المواضع الأخرى.
النتيجة: تمنح 500 نقطة لإنجاز مهمة لكن النص يقول "يمكنك أن تصبح VIP" (بينما 400 كافية)، أو تُنشئ بيانات seed لميزة جديدة بإعجابات أقل من اللازم فلا تصل قط إلى عتبة 15.
قدرة Claude البرمجية قوية، لكنه لا يملك إحساساً عامّاً بأرقام النظام ككل. وضع العتبات الحرجة في CLAUDE.md يعني أن كل محادثة تبدأ وهو يعرف أن "400 و15 أرقام خاصة".
- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules
دور هذا السطر تنقّلي وليس وصفياً.
بدونه، إذا احتاج Claude إضافة تحكّم صلاحيات جديد، ستظهر ثلاث احتمالات:
unless current_user.admin? مباشرة في الـ controllerauthorize? خاصة به في الـ modelمع وجود "Pundit policies in app/policies/"، سيتجه Claude دائماً إلى app/policies/ لإضافة ملف policy، بأسلوب موحّد.
سطر واحد يوفّر على Claude "عمل المحقّق" في كل مرة.
Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers
عند إضافة ميزة جديدة، افتراضات Claude هي:
لكن مشروعك يحتاج فعلياً:
مخالفة هذه "القيود الخارجية على مستوى المشروع" تُنتج تنظيفاً لاحقاً مكلفاً — ترجمات ناقصة، اختبارات تُعاد كتابتها. CLAUDE.md يثبّت مرة واحدة "ما يجب فعله في كل مرة".
قدر أهمية "يجب كتابته" تأتي أهمية "لا تكتبه". هذه الفئات التالية احذفها فور رؤيتها:
1. وصف الميزات
"نظام هيئة المحلّفين: يستطيع المستخدمون الإبلاغ عن محتوى مخالف، والمحتوى المُبلَّغ عنه يدخل مرحلة تصويت عام..."
→ Claude يفتح topic_review_service.rb ويقرأه بدقّة أعلى ممّا كتبت. حشو هذا في سياق كل محادثة جديدة هدر صافٍ.
2. ما يمكن استنتاجه فوراً من شجرة الملفات / Gemfile
"app/models/ تحوي نماذج ActiveRecord"، "يستخدم Rails 8"، "قاعدة البيانات PostgreSQL"
→ نظرة سريعة على جذر المشروع وعلى Gemfile كافية لـ Claude.
3. معرفة برمجية عامّة
"Controllers should be thin, delegate to services"، "تجنّب استعلامات N+1"، "اكتب اختبارات للميزات الرئيسية"
→ هذا موجود في بيانات تدريب Claude بالفعل. لا تكتبه إلا إذا كان مشروعك شاذاً — مثلاً "نحن عمداً لا نستخدم طبقة service، المنطق يوضع في الـ controller".
4. سياق المهمّة الحالية
"الآن نحن نُعيد هيكلة نظام الدفع، الهدف هو..."
→ هذا سياق محادثة وليس حقيقة مشروع. دسّه في CLAUDE.md يلوّث بقية المحادثات.
ضع إثبات "أستطيع ذلك" قبل الدعوة. بعد كتابة القسم السابق، أعدت تشغيل أسئلتي الخمسة على CLAUDE.md الخاص بـ Pickful — 238 سطراً — والنتيجة: تقريباً النصف هدر.
ما يجب حذفه (حوالي 120 سطراً):
معظم قسم أوامر التطوير (70 سطراً → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman كلها أوامر Rails قياسية، موجودة في تدريب Claude. يُستبقى فقط الثلاثة الخاصة بالمشروع: bin/jobs (Solid Queue worker)، bin/importmap pin (خاصّ بـ ImportMap)، bin/kamal deploy.
قائمة Core Domain Models (35 سطراً → حذف كامل): إدراج 20 نموذجاً باسم ودور هو أوضح نموذج لـ "CLAUDE.md بأسلوب README" — Claude يشغّل ls app/models/ أو يقرأ ملف نموذج واحداً ويعرف. دسّه في كل محادثة هدر صافٍ.
العناصر القياسية في Tech Stack (28 سطراً → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search كلها تُقرأ من Gemfile فوراً. يُستبقى فقط ما هو مضادّ للبديهة: Propshaft / ImportMap / Lexxy / x402-rails / Grover.
معرفة برمجية عامّة متناثرة: "Controllers should be thin"، "Use app/jobs/ for async processing"، ما تختبره request specs / model specs — هذه يفعلها Claude افتراضياً. هدر tokens.
المتبقّي نحو 100 سطر: اتفاقيات توجيه URL، توزيع الأربع قواعد، عتبتا VIP 400 / Hot 15، تجاوز Lexxy، خيارات معمارية مضادّة للافتراضي، لافتة Pundit، قائمة اللغات، FactoryBot (not fixtures).
لكن الأهم: أيّ invariant لم يُكتب بعد؟
أثناء التدقيق أدركتُ عدة أمور كان يجب إضافتها ولم أضفها:
238 سطراً تُقلَّص إلى 100–120 سطراً، ثم تضاف 5–10 أسطر من invariants سبق إغفالها — هذا أقرب إلى "الكثافة الصحيحة" لـ CLAUDE.md.
هذه ليست دروس تعليم، بل تدقيق على مشروعي أنا. أنا أيضاً لم أكتبه صحيحاً. الشكل الصحيح لـ CLAUDE.md هو الحذف المستمرّ والإضافة المستمرّة — كلّما نضج المشروع قليلاً، يجب أن يصبح CLAUDE.md أقصر وأصلب.
كلّما فكّرت بإضافة شيء إلى CLAUDE.md، أُجري عليه القائمة التالية:
القواعد التي تجتاز الخمس جميعاً تبقى، وما لا يتحصّل على إجابة عن أيّ سؤال يُحذف أو يُعاد صياغته.
الاستعمال الصحيح لـ CLAUDE.md ليس "تقديم المشروع"، بل ضغط المعرفة الضمنية بينك وبين الـ codebase، تلك التي لا يملؤها Claude مهما قرأ الكود — اختيارات شاذّة، عتبات غير مرئية، اتفاقيات مخالفة للافتراضيات، قيود خارجية على مستوى المشروع.
كل سطر تكتبه يجب أن يُجيب على سؤال: "هل هذه معلومة لا يمكن لـ Claude قراءتها من الكود؟" لا يمكن — تبقى. يمكن — تُحذف.
CLAUDE.md مكتوب بهذا الأسلوب عادة ما يقصر عن نسخته الأولى بأكثر من النصف، لكنه لكلّ محادثة أثمن بكثير من آلاف الكلمات من وصف الميزات. كما أنه لن يكتمل أبداً — كلّ ميزة جديدة تُسلّط الضوء على invariant كان ينبغي إدراجه سابقاً، وعلى فقرة كُتبت قديماً وحان وقت حذفها. الحذف والإضافة — تلك هي الصيانة اليومية لـ CLAUDE.md.