رضوان يعمل مستقلا من الدار البيضاء، وكتب أداة صغيرة تحول جداول عميل إلى فواتير. سلمها في ملف واحد مع سطر تنصيب. على جهازه تعمل. على جهاز العميل تتوقف عند سطر لم يكتبه رضوان أصلا، داخل حزمة نزلت في اليوم نفسه بنسخة أحدث.
الصياغة التي أخذت منه عطلة نهاية أسبوع لم تكن هي المشكلة، ولم تكن المشكلة في الأداة. الفرق بين الجهازين كان في ملف واحد لم يرسله، وهو الملف الذي لا يكاد يذكره درس عربي واحد.
الصياغة عطلة، وأربع حلقات حولها هي بقية السنة
تعلم لغة يعني في الاستعمال الشائع تعلم صياغتها: كيف تكتب الحلقة، وكيف تعرف الدالة، وبأي رمز تكتب القائمة. هذه هي النواة، وهي أصغر ما في اللغة وأسرعه انتهاء. من يعرف لغة واحدة يأخذ صياغة الثانية في أيام.
وحولها أربع حلقات لا تعلم في درس: المكتبة القياسية، ثم مدير الحزم، ثم ملف القفل ونسخة اللغة، ثم البناء والاختبار والنشر. الحلقة الأولى تراجع ولا تحفظ، وهذا صحيح. والثلاث الباقية هي التي تفصل برنامجا يعمل على جهاز واحد عن برنامج يعمل على جهاز ثان، وهي التي أوقفت رضوان.
الترقيم الدلالي، والقبعة التي تعني شيئين لا شيئا واحدا
الترقيم الدلالي: 2.4.1 يعني رئيسي 2 وثانوي 4 وترقيعي 1. الرقم الأول يزيد حين يكسر شيء كان يعمل، والثاني حين تضاف ميزة لا تكسر شيئا، والثالث حين يصلح عطل. هذا وعد من صاحب الحزمة، وهو الوعد الذي يبنى عليه كل ما بعده.
وعلى هذا الوعد يكتب القيد. الملف الذي يعلن الحزم لا يكتب فيه رقم واحد عادة، يكتب فيه مدى:
"marked": "^4.0.0"
القبعة في ^2.4.1 تقبل 2.9.0 وترفض 3.0.0، أي أنها تقول: أعطني أي نسخة لا تكسر ما كتبت. وهنا يقع ما يفاجئ كل من قرأ التعريف وحده. القبعة تحت 1.0 تضيق: ^0.2.4 تقبل 0.2.9 وترفض 0.3.0، لأن الحزمة قبل نسختها الأولى لم تعد بعد بشيء، فيعامل رقمها الثاني معاملة الأول. وأكثر الحزم الصغيرة التي تنزل يوميا أرقامها تبدأ بصفر.
فالمدى المكتوب في ملف رضوان كان صادقا، والنسختان اللتان نزلتا على الجهازين كلتاهما تحققه، وهما مختلفتان. القيد يقول ما هو مقبول، ولا يقول ما نزل.
ملف القفل هو كل الفرق بين جهاز وجهاز
ملف القفل يثبت النسخ بالضبط، فيأخذ جهاز ثان النسخ نفسها. اسمه يختلف باختلاف اللغة، ووظيفته واحدة: يسجل رقم كل حزمة نزلت فعلا، ورقم كل حزمة نزلت لأن الأولى طلبتها، ويسجل بصمة كل ملف.
- في Python الملف
uv.lock، والأمرuv sync - في JavaScript الملف
package-lock.json، والأمرnpm ci - في Rust الملف
Cargo.lock، والأمرcargo build --locked
والقاعدة العملية سطر واحد: ملف القفل يدخل في المستودع مع الكود، لا يستثنى منه. وأمر التنصيب على جهاز غير جهازك ليس npm install، هو npm ci، لأن الأول يحق له تحديث الملف والثاني ملزم به. هذا الفرق بين أمرين هو ما فصل جهاز رضوان عن جهاز عميله، وهو مكتوب في توثيق npm بالإنجليزية ولا يكاد يذكر في درس عربي.
ثلاثة أسماء لأداة واحدة: uv وnpm وcargo
uv في Python وnpm في JavaScript وcargo في Rust أداة واحدة بثلاثة أسماء. كل واحدة منها تقرأ ملف إعلان، وتحل شجرة الاعتماديات، وتكتب ملف قفل، وتشغل الأوامر. من فهم واحدة منها فهم الثلاث، وما يتغير هو أسماء الملفات لا الفكرة.
الأداة uv هي الحال في Python سنة 2026، وحلت محل ثلاث أدوات منفصلة كانت تؤدي عملا واحدا: pip للتنصيب وvirtualenv للعزل وpyenv لنسخة اللغة. وهي مجانية ومفتوحة المصدر وتنزل من أي بلد عربي. والدرس العربي المصور قبل 2024 يعرض الثلاث القديمة، وهذا وحده يفرز الدرس الذي يستحق ساعة من غيره.
وnpm تصل مع Node ولا تنصب وحدها، وحلت محل نسخ مجلد المكتبات إلى داخل المشروع باليد. وcargo وصلت مع Rust من أول يوم ولم يكن لها بديل قط، ولهذا السبب وحده تجربة Rust في هذا الباب أنظف من تجربة اللغتين الأخريين: لا خيار ثانيا ولا جدال.
وعدد الحزم التي تنزلها اللغة قبل أن ترد على طلب واحد يقول شيئا عن الحلقة التي ستقضي فيها وقتك. JavaScript تنزل 49 حزمة مباشرة وغير مباشرة، وRust تنزل 4، وPython تنزل 2، وعند Go العدد 0 لأن الخادم داخل مكتبتها القياسية والمشروع لا يعلن شيئا. العدد الكبير ليس عيبا، هو اختيار: سوق يورد كل شيء يتحرك أسرع من لجنة، والثمن يدفع في شجرة اعتماديات لا في انتظار إصدار.
الجدار هنا مفتاح بحث، لا صياغة
القارئ العربي يتعلم اللغة باسمها العربي ويبحث عن نظامها باسمها العربي، والمفتاحان يفتحان مكتبتين مختلفتين. «بايثون» و«جافا» كلمتان يكتبهما الناس فعلا وتردان مادة عربية وافرة. و«جافا سكريبت» تكتب كلمتين بالياء، أما «جافاسكربت» موصولة فلا تكاد ترد شيئا، وهي الكتابة الشائعة في الدروس نفسها.
والأهم من هجاء الاسم أن المكتبتين تحملان مادتين مختلفتين لا ترجمتين لمادة واحدة. الاسم العربي يرد شرح الصياغة، أي النواة وحدها. والاسم اللاتيني يرد التوثيق ومستودع الحزم ورسائل الخطأ، أي الحلقات الأربع. فالقارئ الذي لا يغير مفتاحه يدور في النواة سنة كاملة ويظن أن ما بقي عليه مزيد من الصياغة.
والقاعدة التي تخرج من هذا تطبق اليوم: اللغة تتعلم باسمها العربي، ونظامها يبحث عنه باسمه اللاتيني. رسالة الخطأ تنسخ كما هي ولا تترجم، لأنها هي نفسها عبارة البحث التي كتبها من صنع الأداة.
الترتيب: لغة واحدة حتى الحلقة الرابعة، ثم الثانية في أيام
الترتيب الشائع أخذ لغة ثانية بعد إتمام صياغة الأولى، وهو أغلى ترتيب ممكن، لأنه يشتري النواة مرتين ولا يشتري حلقة واحدة. اللغة الثانية بعد الحلقة الرابعة تكلف أياما؛ اللغة الثانية بعد النواة تكلف سنة أخرى في المكان نفسه.
واختيار لغتك الأولى يسبق هذا كله، وهو أقل كلفة مما يبدو: الاختيار الخاطئ يكلف أسابيع، والبقاء في النواة يكلف سنة. ثم بايثون هي الأوسع مادة عربية والأقصر طريقا إلى أول برنامج يعمل، وJavaScript هي الوحيدة التي يشغلها المتصفح، فمن أراد شيئا يفتحه غيره برابط لا يملك حولها خيارا.
وGo وRust يقرآن بعد ذلك لسببين مختلفين: الأولى لأن أدواتها قليلة ومكتبتها القياسية تغني عن السوق، والثانية لأنها تجبر على تعلم ملكية الذاكرة، وهو درس ينفع في كل لغة بعدها. وجافا تبقى لغة سوق العمل المؤسسي في الخليج، وذلك سبب وجيه لاختيارها لا سبب لتفضيلها.
الاثنتا عشرة تحت هذه الصفحة، والسؤال الذي يليها
كل صفحة تحت هذه الصفحة تأخذ لغة واحدة وتمشي فيها الحلقات الخمس بالترتيب نفسه، فما تتعلمه من قراءة اثنتين منها ليس لغتين، هو الشكل المشترك. وأساسيات البرمجة هي الجزء الذي لا يتغير بتغيير اللغة أصلا، وقراءتها بالتوازي أرخص من قراءتها بعد ثلاث لغات.
ويبقى أن ملف القفل يضمن أن الحزم واحدة على الجهازين، ولا يضمن أن اللغة نفسها واحدة، ولا أن نظام التشغيل واحد، ولا أن قاعدة البيانات ترد على المنفذ نفسه. فما الذي يشحن هذه كلها مع الكود بدل أن يوصف في ملف نصي ويرجى أن يقرأ؟ جوابه في نشر المشاريع، وهو الباب الذي يفتح بعد اللغة الأولى مباشرة لا بعد الثالثة. ومن أراد أن يرى الحلقات الخمس مطبقة على مكان واحد ينتهي إليه الكود فموضعها تطوير الويب.