يروي زوراب بيلي ، قائد الفريق ، ممارسة جافا ، قصته عن العمل في مشروع لشركة كبيرة واحدة ويشاركه تجربته.
كيف استقر ...
دخلت المشروع في نهاية صيف عام 2017 كمطور عادي. لا يمكنني القول أنه في ذلك الوقت أعجبتني كثيرًا: كانت التقنيات المستخدمة في المشروع قديمة ، وتم تقليل التواصل داخل الفريق ، والتواصل مع العميل كان صعبًا وغير مثمر. لذلك التقى المشروع لي. في ذلك الوقت ، كانت لدي رغبة واحدة فقط: الخروج منها بسرعة.
سوف أخبركم قليلاً عن المشروع ككل. هذه هي البوابة الرسمية لشركة واحدة كبيرة بها معلومات عامة وأخبار وترقيات ومحتويات أخرى. تحتوي جميع النشرات الإخبارية التسويقية على روابط إلى صفحات معينة من الموقع ، أي أن متوسط التحميل ثابت ، لكن في بعض النقاط الزمنية قد يصل إلى قيم عالية. يتطلب استقرار تطبيق الويب وإمكانية الوصول إليه عناية خاصة - كل دقيقة توقف في الخدمة تؤدي إلى خسائر كبيرة للعميل.
شانتي أن الحول في الريح
في البداية ، قمت بدراسة الحالة التقنية للمشروع ، وإصلاح الأخطاء الطفيفة وإجراء تحسينات طفيفة. من وجهة نظر تقنية ، بدا التطبيق فظيعة: تم إصدار بنية متجانسة مبنية على الإصدار التجاري القديم من dotCMS ، رمز مكتوب في إصدار Java 6 ، عندما تم تقديم الجزء التاسع من جانب الخادم الخاص بجزء العميل على إطار السرعة ، والذي لم يكن عمره عدة سنوات في ذلك الوقت ، قد تم إصداره بالفعل كان مدعوما. تم إطلاق كل مثيل في JBoss AS وتم توجيهه باستخدام Nginx. أدى تسرب الذاكرة إلى إعادة تشغيل العقد باستمرار ، وأدى عدم وجود ذاكرة التخزين المؤقت العادية إلى زيادة في تحميل الخادم. لكن الشظية الأكبر كانت التغييرات التي أدخلت على كود CMS. استبعدوا إمكانية ترقية غير مؤلمة إلى إصدار أحدث. ومن الأمثلة الجيدة على ذلك الانتقال من الإصدار 3.2 إلى 3.7 ، والذي كان قد انتهى للتو في ذلك الوقت. استغرق الانتقال إلى الإصدار الثانوي التالي أكثر من عام. لا توجد حلول شائعة ، مثل Spring Framework و React.js وهندسة الخدمات المصغرة و Docker وما إلى ذلك. بتعمق أكثر في المشروع ، أصبحت عواقب مثل هذه الحالة الفنية ظاهرة للعيان. كان الأكثر حدة منهم عدم القدرة على تشغيل التطبيق محليًا للتطوير والتصحيح. عمل الفريق بأكمله المكون من 8 أشخاص في جناح تطوير واحد ، حيث تم نشر نسخة من إصدار الإنتاج من التطبيق. وفقًا لذلك ، يمكن لمطور واحد فقط تصحيح التعليمات البرمجية الخاصة به في نفس الوقت ، وقد أدى حظر التعليمات البرمجية المحدثة إلى حظر الفريق بأكمله. كانت الأوج هي عملية بيع فاشلة ، تم خلالها إرسال ملايين الرسائل والرسائل النصية القصيرة وإشعارات الدفع إلى مستخدمين مختلفين من خلال عشرات القنوات - تم فتح عشرات الآلاف من الجلسات في نفس الوقت. تعذر على الخوادم الوقوف ، وفي معظم الأوقات كانت البوابة غير متاحة. التطبيق لا مقياس جيد. لم يكن هناك سوى طريقة واحدة للقيام بذلك: نشر نسخة أخرى جنبًا إلى جنب وموازنة الأحمال بينهما باستخدام Nginx. وتضمن كل تسليم رمز الإنتاج الكثير من العمل اليدوي واستغرق عدة ساعات.
بعد ستة أشهر من مشاركتي في المشروع ، عندما بدأ الوضع بالفعل في الخروج عن نطاق السيطرة ، تم اتخاذ قرار لتغيير الوضع بشكل جذري. بدأت عملية الانتقال. التغييرات أثرت على جميع المجالات: تكوين الفريق ، وعمليات العمل ، والهندسة المعمارية والمكون التقني للتطبيق.
بنينا ، بنيت ...
بادئ ذي بدء ، حدثت تغييرات في الموظفين. استبدال العديد من المطورين ، جعلوا لي قيادة الفريق. بدأ الانتقال إلى الحلول الحديثة بمشاركة أشخاص في الفريق ممن لديهم خبرة في العمل معهم.
كانت التغييرات الإجرائية أكثر عالمية. وبحلول ذلك الوقت ، كان التطوير جارًا بالفعل على منهجية Agile- + Scrum ، التي استمرت لمدة أسبوعين مع التسليم في نهاية كل تكرار. لكن في الواقع ، لم يؤد ذلك إلى زيادة سرعة العمل فحسب ، بل على العكس ، تباطأ. استمرت التجمعات اليومية لمدة ساعة ونصف إلى ساعتين ولم تسفر عن أي نتائج. تحول التخطيط والاستمالة إلى نزاعات أو حلف أو اتصال بسيط. كان هناك شيء لتفعله مع هذا. كان من الصعب في البداية تغيير أي شيء في هذا السياق - بالنيابة عن العميل ، فقد الفريق الثقة تقريبًا ، خاصة بعد عملية بيع غير ناجحة. كان لابد من إثبات كل تغيير ومناقشته وإثباته لفترة طويلة. بشكل غريب ، لكن المبادرة جاءت من الزبون. من جانبهم ، شارك السيد سكروم للتحكم في التطبيق الصحيح للنهج والمنهجيات ، وعمليات التصحيح وتعيين الفريق للعمل. على الرغم من أنه كان منجذبًا إلى عدد قليل من السباقات ، إلا أنه ساعدنا على تجميع الأساس بشكل صحيح. لقد تغيرت طريقة التواصل مع العميل كثيرًا. لقد بدأنا في مناقشة المشكلات في العمليات في كثير من الأحيان ، وبدأت الأحداث الماضية بشكل أكثر إنتاجية ، وكان المطورون أكثر استعدادًا لتقديم ملاحظات ، وقام العميل ، من جانبه ، بالمضي قدمًا ودعم عملية الانتقال بكل الطرق.
لكن بصراحة ، سأقول بصراحة: كانت هناك لحظات قليلة عندما تم إجراء بعض التغييرات داخل الفريق "بصورة عمياء" ، وبعد ظهور نتائج إيجابية ، تم إبلاغ العميل بذلك. لمدة ستة أشهر ، تغير الموقف إلى اتصال عمل مريح. تلا ذلك العديد من أعمال بناء الفريق ، واجتماعات استغرقت يومًا واحدًا لمدة يومين لفريق التطوير بأكمله مع فريق العميل (المسوق ، والمحلل ، والمصمم ، وعارض المنتجات ، ومديري المحتوى ، وما إلى ذلك) ، وزيارات مشتركة إلى الشريط. بعد عام ، وحتى يومنا هذا ، يمكن أن يسمى التواصل ودية. أصبح الجو ودودًا ومريحًا ومريحًا. بالطبع ، لا يتم الاستغناء عن النزاعات ، ولكن حتى في أسعد العائلة ، هناك مشاجرات في بعض الأحيان.
حدثت تغييرات أقل إثارة للاهتمام خلال هذه الفترة في رمز التطبيق ، في الهندسة المعمارية والحلول المستخدمة. إذا لم تكن ذكيًا من الناحية الفنية ، فيمكنك تخطي النص بأكمله بأمان حتى النهاية. وإذا كنت محظوظًا تمامًا مثلي - مرحبًا! يمكن تقسيم الانتقال بأكمله إلى عدة مراحل. حول كل بمزيد من التفاصيل.
المرحلة 1. تحديد مجالات المشاكل الحرجة.كان كل شيء بسيطًا وواضحًا قدر الإمكان. بادئ ذي بدء ، كان من الضروري التخلص من اعتماد منتج تجاري لطرف ثالث ، وقطع متراصة وجعل التصحيح ممكن محليًا. أردت فصل كود العميل والخادم ، لتوزيعه معماريا وجسديا. مكان مشكلة أخرى هو التأهيل. يفتقر المشروع تمامًا إلى أي اختبار تلقائي. جعل هذا الانتقال صعبة بعض الشيء ، حيث كان يجب فحص كل شيء يدويًا. نظرًا لعدم وجود مهام فنية للوظيفة (تتأثر تفاصيل المشروع هنا) ، كان هناك احتمال كبير بفقد شيء ما. بعد أن رسمت المناطق التي توجد فيها مشكلات ، نظرنا مرة أخرى في القائمة. بدا الأمر وكأنه خطة. حان الوقت لبناء ناطحة سحاب!
المرحلة 2. تحديث قاعدة الكود.أطول مرحلة تشغيل. بدأ كل شيء مع الانتقال إلى بنية الخدمة (يجب عدم الخلط بينه وبين الخدمات المصغرة). كانت الفكرة على النحو التالي: تقسيم التطبيق إلى عدة خدمات منفصلة ، كل منها سيتعامل مع مهمته المحددة. كان من المفترض أن تكون الخدمات "غير متناهية الصغر" ، لكنني أيضًا لا أريد أن أضع كل شيء في مرجل واحد. كان من المفترض أن تكون كل خدمة تطبيق Spring Boot مكتوبًا في Java SE 8 وتعمل على Tomcat.
الأول كان ما يسمى. "خدمة المحتوى" ، والتي أصبحت طبقة بين التطبيق المستقبلي و CMS. لقد أصبح مجردة في الطريق إلى المحتوى. كان من المفترض أن يتم تنفيذ جميع الطلبات التي قدمناها مسبقًا مباشرة في CMS من خلال هذه الخدمة ، ولكن بالفعل باستخدام بروتوكول HTTP. مثل هذا الحل سمح لنا بتقليل الاتصال وخلق إمكانية استبدال dotCMS لاحقًا بتناظرية أكثر حداثة أو حتى التخلص من استخدام المنتجات التجارية وكتابة حلنا الخاص المصمم لمهامنا (بالنظر إلى المستقبل ، سأقول إن هذه هي الطريقة التي ذهبنا إليها).
خلقت على الفور الأرض لفصل الكود الأمامي والخلفي. أنشأوا خدمة الواجهة الأمامية ، والتي أصبحت مسؤولة عن توزيع التعليمات البرمجية المكتوبة على رد الفعل. لقد ثبنا npm ، وقمنا بتكوين العقدة وتصحيح أخطاء التجميع - كل شيء كما ينبغي وفقًا للاتجاهات الحديثة لجزء العميل.
بشكل عام ، تم تخصيص الوظيفة للخدمة وفقًا للخوارزمية التالية:
- إنشاء تطبيق Spring Boot جديد مع كل التبعيات والإعدادات اللازمة ؛
- نقل كل المنطق الأساسي (غالبًا ما كتبه من نقطة الصفر ، بالرجوع إلى الكود القديم ، فقط للتأكد من أنك لم تنسَ أي فارق بسيط) ، على سبيل المثال ، بالنسبة لخدمة التخزين المؤقت ، فهذه هي الخيارات لإضافتها إلى ذاكرة التخزين المؤقت والقراءة منها وتعطيلها ؛
- تمت كتابة جميع الوظائف الجديدة دائمًا باستخدام الخدمة الجديدة ؛
- إعادة كتابة الأجزاء القديمة للتطبيق تدريجياً في خدمة جديدة حسب الأهمية.
في البداية ، كان لدينا عدد قليل منها:
- خدمة المحتوى. بمثابة طبقة بين التطبيق و CMS.
- خدمة ذاكرة التخزين المؤقت. مستودع بسيط على Spring Cache.
- خدمة AA. في البداية ، كان يعمل فقط في توزيع المعلومات حول مستخدم مخول. بقي الباقي داخل dotCMS.
- خدمة الجبهة. مسؤول عن توزيع كود العميل.
المرحلة 3. Autotests.مع الأخذ في الاعتبار كل تجربة المشروع ، قررنا أن وجود اختبارات وظيفية يبسط بشكل كبير الحياة والتحديث الإضافي المحتمل للتطبيق. حان الوقت لإدخالهم في المشروع. اختبارات وحدة من رمز ، للأسف أن أقول هذا ، توقفت على الفور تقريبا. لقد استغرقوا الكثير من الوقت للكتابة والدعم ، ولم يكن لدينا الكثير من ذلك ، لأنه بالإضافة إلى إعادة كتابة الكود ، كانت المهام الحالية المتعلقة بالوظائف الجديدة معلقة علينا ، وغالبًا ما ظهرت الأخطاء. تقرر التركيز فقط على اختبار واجهة التطبيق باستخدام السيلينيوم. هذا ، من ناحية ، جعل من الأسهل بالنسبة لنا إجراء اختبار الانحدار قبل التسليم للإنتاج ، من ناحية أخرى ، أصبح من الممكن القيام بإعادة التجهيز على جانب الخادم ، ومراقبة الحالة من جانب العميل. لم يكن لدى الفريق مشغل آلي ، والكتابة والحفاظ على أهمية الاختبارات الذاتية تتطلب تكاليف إضافية. لم يعيدوا تدريب أحد المختبرين ، وتمت إضافة شخص آخر إلى الفريق.
المرحلة 4. نشر التشغيل الآلي.الآن بعد أن أصبح لدينا خدمات منفصلة ، عندما انفصلت الواجهة الأمامية عن الواجهة الخلفية ، عندما بدأت الاختبارات الذاتية مغطاة ، فقد حان الوقت لفتح علبة البيرة وأتمتة جميع الأعمال اليدوية لنشر التطبيق ودعمه محليًا ، على خوادم العرض التوضيحي و prod. قطع المتراصة إلى قطع واستخدام Spring Boot قد فتح آفاقا جديدة لنا.
تمكن المطورون من تصحيح التعليمات البرمجية محليًا ، وذلك بتشغيل هذا الجزء فقط من الوظيفة المطلوبة لهذا الغرض. أخيرًا ، بدأ استخدام منصات الاختبار للأغراض المقصودة - هناك بالفعل رمز تصحيح أكثر أو أقل ، جاهز للاختبار الأولي والمؤهل. ولكن لا يزال هناك الكثير من الأعمال اليدوية التي ضاعت وقتنا الثمين والطاقة. بعد دراسة المشكلة والفرز بين الحلول ، استقرنا على مجموعة من Gradle + TeamCity. نظرًا لأن المشروع قد تم إنشاؤه بواسطة Gradle ، مضيفًا شيئًا جديدًا غير منطقي ، وتبين أن النصوص التي تمت كتابتها كانت مستقلة عن النظام الأساسي ، يمكن تشغيلها على أي نظام تشغيل ، عن بُعد أو محليًا. وسمح هذا ليس فقط باستخدام أي حل ل CI / CD ، ولكن أيضا غير مؤلم تغيير النظام الأساسي إلى أي حل آخر. تم اختيار TeamCity بسبب وظيفته المضمنة الغنية ، وجود مجتمع كبير وقائمة طويلة من المكونات الإضافية لجميع المناسبات ، بالإضافة إلى التكامل مع بيئة تطوير Intellij IDEA.
في الوقت الحالي ، هناك أكثر من 100 نصوص تحسينية وأكثر من 300 مهمة في نظام CI لتشغيلها بمعلمات مختلفة. هذا ليس مجرد نشر لاختبار المقاعد والتسليم للإنتاج ، ولكن أيضًا العمل مع السجلات وإدارة الخادم والتوجيه والحلول فقط للمهام الروتينية من نفس النوع. تمت إزالة بعض المهام من أكتافنا. تمكن مديرو المحتوى من مسح ذاكرة التخزين المؤقت بأنفسهم. حصل الرجال من الدعم الفني على فرصة لسحب الخدمات الفردية من تلقاء أنفسهم ، للقيام بأعمال الإنعاش الأولية. أصبح نوم المطورين أعمق وأكثر هدوءًا.
المرحلة 5. CMS الخاصة.بعد أن كان من الممكن الاستخلاص من نظام إدارة المحتوى التجاري ، أصبح من الممكن استلام البيانات من مصدر آخر ، دون إعادة كتابة رمز التطبيق. مكان الحصول على هذه البيانات أو تلك التي تم تحديدها بواسطة خدمة المحتوى. بعد البحث عن حلول جاهزة وتحليلها ، قررنا أن نكتب نظامنا لإدارة المحتوى ، حيث لم يلبي أي منها احتياجاتنا بالكامل. كتابة CMS الخاصة بك هي عملية لا نهاية لها ، والاحتياجات والرغبات الجديدة تظهر باستمرار. اخترنا بعض الميزات الأساسية وذهبنا إلى عالم جميل من التنمية. لإطلاق الإصدار الأول في prod ، كان لدينا شهر ونصف الشهر. نظرًا لأن الوظيفة جاهزة في نظام إدارة المحتوى الجديد ، فنحن ننقل المحتوى من القديم إلى ذلك. كل الصفحات الجديدة لا علاقة لها بـ dotCMS بعد الآن. بمرور الوقت ، جعل ذلك من الممكن التخلي عن النسخة المدفوعة والتحول إلى إصدار المجتمع ، وفي المستقبل التخلي تمامًا عن شيء تابع لجهة خارجية.
المرحلة 6. التدقيق.بعد أن طوى سروالنا ، بدأنا رحلتنا إلى عالم برمجة محب. كانت هذه المرحلة لمشروعي هي الأخيرة في عملية إعادة الهيكلة. يستمر حتى يومنا هذا. المجال الرئيسي الذي ظهرت فيه هذه المرحلة بشكل عام هو التوسع. تتيح لك حزمة Docker + Kubernetes + Consul ليس فقط تسهيل النشر على خوادم مختلفة وإدارة كل خدمة على حدة ، ولكن أيضًا توسيع نطاق التطبيق بالكامل بمرونة ، والقيام بذلك فقط في تلك الأماكن التي تكون ضرورية ، وفقط طالما كان ذلك مطلوبًا. بمزيد من التفصيل ، لا يمكنني الطلاء إلا عند التبديل الكامل لهذا الحل في الإنتاج.
... وأخيرا بنيت. الصيحة!
لقد مر عام منذ بدء تحديث التطبيق. هذه هي الآن 26 خدمات REST مكتوبة في Spring Boot. يحتوي كل منها على وثائق API مفصلة في Swagger UI. تتم كتابة جانب العميل في React.js وفصلها عن جانب الخادم. جميع الصفحات الرئيسية للبوابة مغطاة بالاختبارات. أجريت العديد من المبيعات الرئيسية. إن الانتقال إلى التقنيات الحديثة والتخلص من الشفرة القديمة والتشغيل المستقر للخوادم يحفزان بشدة المطورين. من "كما قلنا ، نحن نفعل ذلك" ، انتقل المشروع إلى الاتجاه السائد ، حيث يهتم الجميع بالنجاح ، ويقدم خياراتهم الخاصة للتحسين والتحسين. لقد تغير موقف العميل تجاه الفريق ، وتم إنشاء جو ودود.
هذا هو نتيجة عمل الفريق بأكمله ، كل مطور واختبار ، المديرين والعملاء. كان من الصعب جدا ، العصبي وأحيانا على وشك ارتكاب خطأ. لكن تماسك الفريق والتخطيط الكفء والوعي بالنتائج مكّنا من التغلب على جميع الصعوبات.