كل codebase فيه واحد: ذلك الـ subsystem المترامي، قليل الـ tests، حامل الأحمال، الذي يلتفّ حوله الجميع لأن لمسه مرعب. الحركة الكلاسيكية هي إعادة كتابته من الصفر — وهكذا تحرق ربع سنة وتشحن دفعةً جديدة من الـ regressions، لأن rewrite من الصفر يرمي بعيدًا سنواتٍ من الـ edge cases المكتسَبة بشقّ الأنفس التي لا يذكر أحد أنه كتبها. طريقة Claude هي النقيض من البطولية: حدِّد النطاق في plan mode، وارسم خريطة كل call site بـ subagent، وثبّت السلوك الحالي بـ tests أولًا، ثم رحِّل تدريجيًا خلف test suite خضراء — كل خطوة مُراجَعة، وكل commit صغير. الـ subsystem لا يبقى معطّلًا أكثر من diff واحد قابل للمراجعة في كل مرة. هذه هي حركة التنسيق في الـ stage الثالث: تستدعي الخريطة (playbook الـ map-the-codebase)، والـ CLAUDE.md (الـ project-context)، والـ test suite الخضراء (الـ debug-from-trace والـ backfill-tests)، والـ refactor الآمن (الـ safe-refactor)، والـ migration الواسعة (الـ mechanical-migration)، والـ commits الصغيرة المُراجَعة (الـ git-workflow) — حركة واحدة بدل سبع.
- الـ repo مفتوحًا في Claude Desktop مع الـ
CLAUDE.mdالخاص به، كي يقرأ Claude الـ subsystem الفعلي لديك ويتّبع قواعد بيتك — لا نسخة عامة عن شكل كود كهذا عادةً. - هدف واضح: ما الذي يعنيه حديث هنا، تحديدًا. الـ library الجديدة، أو الـ pattern الجديد، أو الحدّ الفاصل الجديد — «انقل الـ payments adapter بعيدًا عن الـ SDK المهجور وخلف interface نظيف»، لا مجرّد «نظّف الـ payments».
- تغطية الـ tests الحالية للـ subsystem — وقراءة صادقة للثغرات. التغطية الرقيقة ليست عائقًا؛ بل أول ما تُصلح (ستعمل backfill قبل أن ترحّل). أحضِر أوامر الـ test والـ type-check والـ lint كي يستطيع Claude تشغيل الـ loop الحقيقي لديك.
- موافقة على أن هذا مُرحَّل، لا دفعةً واحدة: يتّفق الفريق على أن المسار القديم والجديد سيعيشان جنبًا إلى جنب لفترة، وأن الـ suite تبقى خضراء طوال الوقت.
-
حدِّد النطاق في plan mode قبل أي كود
افتح المجلد في Claude Desktop وبدّل إلى plan mode — إعادة بناء كهذه هي بالضبط ما صُمّم plan mode من أجله. الخطة هي أرخص مكان في المشروع كلّه لتكون مخطئًا فيه، فأنفِق حُكمك هنا. أخرِج الشكل بالقوّة: الحالة الراهنة، والحالة المستهدفة، والـ seam الذي ستقطع عليه، والمخاطر — قبل أن يتغيّر سطر واحد. انظر
/features/plan-mode/.أنت تطلبنحن نحدّث الـ payments subsystem بعيدًا عن الـ SDK المهجور وخلف interface نظيف. في plan mode: اقرأ الـ subsystem، وصِف الحالة الراهنة، والحالة المستهدفة، والـ seam المحدّد الذي سنقطع عليه، وترتيب العمليات، وأبرز المخاطر. لا تكتب أي كود — أعطني خطة مُرحَّلة أستطيع الاعتراض عليها.ما تحصل عليه خطة مُرحَّلة في نثر — الراهن مقابل المستهدف، الحدّ الفاصل المراد إدخاله، تسلسل مرتّب من الخطوات التدريجية، وقائمة مخاطر مرتّبة — تقرأها بإمعان وتجادلها قبل الموافقة. لا تعديلات بعد.
اقرأ هذه كأنها حاملة للأحمال، لأنها كذلك. تصحيح مقاربة خاطئة يكلّف فقرةً هنا؛ وتصحيحها بعد بدء الـ migration يكلّف أيامًا. إن اقترحت الخطة cut-over دفعةً واحدة، فتلك إشارتك لتعترض وتُرحّلها.
-
ارسم خريطة الـ blast radius بـ subagent
تفشل عملية التحديث على الـ call site الذي لم يتذكّره أحد. أرسِل subagent ليقوم بالقراءة الواسعة — كل caller، وكل عقد ضمني، وما هو حامل للأحمال وما هو ميت — كي لا يزاحم الحفر العميق سياق عملك. الـ subagent يعود بالتقرير؛ أنت لا تفقد مكانك. انظر
/features/subagents/.أنت تطلباستخدِم subagent لرسم خريطة الـ blast radius الكامل للـ payments subsystem: كل call site، وكل مكان يعتمد على سلوكه الحالي أو شكل بياناته، وأي عقود ضمنية (أشكال الـ errors، الـ side effects، الترتيب)، وأي شيء يبدو ميتًا. أعطني المخزون مجمّعًا حسب المخاطر — مباشر، يحتاج حُكمًا، وغريب.ما تحصل عليه مخزون مجمّع — «41 caller مباشر، و7 يعتمدون على شكل الـ error بالضبط، و3 يفعلون شيئًا غير موثّق بالـ retries» — مسمّيًا ملفات حقيقية، كي تعرف الحجم والشكل الحقيقيين للعمل قبل الالتزام به.
لا تدع القراءة الواسعة للـ subagent تصير وحيًا — افحص المُدخلات المفاجئة بنفسك مقابل الملفات الحقيقية. خريطة واثقة لمكان مرّ عليه مرورًا سريعًا تبقى خريطة عليك التحقّق منها.
-
ثبّت السلوك الحالي بـ tests أولًا
لا يمكنك إثبات «لم يُكسَر شيء» إلا مقابل baseline خضراء — فقبل أن تغيّر أي شيء، شخّص ما يفعله الـ subsystem اليوم، بأخطائه وكلّ شيء. إن كانت التغطية رقيقة، فهنا تُصلحها. مُرّ عبر playbook الـ backfill-tests للثغرات؛ الـ suite التي تبنيها هنا هي حزام الأمان لكل خطوة تلي.
أنت تطلبقبل أن نرحّل أي شيء، اكتب characterization tests تثبّت السلوك الحالي للـ payments subsystem — الـ happy paths، وأشكال الـ errors، والـ edge cases الغريبة التي أظهرتها الخريطة. أريد test suite خضراء ستفشل بصوت عالٍ إن تغيّر السلوك. أشِر إلى أي سلوك يبدو bug كامنًا، لكن ثبّته كما هو الآن.ما تحصل عليه characterization suite خضراء تغطّي السلوك الحالي الحقيقي للـ subsystem، مع الإشارة إلى أي سلوك يبدو مريبًا لكنه مُثبَّت — شبكة أمانك، في مكانها قبل أول خطوة migration.
ثبّت السلوك كما هو، لا كما ينبغي أن يكون. خلط bugfix في الـ baseline يعني أن test أحمر لاحق قد يكون الـ migration أو الإصلاح — وتكون فقدت القدرة على التمييز بينهما. أصلِح الـ bug الكامن في commit خاص به، لاحقًا.
-
ابنِ المسار الجديد خلف حدّ فاصل، القديم والجديد جنبًا إلى جنب
لا ترحّل في المكان نفسه. أقِم الـ implementation الحديث خلف flag أو interface نظيف، مع بقاء المسار القديم يعمل، وأبقِ الـ suite خضراء. هذا هو شكل الـ strangler-fig: الشيء الجديد ينمو إلى جانب القديم، ولا شيء غير قابل للتراجع بعد. كل خطوة تنزل على شكل diff مرئي تقرأه وتقبله في لوحة الملفات.
أنت تطلبابنِ مسار الـ payments الجديد خلف الـ interface المأخوذ من الخطة، خلف feature flag، مع بقاء المسار القديم في مكانه والـ suite ما زالت خضراء. لا ترحّل أي call sites بعد — أريد كلا الـ implementations حيًّا ومُختبَرًا كي نبدّل تدريجيًا. أرِني الـ diff.ما تحصل عليه implementation جديد يعيش بجانب القديم خلف flag/حدّ فاصل، والـ characterization suite ما زالت خضراء مقابل المسار القديم، وdiff قابل للمراجعة — خطوة قابلة للتراجع، لا cut-over.
«المسار القديم ما زال في مكانه» هي العبارة الحاملة للأحمال. ما دام الاثنان يعملان، فكل خطوة قابلة للتراجع والـ subsystem لا يُكسَر أبدًا — وهذا هو كامل المغزى من الترحيل المُرحَّل بدل الدفعة الواحدة.
-
رحّل الـ call sites تدريجيًا، في commits صغيرة مُراجَعة
الآن انقل الـ callers إلى المسار الجديد شريحةً في كل مرة — حركة الـ mechanical-migration، لكن كل شريحة مُلتزَمة ومُراجَعة قبل التالية. بعد كل شريحة، تبقى الـ suite خضراء. هنا يبقى refactor كل call site صادقًا: اقرأ الـ diff، اقبله، التزِم به برسالة تقول لماذا، ثم انتقل.
أنت تطلبرحّل الـ call sites المباشرة إلى المسار الجديد على دفعات صغيرة. بعد كل دفعة، شغّل الـ suite وأرِني الـ diff لتلك الدفعة فقط كي أراجعها وألتزم بها قبل التالية. اترك الحالات التي تحتاج حُكمًا والغريبة لنفعلها معًا — لا تجبرها.ما تحصل عليه سلسلة من الديفات الصغيرة، الخضراء، القابلة للمراجعة كلٌّ على حدة — دفعة call sites واحدة لكلٍّ — تقبلها وتلتزم بها واحدةً واحدة، مع ترك الحالات الصعبة مُشارًا إليها لحُكم بشري بدل التخمين.
قاوِم الرغبة في ترحيل كل شيء في diff واحد ضخم. الدفعات الصغيرة تعني أن suite حمراء تشير إلى دفعة واحدة، لا إلى الـ subsystem كلّه — ويبقى
git blameالخاص بك صادقًا للشخص التالي. أبقِ الـ commits صغيرةً ومنطقية؛ الفرق ثنائية اللغة تُبقي الكود إنجليزيًا لكنها تكتب رسائل الـ commit والـ ADR بلغة المُراجِع. -
نفّذ الـ cut-over، احذف المسار القديم، وأثبت أنه مطابق
بمجرد أن يُرحَّل كل call site ويصير أخضر، اقلِب الـ flag، واحذف الـ implementation القديم، وأثبت كل شيء: suite خضراء، types خضراء، lint أخضر، وسلوك مطابق للـ baseline التي ثبّتها في الخطوة الثالثة. تشغيل الـ loop هو نصف مسار
Power Track(جلسة مفعّلة للـ Terminal)؛ على Claude Desktop، شغّله بنفسك والصق أي أحمر. الإنسان يملك هذا الـ cut-over — اقرأ الـ diff النهائي.أنت تطلبكل الـ call sites على المسار الجديد وخضراء. أزِل الـ feature flag واحذف الـ implementation القديم للـ payments بالكامل. ثم شغّل الـ suite كاملةً، والـ type-checker، والـ linter، وأرِني أن السلوك يطابق characterization baseline بالضبط. أعطني الـ diff النهائي والحذف لمراجعتهما — أنا أملك الـ cut-over.ما تحصل عليه المسار القديم ذهب، والـ flag أُزيل، وloop أخضر (tests + types + lint) يُثبت أن الـ subsystem الحديث يتصرّف بشكل مطابق للـ baseline — إضافةً إلى diff نهائي تقرأه سطرًا سطرًا قبل أن تملك الـ merge.
الحذف هو الجزء المُرضي، لكنه أيضًا الجزء غير القابل للتراجع — اقرأه. والتقِط لماذا اتخذت القرارات الرئيسية في ADR وهي طازجة؛ المهندس التالي سيشكرك على المنطق، لا على الـ diff فقط.
- strangler-fig على طبقة الـ traffic: بدل code flag، وجّه نسبةً مئوية من الـ traffic الحقيقي إلى المسار الجديد وارفعها كلما نمت الثقة — الـ subsystem الجديد يخنق القديم تدريجيًا في الـ production، لا في الـ test suite فقط. الانضباط المُرحَّل نفسه، مطبَّقًا على الـ rollout.
- قسّمه عبر فريق agents: لـ subsystem كبير، شغّل العمل الواسع بالتوازي — subagent يرسم خريطة الـ blast radius، وآخر يعمل backfill للـ characterization tests، وآخر يقوم بالـ mechanical migration للـ call sites السهلة — بينما تبقى أنت المنسّق الذي يقرأ كل diff. انظر
/features/subagents/. - احتفِظ بـ ADR للقرارات: اكتب Architecture Decision Record بينما تمضي — لماذا هذا الحدّ الفاصل، ولماذا هذه الـ library، وما الذي فكّرت فيه ورفضته. للفريق ثنائي اللغة، يبقى الكود إنجليزيًا لكن الـ ADR يستحقّ أن يُكتب بلغة المُراجِع كي يُقرأ الـ لماذا فعلًا.
- rewrite كبير دفعةً واحدة دون suite خضراء هو تخمين على نطاق واسع. كامل مخاطر التحديث هو الـ edge case الذي لا يذكره أحد — الترحيل خلف characterization suite هو كيف تمسكه diff واحدًا في كل مرة بدل كلّه دفعةً واحدة يوم الإطلاق. إن لم تستطع إبقاءه قابلًا للشحن طوال الطريق، فقد اخترت المسار الخطِر.
- الخطة هي أرخص مكان لتكون مخطئًا فيه — فاقرأها. إعادة بناء تبدأ بالكتابة قبل الاتفاق على الـ seam هي إعادة بناء تكتشف أن الـ seam كان خاطئًا في منتصف الطريق. أنفِق حُكمك في plan mode، حيث يكلّف الخطأ فقرة.
- لا تدع القراءة الواسعة للـ subagent تصير وحيًا. خريطة subsystem كبير هي بالضبط حيث تختبئ إجابة واثقة لكن سطحية. افحص الـ call sites المفاجئة مقابل الملفات الحقيقية — الخريطة توجّهك، لا تُبرّئك.
- الإنسان يملك كل merge والـ cut-over. كل diff مُراجَع والحذف النهائي مسؤوليتك للتوقيع عليها — عامِل مخرجات الـ agent كتيّار من PRs من مبتدئ سريع متحمّس، لا يُدمَج أبدًا دون قراءة. وأبقِ الكود المملوك والأسرار وبيانات العملاء داخل الحدّ: على Claude Desktop المجلد المفتوح هو الحدّ وأنت توافق على كل قراءة.
ستحصل في النهاية على الـ subsystem الذي خشيه الجميع، مُحدَّثًا تدريجيًا — كل خطوة محدّدة النطاق في plan mode، ومرسومة بـ subagent، ومُثبَّتة بـ characterization suite، ومُرحَّلة في commits صغيرة مُراجَعة، ومُثبَتة كمطابقة عند الـ cut-over — بدل rewrite يستغرق ربع سنة وفوضى regressions. المسار القديم محذوف، والجديد نظيف، وADR يسجّل لماذا. بقيتَ أنت المهندس المعماري؛ وكان Claude القارئ والمُختبِر والمُرحِّل الذي لا يكلّ.
أسئلة يطرحها الناس
- لماذا لا أعيد كتابة الـ subsystem من الصفر — أليس ذلك أنظف؟
- rewrite من الصفر يرمي بعيدًا سنواتٍ من الـ edge cases المتراكمة التي لا يذكر أحد أنه كتبها، وهو مكسور طوال الوقت الذي تبنيه فيه — وهكذا تحرق عمليات الـ rewrite ربع سنة وتشحن regressions. ترحيله خلف characterization suite خضراء يمنحك النتيجة النظيفة *و* نظامًا يعمل في كل خطوة: المسار الجديد ينمو إلى جانب القديم، وترحّل تدريجيًا، والـ subsystem لا يتعطّل أبدًا أكثر من diff واحد قابل للمراجعة. تحصل على الكود الحديث دون المراهنة بربع السنة على cut-over دفعةً واحدة.
- كيف تساعد الـ subagents وفِرق الـ agents فعلًا في تحديث كهذا؟
- الفشل المكلِف في إعادة البناء هو الـ call site الذي لم يتذكّره أحد، فالقراءة الواسعة هي أعلى عمل من حيث الرافعة. الـ subagent يقوم بتلك القراءة الواسعة — رسم خريطة كل caller وعقد ضمني — في سياقه الخاص، كي لا يزاحم المخزون العميق سياق العمل الذي تحتاجه للـ migration نفسها. على subsystem كبير تستطيع تشغيل فريق agents بالتوازي: واحد يرسم خريطة الـ blast radius، وواحد يعمل backfill للـ characterization tests، وواحد يرحّل الـ call sites السهلة، بينما تبقى أنت المنسّق الذي يقرأ ويوافق على كل diff. انظر `/features/subagents/`.
- كيف أبقي الـ subsystem قابلًا للشحن طوال الطريق؟
- قاعدتان. أولًا، ثبّت السلوك الحالي بـ characterization suite *قبل* أن تغيّر أي شيء، وأبقِها خضراء بعد كل خطوة — suite حمراء تعني أنك كسرت السلوك، وانتهى الأمر. ثانيًا، ابنِ المسار الجديد خلف flag أو حدّ فاصل مع بقاء المسار القديم يعمل، كي يعيش القديم والجديد جنبًا إلى جنب وتكون كل خطوة قابلة للتراجع حتى الـ cut-over النهائي. لا تحذف الـ implementation القديم إلا بعد ترحيل كل call site وأن تكون الـ suite والـ types والـ lint كلّها خضراء.
- كم ينبغي أن يستغرق هذا، وكيف أحدّد نطاقه؟
- من نصف يوم لـ subsystem محدود إلى بضعة أيام لكبير — معظم الوقت يذهب في الخطة، وخريطة الـ blast radius، وعمل backfill للـ characterization tests، لا في الـ migration نفسها. حدّد نطاقه في plan mode أولًا: اجعل Claude يضع الحالة الراهنة، والمستهدفة، والـ seam الذي ستقطع عليه، والمخاطر، ودَع حجم *ذلك* يخبرك بالميزانية. إن كانت الخطة غامضة، فالتقدير تخمين — اشحذ الخطة حتى تصير الخطوات ملموسة، ثم قِسها.