كنت أعبّر عن هذه الفكرة سابقاً بصيغة متعمدة الاستفزاز: TypeScript هي أفضل لغة لـ Codex. اليوم أفضّل صياغة أدق. في نوع تطوير الواجهات والتطبيقات full-stack الذي أعمل عليه فعلياً في الإنتاج، أجد أن TypeScript هي البيئة الأسهل للتحكم في Codex والتحقق من مخرجاته واستخدامه بثقة معقولة.
الكلمة المهمة هنا ليست “TypeScript” بل قابلية التحكم. أدوات AI coding ممتازة في إنتاج كود يبدو منطقياً. لكن هندسة الإنتاج تحتاج أكثر من ذلك: يجب أن يحترم الكود العقود الموجودة، ويحافظ على السلوك، ويتحمل refactor واسعاً، ويفشل مبكراً عندما يكون الافتراض خاطئاً. TypeScript تجعل عدداً أكبر بكثير من هذه القيود قابلاً للقراءة والتحقق آلياً مقارنة بـ JavaScript العادي.
ما الذي يحتاجه Codex فعلاً من codebase؟
الـ prompt الجيد مفيد، لكنه ليس سوى مصدر واحد للسياق. codebase نفسه يمكن أن يوضح المهمة أو يجبر النموذج على التخمين. عملياً، تكون البيئة أقوى عندما تظهر الافتراضات المهمة في الكود ويتم التحقق منها تلقائياً.
- توقيعات الدوال تحدد المدخلات والمخرجات المسموح بها.
- interfaces وtypes تجعل بنية كائنات المجال صريحة.
- union وenum تقللان مجموعة الحالات الصحيحة.
- المترجم يحول كثيراً من الافتراضات الخاطئة إلى أخطاء واضحة فور التعديل.
لا يحتاج AI agent إلى فهم التطبيق كله بشكل مثالي إذا كان التطبيق نفسه يعترض فور كسر عقد. كلما قصرت feedback loop قلّت مساحة التخمين الواثق.
الأنواع سياق قابل للتحقق، وليست مجرد توثيق
TypeScript الجيدة تشرح النية للمطور، لكنها داخل workflow آلي تقدم شيئاً إضافياً: هذا التوثيق نفسه قابل للفحص. مثال:
type PaymentResult =
| { ok: true; receiptId: string }
| { ok: false; code: "declined" | "timeout" };
function getReceiptId(result: PaymentResult) {
if (result.ok) return result.receiptId;
return result.receiptId;
// TypeScript: Property 'receiptId' does not exist
// on the failure branch.
}في JavaScript قد يبدو الخطأ نفسه طبيعياً حتى يصل التنفيذ فعلياً إلى ذلك branch. أما TypeScript فيمكنها رفض الافتراض قبل merge. بالنسبة إلى Codex، compiler error ليس مجرد خط أحمر في المحرر، بل إشارة دقيقة إلى ما فهمه النموذج بشكل خاطئ.
لهذا أفضل domain types واضحة على تراكيب واسعة مثل Record<string, any>. كلما وصفت الأنواع الواقع بدقة، أصبح codebase سياقاً أفضل للبشر وللـ AI.
المترجم يحول الأخطاء إلى feedback loop
لا أثق بطريقة “اطلب من Codex الكود ثم اقبل أول إجابة”. العملية التي أعتمد عليها تكرارية:
task → inspect → edit → typecheck → lint → test → review diffهذه سلسلة مملة، وهذا سبب قوتها. بعد كل تغيير مهم ترد البيئة. Type errors تكشف العقود المكسورة، وlint يلتقط فئة أخرى من المشاكل، والاختبارات تتحقق من السلوك الذي لا تستطيع الأنواع التعبير عنه، أما diff النهائي فيبقى بحاجة إلى مراجعة بشرية.
قد يحسن prompt ذكي المحاولة الأولى. لكن حلقة تحقق قوية تحسن كل المحاولات. وهذا أهم بكثير في الإنتاج.
قيمة TypeScript تظهر أكثر في refactor الكبير
التغييرات الصغيرة المعزولة سهلة لمعظم coding assistants. الحالة الأصعب هي تغيير ينتشر عبر عشرات الملفات: إعادة تسمية field في المجال، تعديل API response، تضييق component props، تقسيم union أو استبدال abstraction قديم.
مع TypeScript، يصنع breaking change خريطة للافتراضات المتأثرة. المترجم يبين أين ما زال العقد القديم مستخدماً. يستطيع Codex المرور على هذه القائمة ثم إعادة التحقق بعد كل جولة. هذا لا يجعل refactor صحيحاً تلقائياً، لكنه يجعل blast radius مرئياً.
ما الذي لا تستطيع TypeScript حمايتك منه؟
TypeScript هي guardrail وليست برهاناً على صحة البرنامج.
- منطق أعمال خاطئ: قد يكون الكود مضبوط الأنواع لكنه يحسب النتيجة الخطأ.
- بيانات runtime: API أو قاعدة بيانات أو form أو خدمة خارجية قد تعيد بيانات تختلف عن افتراضات compile time.
- سياق المنتج: المترجم لا يعرف لماذا يوجد flow معين أو أي edge case مهم للمستخدم.
- الأمان والمعمارية: الأنواع الصحيحة لا تضمن authorization أو caching أو حدود قاعدة البيانات أو البنية التحتية الصحيحة.
لذلك تبقى runtime validation والاختبارات وlogging وobservability والمراجعة ضرورية. نجاح typecheck لا يعني أن feature صحيحة.
الـ workflow الذي أستخدمه مع AI
- أحدد هدفاً ضيقاً. ما الذي سيتغير وما الذي لن يتغير وما هي acceptance criteria.
- أجعل Codex يفحص قبل أن يعدل. الأنواع الحالية وcall sites والاختبارات والوحدات المجاورة غالباً أفضل من prompt طويل.
- أفضل أصغر تغيير متماسك. diff مركز أسهل في التحقق من إعادة كتابة غير مرتبطة.
- أشغّل typecheck وlint فوراً. أريد الأخطاء البنيوية بينما التغيير ما زال صغيراً.
- أشغّل الاختبارات المناسبة وأضيف اختبارات عند تغير السلوك. الأنواع تتحقق من العقود، والاختبارات من السلوك.
- أراجع diff النهائي بنفسي. الأسماء، المعمارية، التكرار، edge cases، وهل حُلّت المشكلة الأصلية فعلاً.
متى تكون JavaScript كافية؟
هذا ليس هجوماً على JavaScript. في script صغير أو prototype مؤقت أو automation بسيط أو كود قليل shared state قد لا تضيف TypeScript قيمة كبيرة. كما أن مشروع JavaScript باختبارات قوية واتفاقيات واضحة يمكن أن يكون بيئة ممتازة للـ AI.
ادعائي أضيق: كلما كبر codebase وطال عمره وزاد عدد من يغيرونه، ارتفعت قيمة القيود القابلة للفحص آلياً. وفي المرحلة نفسها تصبح أخطاء AI أغلى.
السبب الحقيقي لتفضيلي TypeScript مع Codex
TypeScript لا تجعل Codex أذكى. إنها تجعل البيئة أقل تسامحاً مع الافتراضات السيئة.
في الإنتاج لا أريد مجرد agent مبدع، بل agent داخل نظام يخبره باستمرار عندما يكون مخطئاً. الأنواع، compiler errors، lint، tests والمراجعة البشرية تصنع هذا النظام معاً.
لذلك عندما أقول إن TypeScript تعمل بشكل ممتاز مع Codex فأنا لا أدافع عن لغة بعاطفة، بل عن feedback سريع وقابل للتحقق آلياً: غموض أقل، blast radius أصغر، refactor أكثر أماناً، وسرعة هندسية أكثر موثوقية عندما تؤخذ عملية التحقق بجدية.