Free

كيف جعلتُ Claude يغيّر رأيه

التوصية "الأخفّ" التي قدّمها Claude كانت تخفي تكاليف حقيقيّة. سؤال مرتدّ واحد كَفى لكشفها كلّها.


كنت أعدّل smarts — تطبيق Rails يَعرض عقود Ethereum الذكية على هيئة "وثائق للمطوّرين". قبل أيام عُدتُ إلى مشكلة قديمة: العقود تُرقَّى. إذا عَرَض النظام عقد proxy مُرقَّى مستخدِماً ABI قديماً، فهو يكذب على المستخدم.

طلبتُ من Claude أن نصمّم معاً آلية كشف ترقيات الـ proxy. أعطاني خطة تقنية متينة، تشمل توافق EIP-1967 و Beacon proxy و ZeppelinOS. عند بند "متى نُطلِق الفحص؟" طرح خيارَين:

# الخيار A: إضافة last_proxy_check_at إلى قاعدة البيانات، throttle لمدة 5 دقائق
if @contract.last_proxy_check_at.nil? || @contract.last_proxy_check_at < 5.minutes.ago
  CheckProxyUpgradeJob.perform_later(@contract)
end

# الخيار B: throttle عبر Solid Cache، أخفّ
unless Rails.cache.exist?("proxy_check:#{@contract.id}")
  Rails.cache.write("proxy_check:#{@contract.id}", true, expires_in: 5.minutes)
  CheckProxyUpgradeJob.perform_later(@contract)
end

وجملته الختامية:

B أبسط، لا حاجة لإضافة عمود.

حدَّقتُ في الجزءَين قليلاً. B أبسط فعلاً، بلا migration، كلّه استدعاءات cache، وأقرب إلى أسلوب Rails. لكنّ شيئاً لم يطمئنّي.

كتبتُ خمسة أحرف وضغطتُ Enter:

توصي بـ a؟

أعاد Claude الحساب

ردُّه (مع اختصار بعض كتل الكود):

أُغيّر رأيي — أوصي بـ A.

السبب: الهدف كلّه من كشف ترقيات الـ proxy هو عدم الوثوق بطزاجة الـ ABI. إن استخدمنا Solid Cache للـ throttle، فنحن نضع "هل فحصنا أم لا؟" مرّة أخرى داخل تخزين مُتبخِّر:

  • بعد deploy / cache wipe، تدخل جميع العقود معاً في حالة "غير مفحوصة" → الموجة التالية من الزيارات تُطلق عاصفة فحص
  • لا يمكن الإجابة عن سؤال "منذ متى لم يُعَد فحص هذا العقد؟"
  • لا يمكن إنشاء شبكة أمان مجدوَلة: قد تمرّ على العقود الباردة أسابيع دون زيارة، فلا يطلق المشغّل الكسول أبداً

ما يفتحه إضافة عمود last_proxy_check_at:

  1. تشغيل كسول (نفس منطق B)
  2. شبكة أمان مجدوَلة عبر recurring.yml تضع دفعة من العقود التي تجاوز فحصها 24 ساعة في الطابور — مستقلّة عن زيارات المستخدمين
  3. إظهار الطزاجة على الواجهة: "Implementation verified 12 min ago"

الكلفة: migration واحدة + استدعاء update_column واحد عند انتهاء الـ job (دون callbacks، شبه مجّاني).

خلاصة قصيرة: B يبدو "أكثر Rails-أصالةً وأخفّ"، لكنّه يفتقد طبقةً كاملة من القدرة. A هو الخيار الذي يُجسّد فعلاً مبدأ "نحن لا نثق بطزاجة الـ ABI".

انتبه إلى ما حدث بين التوصيتين:

  • المحاولة الأولى: عرض الخيارَين → مقارنة حجم الكود → اختيار B ("أبسط")
  • المحاولة الثانية: التثبّت في لماذا نفعل ذلك أساساً → ملاحظة أنّ B يضع الحالة في تخزين مُتبخِّر، فيناقض فرضيّة "عدم الوثوق بطزاجة الـ ABI" → اختيار A

لم تَدخل بين المحاولتَين أيّ حقيقة جديدة. حجم الكود، مصطلحات Rails، دلالات الـ cache — كلّه على حاله. ما تغيَّر هو الطبقة التي يقيّم منها.

المحاولة الأولى كانت تقارن حجم الكود. الثانية كانت تقارن "هل هذا التغيير يحلّ فعلاً المشكلة التي وضعناها؟".

ما الذي فعلته تلك الأحرف الخمسة

حين كتبت "توصي بـ a؟" لم أكن أحاول دفعه إلى أيّ مكان. علقتُ ببساطة عند كود B وعدتُ بدافع غريزيّ أسأله عن تفضيله.

لكنّ الأثر كان: انتشاله من وضع "اختر واحداً من خيارَين" إلى وضع "لماذا نفعل هذا أصلاً؟". هذه الفجوة هي بيت القصيد.

حين تُقدِّم نماذج اللغة توصيات، فإنّها تنطلق دائماً تقريباً من الأمثلية المحلّية: أيّ diff أصغر، أيّ snippet أكثر مصطلحيّةً، أيّ تنفيذ يُقرأ أنظف. هذه محاور حقيقيّة. لكنّها كثيراً ما لا ترتبط بـالغاية الفعليّة من هذا الـ PR.

A و B متكافئان على محور "throttle لمدة 5 دقائق". لكنّ A يفتح ثلاثة أشياء لا يفعلها B — شبكة مجدوَلة، قابليّة للرصد، طزاجة على الواجهة. إن نظرتَ إلى طبقة الـ throttle فحسب، يفوز B. إن اهتممتَ بسبب وجود هذا الـ job، يفوز A.

لم يَرَ Claude تلك الطبقة في المرّة الأولى ليس لأنّه عاجز. بل لأنّ الإطار الافتراضي لـ "قارن هذه الخيارات" هو طبقة الصياغة/Rails. سؤال مرتدّ واحد رفعه إلى طبقة المسألة، ومن هناك حسب كلفة B بنفسه.

كيف نحوّل هذا إلى عادة

تعديلات صغيرة أدخلتُها على سير عملي:

حين يختم Claude بعبارة "أبسط / أخفّ / أكثر أصالةً"، ألقِ نظرة ثانية. هذه كلمات جماليّة لا حُكميّة. تَصِف ما يَحسُن قراءته، لا ما يصمد تحت الحمل.

كلفة إعادة السؤال صفر. "توصي بـ X؟" — خمسة أحرف. سيُجبَر Claude على التقييم من زاوية لم يقفها بعد. حتى لو هبط على التوصية ذاتها، ستكون الحجج أمتن، تكفيك للحكم على ما إذا كنتَ ستثق بها.

اطلب منه فرد الكلف، لا التوصية وحدها. ما جعل النتيجة جيّدة هذه المرّة لم يكن إصراري على A — بل أن Claude نفسه دوّن كلف B الخفيّة (عاصفة بعد cache wipe، غياب القابليّة للرصد، طريق مسدود للأعمال المجدوَلة). لم أكن قد صُغتُ شيئاً منها قبل أن يفعل هو.

نشرتُ A: أضفتُ عمود last_proxy_check_at، تشغيلاً كسولاً مع شبكة أمان مجدوَلة كلّ 24 ساعة، وأصبحت الواجهة تَعرض سطراً: Implementation verified 12 min ago. بعد ثلاثة أيّام، في عمل لا علاقة له بهذا، أتاح لي ذلك العمود كتابة الاستعلام Contract.where("last_proxy_check_at < ?", 24.hours.ago).find_each — استعلام لم يكن ليوجد ببساطة لو اخترت B.

خمسة أحرف من سؤال مرتدّ، تساوي تقريباً إعادة هيكلةً بعد أسبوع.