من الكود المصدري إلى شيء يمكن تشغيله فعليًا
لقد انتهيت للتو من كتابة الكود على حاسوبك المحمول. يعمل بشكل مثالي. جميع الميزات تعمل. لا توجد أخطاء. تشعر بالرضا حيال ذلك. الآن تريد عرضه على فريقك أو على مستخدم. تنسخ مجلد المشروع إلى حاسوب آخر وتحاول تشغيله. فجأة، لا يعمل شيء. تظهر أخطاء لم ترها من قبل. أو ربما يبدأ التطبيق، لكنه يبدو مختلفًا تمامًا.
نفس الكود. نتيجة مختلفة.
هذه هي اللحظة التي يدرك فيها العديد من المطورين شيئًا مهمًا: الكود المصدري ليس شيئًا يمكنك تشغيله في أي مكان. الكود المصدري هو مادة خام. إنه مجموعة من ملفات النص التي تحتوي على تعليمات البرنامج. قبل أن يتمكن أي خادم، أو حاسوب أي مستخدم، أو أي بيئة إنتاج من استخدامه، يجب تحويل هذا الكود المصدري إلى شيء قابل للتنفيذ.
عملية التحويل هذه تسمى بناء (Build).
لماذا لا يمكن للكود المصدري الذهاب مباشرة إلى الإنتاج
هناك عدة أسباب تجعل الكود المصدري الخام غير جاهز للتشغيل خارج حاسوبك المحمول.
أولاً، معظم لغات البرمجة تحتاج إلى ترجمة. Java و Kotlin و Go و Rust و C# يجب أن تُترجم إلى ملفات ثنائية أو bytecode. لغات مثل JavaScript أو Python أو Ruby لا تحتاج إلى ترجمة بنفس الطريقة، لكنها لا تزال تتطلب ترتيب التبعيات والحزم بشكل صحيح حتى تعمل في بيئات مختلفة.
ثانيًا، التطبيقات الحديثة تعتمد دائمًا تقريبًا على مكتبات أو أطر عمل خارجية. هذه المكتبات تحتاج إلى تنزيلها، ووضعها في الأماكن الصحيحة، وأحيانًا تكوينها لكل بيئة. الكود الخاص بك وحده عديم الفائدة بدونها.
ثالثًا، التطبيقات غالبًا ما تحتاج إلى موارد مثل ملفات التكوين، الأصول الثابتة، القوالب، أو الصور. يجب حزم هذه مع الكود بطريقة متسقة.
كل هذا العمل — ترجمة الكود، تنزيل التبعيات، تنظيم الملفات، وإنتاج مخرجات جاهزة للتشغيل — هو ما نسميه بناء. نتيجة البناء تسمى أرتيفكت (Artifact).
كيف يبدو الأرتيفكت
تأتي الأرتيفكتس بأشكال عديدة حسب مجموعة التقنيات التي تستخدمها.
لتطبيق Java، الأرتيفكت عادة ما يكون ملف JAR أو WAR. لتطبيق Go، إنه ملف ثنائي واحد. لواجهة أمامية ويب، قد يكون الأرتيفكت مجلدًا من ملفات HTML و CSS و JavaScript مصغرة. لتطبيق Python، قد يكون حزمة تحتوي على جميع التبعيات المجمعة معًا.
التنسيق المحدد ليس مهمًا بقدر ما يمثله الأرتيفكت: حزمة واحدة مكتفية ذاتيًا وجاهزة للتشغيل. هذا الأرتيفكت هو ما يُرسل إلى الخوادم، ويوضع في بيئات الاختبار، أو يُنشر إلى الإنتاج. الكود المصدري نفسه لا يذهب أبدًا مباشرة إلى الخادم. الأرتيفكت دائمًا هو ما يذهب.
على سبيل المثال، يحدد Dockerfile خطوات البناء التي تحول الكود المصدري إلى صورة حاوية — وهو تنسيق شائع للأرتيفكت:
FROM node:18-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]
فكر في الأمر مثل الطهي. الكود المصدري هو مكوناتك — دقيق، بيض، سكر، زبدة. عملية البناء هي فعل الخلط والخبز والتقديم. الأرتيفكت هو الكعكة النهائية. أنت لا ترسل المكونات الخام إلى طاولة طعام المطعم. أنت ترسل الكعكة.
مشكلة البناء المتعدد
بمجرد أن تفهم أن البناء ينتج أرتيفكتس، يظهر سؤال جديد. ستبني تطبيقك عدة مرات. كل تغيير في الكود، كل إصلاح لخلل، كل ميزة جديدة تؤدي إلى بناء جديد. كيف تفرق بين أرتيفكت وآخر؟
بدون طريقة لتمييزها، لا يمكنك معرفة أي أرتيفكت تم اختباره، وأيها يعمل في الإنتاج، وأيها جديد تمامًا. يصبح هذا بالغ الأهمية عندما يحدث خطأ ما. إذا كان الإنتاج معطلاً، فأنت بحاجة إلى معرفة بالضبط أي أرتيفكت منشور هناك وأي إصدار من الكود أنتجه.
هنا يصبح تحديد الأرتيفكت أمرًا ضروريًا. كل مخرجات بناء تحتاج إلى هوية فريدة. تسمح لك هذه الهوية بتتبع أي أرتيفكت إلى الكود المصدري الدقيق، وتكوين البناء، والبيئة التي أنشأته.
ماذا يحدث أثناء البناء
تتضمن عملية البناء النموذجية عدة خطوات، على الرغم من أن التسلسل الدقيق يعتمد على تقنيتك.
يظهر المخطط الانسيابي التالي التسلسل النموذجي للخطوات التي تحول الكود المصدري إلى أرتيفكت قابل للنشر:
الخطوة الأولى عادة ما تكون جلب أحدث كود مصدري من نظام التحكم بالإصدارات. هذا يضمن أنك تبني بالضبط ما هو موجود في المستودع، وليس بعض التعديلات المحلية التي نسيت إيداعها.
بعد ذلك يأتي حل التبعيات. أداة البناء تقوم بتنزيل جميع المكتبات والأطر المطلوبة. يمكن أن تكون هذه الخطوة بطيئة في المرة الأولى، لكن عمليات البناء اللاحقة غالبًا ما تخزن التبعيات مؤقتًا لتوفير الوقت.
ثم يحدث التجميع أو التحويل الفعلي. يتم تجميع الكود، وتصغير الأصول، ومعالجة القوالب. تطبق أداة البناء أي تحويلات محددة في تكوين البناء الخاص بك.
بعد ذلك، يتم تجميع الموارد. يتم جمع ملفات التكوين والملفات الثابتة والأصول الأخرى ووضعها في المواقع الصحيحة داخل هيكل المخرجات.
أخيرًا، تقوم أداة البناء بحزم كل شيء في تنسيق الأرتيفكت المناسب لتطبيقك. قد يكون هذا أرشيفًا مضغوطًا، أو ملفًا ثنائيًا، أو صورة حاوية.
بيئات البناء مهمة
تفصيل حاسم يقع فيه العديد من الفرق: يجب أن تتم عمليات البناء في بيئة متسقة. إذا بنيت على حاسوبك المحمول، فقد تختلف النتيجة عن البناء على جهاز زميل أو على خادم بناء. أنظمة تشغيل مختلفة، إصدارات أدوات مختلفة، مكتبات مثبتة مختلفة — كل هذه يمكن أن تنتج أرتيفكتس مختلفة من نفس الكود المصدري.
لهذا السبب تستخدم الفرق المحترفة خوادم بناء مخصصة أو حاويات بناء. بيئة البناء موحدة ومضبوطة. كل بناء يعمل في نفس الظروف، منتجًا أرتيفكتس قابلة للتكرار. حاسوبك المحمول ليس خادم بناء، بغض النظر عن مدى دقة إعداده.
الأرتيفكت هو ما يتم نشره
إليك مبدأ يبدو واضحًا لكنه يُخالف بشكل متكرر: الأرتيفكت الذي تختبره في بيئة الاختبار يجب أن يكون نفس الأرتيفكت الذي تنشره إلى الإنتاج بالضبط. ليس معاد بناؤه من نفس الكود. ليس تكوينًا مختلفًا قليلاً. نفس الملف بالضبط.
إذا أعدت البناء للإنتاج، فأنت تقدم مخاطرة. ربما كانت حالة خادم البناء مختلفة. ربما تغير إصدار تبعية بين عمليات البناء. ربما تصرفت أداة البناء بشكل مختلف. الطريقة الوحيدة للتأكد من أن ما اختبرته هو ما يعمل في الإنتاج هي استخدام الأرتيفكت المطابق.
هذا يعني أن عملية البناء الخاصة بك يجب أن تنتج أرتيفكتًا واحدًا لكل إصدار من الكود الخاص بك. يتم ترقية هذا الأرتيفكت عبر البيئات — من التطوير إلى الاختبار إلى التدريج إلى الإنتاج — دون إعادة بنائه. الأرتيفكت يحمل هويته معه، ويمكنك دائمًا تتبع مصدره.
قائمة تحقق عملية لعملية البناء الخاصة بك
- حدد تنسيق الأرتيفكت الذي ينتجه تطبيقك (JAR، ثنائي، صورة حاوية، إلخ.)
- قم بإعداد بيئة بناء مخصصة ومتسقة عبر جميع عمليات البناء
- قم بتكوين أداة البناء الخاصة بك لإنتاج معرفات إصدار فريدة لكل بناء
- قم بتخزين كل أرتيفكت في مستودع مركزي حيث يمكن استرجاعه لاحقًا
- لا تقم أبدًا بإعادة بناء نفس الكود لبيئات مختلفة — قم بترقية نفس الأرتيفكت
- وثق الخطوات في عملية البناء الخاصة بك حتى يتمكن أي شخص في الفريق من فهمها
ماذا يعني هذا لعملك اليومي
في المرة القادمة التي تقوم فيها بتشغيل تطبيقك على حاسوبك المحمول ويعمل، تذكر أن هذه مجرد البداية. هذا الكود يحتاج إلى المرور بعملية بناء قبل أن يتمكن من العمل في أي مكان آخر. البناء يحول الكود المصدري الخام إلى أرتيفكت — حزمة جاهزة للتشغيل في أي بيئة مهيأة بشكل صحيح.
فهم هذا التمييز بين الكود المصدري والأرتيفكتس يغير طريقة تفكيرك في التسليم. الكود المصدري هو ما تكتبه. الأرتيفكتس هي ما تنشره. إنهما ليسا نفس الشيء، ومعاملتهما كشيء قابل للتبادل يؤدي إلى نوع مشاكل "يعمل على جهازي" التي تضيع ساعات من وقت تصحيح الأخطاء.
قم ببناء أرتيفكتسك بشكل متسق. حددها بوضوح. قم بترقيتها عبر البيئات دون إعادة بناء. هذا الانضباط البسيط يلغي فئة كاملة من مشاكل النشر قبل أن تصل إلى الإنتاج.