إعادة كتابة الرمز القديم كرحلة إلى طبيب الأسنان - يبدو أن الجميع يفهم أنه يجب عليهم الذهاب ، لكنهم ما زالوا يماطلون ويحاولون تأخير ما لا مفر منه ، لأنهم يعرفون أنه سيؤذي. في حالتنا ، كانت الأمور أسوأ: كان علينا إعادة كتابة الجزء الرئيسي من النظام ، وبسبب الظروف الخارجية لم نتمكن من استبدال الأجزاء القديمة من التعليمات البرمجية بأجزاء جديدة في أجزاء ، كل ذلك مرة واحدة وبالكامل. وكل هذا في ظروف ضيق الوقت والموارد والوثائق ، ولكن مع متطلبات الإدارة أنه نتيجة "للعملية" لا ينبغي أن يعاني أي عميل.
تحت القصة ، قصة كيفية إعادة كتابة المكون الرئيسي للمنتج بتاريخ 17 عامًا (!) من Scheme إلى Clojure ، وعمل كل هذا على الفور (جيدًا تقريبًا :)).

17 عاما في "المراقبة"
Solar Dozor هو نظام DLP له تاريخ طويل جدًا. ظهرت النسخة الأولى في عام 2001 كخدمة صغيرة نسبيًا لتصفية حركة مرور البريد. على مدى 17 عامًا ، نما المنتج ليصبح حزمة برمجيات كبيرة تجمع وتصفية وتحلل المعلومات غير المتجانسة التي تعمل داخل المنظمة وتحمي أعمال العملاء من التهديدات الداخلية.
عند تطوير الإصدار السادس من Solar Dozor ، هزنا المنتج بشكل حاسم ، وأخرجنا العكازات القديمة من الشفرة
واستبدلناها بأخرى جديدة ، وقمنا بتحديث الواجهة ، ونراجع الوظيفة في اتجاه الحقائق الحديثة - بشكل عام ، جعل المنتج هندسيًا وفكريًا أكثر شمولية.
في ذلك الوقت ، تحت غطاء Solar Dozor المحدث ، كانت هناك طبقة ضخمة من رمز الإرث المتآلف - خدمة التصفية للغاية ، التي نمت تدريجيًا طوال هذه السنوات الـ 17 إلى وظائف جديدة ، تجسد كلًا من الحلول طويلة المدى والمهام التجارية قصيرة المدى ، لكنها تمكنت من البقاء داخل الهندسة المعمارية الأصلية نماذج.
خدمة التصفيةوغني عن القول أن إدخال أي تغييرات على مثل هذا الرمز القديم يتطلب حساسية خاصة. كان على المطورين توخي الحذر الشديد لعدم إتلاف الوظائف التي تم إنشاؤها منذ عقد عن طريق الخطأ. بالإضافة إلى ذلك ، اضطرت الحلول الجديدة المثيرة للاهتمام إلى الضغط على السرير المعماري Procrustean ، الذي تم اختراعه في فجر العصر.
لقد ظهر منذ فترة وجيزة فهم الحاجة إلى تحديث النظام. لكن روح لمس خدمة نظام ضخمة وقديمة كانت تفتقر بشكل واضح.
لا تحاول تأخير ما لا مفر منه
تتميز المنتجات التي لها تاريخ طويل من التطوير بميزة مثيرة للاهتمام. بغض النظر عن مدى غرابة أي قطعة من الوظائف قد تبدو ، إذا نجحت في البقاء حتى يومنا هذا ، فهذا يعني أنه تم إنشاؤها ليس من الأفكار النظرية للمطورين ، ولكن استجابة للاحتياجات المحددة للعملاء.
في هذه الحالة ، قد لا يكون هناك حديث عن أي استبدال مرحلي. كان من المستحيل قطع وإعادة الوظائف في الأجزاء ، لأن جميع هذه الأجزاء كانت مطلوبة من قبل العملاء ، ولم نتمكن من "إغلاقها لإعادة البناء". كان من الضروري إزالة الخدمة القديمة بعناية وتزويدها باستبدال كامل المواصفات. بشكل عام فقط ، مرة واحدة فقط.
كان تحسين عملية تطوير المنتج ، وسرعة إجراء التغييرات ، وتحسين الجودة ككل شرطًا ضروريًا ولكنه ليس كافيًا. تساءلت الإدارة عن الفوائد التي سيجلبها التغيير لعملائنا. كان الجواب هو توسيع مجموعة الواجهات للتفاعل مع أنظمة الاعتراض الجديدة ، والتي من شأنها أن توفر ملاحظات سريعة وتسمح للمعترضين بالاستجابة بسرعة أكبر للحوادث.
كان علينا أيضًا أن نكافح للحد من استهلاك الموارد ، والحفاظ على (وزيادة مثالية) معدل المعالجة الحالي.
القليل عن الحشو
خلال مسار تطوير المنتج ، اتجه فريق Solar Dozor نحو نهج وظيفي. هذا يؤدي إلى اختيار غير قياسي إلى حد ما من لغات البرمجة لصناعة ناضجة. في مراحل مختلفة من حياة النظام ، كانت هذه المخططات ، OCaml ، Scala ، Clojure ، بالإضافة إلى C (++) التقليدية وجافا.
تمت كتابة وتطوير خدمة التصفية الرئيسية والخدمات الأخرى التي تساعد في استقبال الرسائل وإرسالها بلغة النظام في تطبيقاتها المختلفة (تم استخدام الأخير بواسطة Racket). بغض النظر عن مدى رغبة المرء في الغناء على بساطة وأناقة هذه اللغة ، لا يسع المرء إلا أن يعترف بأن تطورها يلبي اهتمامات أكاديمية أكثر من اهتمامات صناعية. يمكن ملاحظة التأخر بشكل خاص بالمقارنة مع خدمات Solar Dozor الأخرى الأكثر حداثة ، والتي تم تطويرها بشكل أساسي على Scala و Clojure. كما تقرر تنفيذ الخدمة الجديدة في كلوجور.
كلوجر ؟!
هنا ، بالطبع ، يجب أن أقول بضع كلمات حول سبب اختيارنا لكلوجور كلغة التنفيذ الرئيسية.
أولاً ، لم أكن أرغب في فقدان التجربة الفريدة للفريق الذي يعمل على البرنامج. Clojure هو أيضًا عضو حديث في عائلة اللغات Lisp ، وعادة ما يكون التبديل من لغة Lisp إلى أخرى بسيطًا للغاية.
ثانيًا ، بفضل الالتزام بالمبادئ الوظيفية وعدد من الحلول المعمارية الفريدة ، يوفر Clojure سهولة غير مسبوقة في معالجة تدفقات البيانات. من المهم أيضًا أن يعمل Clojure على منصة JVM ، مما يعني أنه يمكنك استخدام قاعدة بيانات مشتركة مع خدمات أخرى في Java و Scala ، بالإضافة إلى استخدام العديد من الأدوات للتنميط وتصحيح الأخطاء.
ثالثًا ، كلوجور لغة مختصرة ومعبرة. هذا يجعل من السهل قراءة رمز شخص آخر ويسهل تمرير الرمز إلى زميل في الفريق.
أخيرًا ، نحن نقدر Clojure لسهولة تصميمه للنماذج الأولية وما يسمى التطوير المتمركز حول REPL. في أي موقف تقريبًا يكون هناك شك ، يمكنك ببساطة إنشاء نموذج أولي ومواصلة المناقشة بطريقة أكثر جوهرية ، باستخدام بيانات جديدة. يعطي التطوير الموجه ل REPL عائدًا سريعًا ، لأنه للتحقق من وظائف الوظيفة ، ليس من الضروري فقط إعادة ترجمة البرنامج ، ولكن حتى إعادة تشغيله (حتى إذا كان البرنامج خدمة موجودة على خادم بعيد).
بالنظر إلى المستقبل ، يمكنني أن أقول: أعتقد أننا لم نفقد الاختيار.
وضع الوظيفة شيئا فشيئا
عندما نتحدث عن بديل كامل المواصفات ، فإن السؤال الأول الذي يطرح نفسه هو جمع المعلومات حول الوظائف الموجودة.
لقد أصبحت هذه مهمة مثيرة للاهتمام. يبدو أن هنا نظام عمل ، وهنا وثائق له ، هنا أناس - خبراء يعملون بشكل وثيق مع النظام ويعلمون الآخرين عنه. ولكن للحصول على صورة كاملة من مجموعة متنوعة ، بل وأكثر من ذلك ، تبين أن متطلبات التطوير ليست بهذه البساطة.
إن جمع المتطلبات لا يعتبر عبثًا تخصصًا هندسيًا منفصلاً. ومن المفارقات أن التنفيذ الحالي هو دور بعض "المعايير الفاسدة". يوضح كيف وكيف يجب أن يعمل ، ولكن في نفس الوقت ، من المتوقع أن يتحول الإصدار الجديد بشكل أفضل من الإصدار الأصلي. من الضروري فصل اللحظات الضرورية للتنفيذ (عادة ما تتعلق بالواجهات الخارجية) عن تلك التي يمكن تحسينها وفقًا لتوقعات المستخدمين.
عملية تصفية الرسائلالوثائق ليست كافية
ما هي الوظيفة الفعلية للنظام؟ تم تقديم الإجابة على هذا السؤال من خلال أوصاف مختلفة ، مثل وثائق المستخدم والأدلة والمستندات المعمارية ، مما يعكس هيكل الخدمة في جوانب مختلفة. ولكن عندما يتعلق الأمر بذلك ، فأنت تفهم جيدًا مدى اختلاف الأفكار والواقع ، وعدد الفروق الدقيقة وغير المحسوبة للإمكانيات التي تحتوي عليها الشفرة القديمة.
أريد الاتصال بجميع المطورين. اعتني بكودك! هذا هو أهم ما لديك. لا تعتمد على الوثائق. ثق في شفرة المصدر فقط.
لحسن الحظ بالنسبة لنا ، فإن كود البرنامج ، نظرًا لطبيعة اللغة التي تم إنشاؤها لتدريس البرمجة ، سهل القراءة إلى حد ما حتى بالنسبة لشخص غير مدرب. الشيء الرئيسي هو التعود على بعض الأشكال الفردية التي تحمل لمسة خفيفة من Lisp-archaic.
بناء عملية
كان حجم العمل هائلاً ، والفريق صغير جدًا. لذلك لم يكن من دون صعوبات تنظيمية. لم يتوقف سير عمل الأخطاء وإصلاح الطلبات (والتحسينات الطفيفة) لخدمة التصفية القديمة حتى. كان يجب على المطورين تشتيت انتباههم بانتظام بهذه المهام.
لحسن الحظ ، كان من الممكن تجنب طلبات تضمين قطع جديدة من الوظائف الرائعة في الفلتر القديم. صحيح ، في ظل الوعد بتضمين هذه الوظيفة في خدمة جديدة. ومع ذلك ، كانت مجموعة مهام الإصدار تنمو ببطء ولكن بثبات.
العامل الآخر الذي أضاف الكثير من المتاعب كان التبعيات الخارجية للخدمة. كونها مكونًا مركزيًا ، تستخدم خدمة التصفية العديد من الخدمات لتفريغ المحتوى وتحليله (النصوص والصور والبصمات الرقمية وما إلى ذلك). استرشد العمل معهم جزئيًا بالقرارات المعمارية القديمة. أثناء عملية التطوير ، كان من الضروري أيضًا إعادة كتابة بعض المكونات بطريقة حديثة (وبعضها بلغة حديثة).
في مثل هذه الظروف ، تم بناء نظام للاختبار الوظيفي خطوة بخطوة. لقد قمنا بتطوير الخدمة إلى حالة معينة ، والتي تم تعزيزها من خلال الاختبار النشط ، ثم انتقلنا إلى تنفيذ حالة جديدة.
ابدأ التطوير
بادئ ذي بدء ، تم تنفيذ الإطار الرئيسي للخدمة ، والآليات الأساسية لاستقبال الرسائل وتفريغ الملفات. كان هذا هو الحد الأدنى المطلق اللازم لبدء الاختبار لسرعة وصحة الخدمة المستقبلية.
هنا يجب توضيح أن التفريغ يشير إلى العملية العودية للحصول على أجزاء من ملف واستخراج معلومات مفيدة منها. لذلك ، على سبيل المثال ، لا يمكن أن يحتوي مستند Word على نص فحسب ، بل يحتوي أيضًا على صور ، ومستند Excel مضمن ، وكائنات OLE ، والمزيد.
لا تميز آلية التفريغ بين استخدام المكتبات الداخلية أو البرامج الخارجية أو خدمات الجهات الخارجية ، مما يوفر واجهة واحدة لتنظيم تفريغ خطوط الأنابيب.
تكملة أخرى تجاه Clojure: حصلنا على نموذج أولي فعال ، حددنا فيه ملامح الوظائف المستقبلية ، في أقصر وقت ممكن.
DSL للسياسة
كانت الخطوة الثانية لإضافة التحقق من صحة الرسائل باستخدام سياسات التصفية.
لوصف السياسات ، تم إنشاء DSL خاص - لغة بسيطة بدون زخرفة ، مما سمح لنا بتقديم قواعد وشروط السياسة بشكل أو بآخر يقرأه الإنسان. يطلق عليه MFLang.
يتم تفسير البرنامج النصي على MFLang "على الطاير" في كود Clojure ، ويخزن نتائج الشيكات على الرسالة ، ويحتفظ بسجل مفصل للعمل (وبصراحة ، يستحق مقالة منفصلة).
استخدام DSL مناشدة للمختبرين. يسقط الحفر في قاعدة البيانات أو تنسيق التصدير! الآن أصبح من الممكن ببساطة إرسال القاعدة التي تم إنشاؤها للتحقق ، وعلى الفور أصبح من الواضح ما هي الشروط التي تم التحقق منها. كان من الممكن أيضًا الحصول على سجل مفصل للتحقق من الرسائل ، والذي يتضح منه البيانات التي تم أخذها للتحقق وما النتائج التي تم إرجاعها بواسطة وظيفة المقارنة.
يمكننا القول بثقة أن MFLang تبين أنها أداة لا تقدر بثمن على الإطلاق لوظائف التصحيح.
بكامل قوته
في المرحلة الثالثة ، تمت إضافة آلية لتطبيق الإجراءات التي حددتها السياسة الأمنية على الرسالة ، بالإضافة إلى عمليات ربط الخدمة التي تتيح إدراج مكونات جديدة في مجمع Solar Dozor. وأخيرًا ، تمكنا من إطلاق الخدمة وملاحظة نتيجة العمل بكل تنوعه.
كان السؤال الرئيسي ، بالطبع ، كيف تتوافق الوظيفة المنفذة مع ما كان متوقعًا ومدى تنفيذها بالكامل.
ألاحظ أنه إذا لم تكن الحاجة إلى اختبار الوحدة موضع تساؤل لفترة طويلة (على الرغم من أن ممارسات TDD نفسها لا تزال تسبب نقاشًا حيويًا) ، فإن إدخال الاختبار الآلي لوظائف النظام غالبًا ما يواجه مقاومة مفتوحة.
يساعد تطوير الاختبارات الذاتية جميع أعضاء الفريق على فهم عملية المنتج بشكل أفضل ، وتوفير الطاقة على الانحدار ، وغرس ثقة معينة في أداء المنتج. لكن عملية إنشائها محفوفة بعدد من الصعوبات - جمع البيانات اللازمة ، وتحديد مؤشرات الاهتمام وخيارات الاختبار. ينظر المبرمجون حتمًا إلى إنشاء الاختبارات الذاتية على أنها عمل جانبي اختياري ، ومن الأفضل تجنبه إن أمكن.
ولكن إذا تمكنت من التغلب على المقاومة ، يتم إنشاء أساس متين إلى حد ما يسمح لك ببناء فكرة عن صحة النظام.
نستبدل
ثم جاءت لحظة مهمة: أدرجنا الخدمة في حزمة التسليم. حتى الآن ، مع القديم. وهكذا ، يمكن لفريق واحد إجراء تغيير الإصدار ومقارنة سلوك الخدمات.
في هذا الوضع الموازي ، استمرت خدمة التصفية الجديدة لإصدار واحد. خلال هذا الوقت ، تمكنا من جمع إحصاءات إضافية عن العمل ، وتحديد وتنفيذ التحسينات اللازمة.
أخيرًا ، بعد أن جمعنا قوتنا ، أزلنا خدمة التصفية القديمة من المنتج. ذهبت المرحلة الأخيرة من القبول الداخلي ، وتم إصلاح الأخطاء ، وبدأ المطورون في التحول تدريجيًا إلى مهام أخرى. بطريقة ما غير محسوسة ، بدون ضجة وتصفيق ، تم إصدار منتج بخدمة جديدة.
وفقط عندما بدأت الأسئلة تأتي من فريق التنفيذ ، جاء الفهم - الخدمة التي كنا نعمل عليها لفترة طويلة ، كانت بالفعل في المواقع و ... تعمل!
بالطبع ، كانت هناك أخطاء وتحسينات طفيفة ، ومع ذلك ، بعد شهر من الاستخدام النشط ، أصدر العملاء قرارًا: تسبب تقديم منتج بإصدار جديد من خدمة التصفية في حدوث مشكلات أقل من تقديم الإصدارات السابقة. مرحبًا! يبدو أننا فعلنا ذلك!
في النهاية
استغرق تطوير خدمة ترشيح جديدة حوالي عام ونصف. أطول مما كان يعتقد في الأصل ، ولكن ليس حرجًا ، خاصة وأن كثافة العمل الفعلية للعمل تزامنت مع التقييم الأولي. والأهم من ذلك ، تمكنا من تلبية توقعات الإدارة والعملاء ووضع الأساس لتحسين المنتج في المستقبل. بالفعل في الحالة الحالية ، يمكنك أن ترى انخفاضًا كبيرًا في استهلاك الموارد - على الرغم من حقيقة أن المنتج لا يزال لديه فرص وافرة للتحسين.
يمكنني إضافة بعض الانطباعات الشخصية.
إن استبدال مكون مركزي بتاريخ طويل هو نفس الهواء النقي للتنمية. لأول مرة منذ وقت طويل ، هناك ثقة في أن مراقبة المنتج تعود إلى أيدينا.
من الصعب المبالغة في تقدير فوائد عملية الاتصال والتطوير المنظمة بشكل صحيح. في هذه الحالة ، كان من المهم إنشاء عمل ليس داخل الفريق ، كما هو الحال مع العديد من المستهلكين للمنتج ، الذين لديهم تفضيلات وتوقعات واضحة منذ فترة طويلة من النظام ، ورغبات غامضة إلى حد ما.
بالنسبة لنا ، كانت هذه أول تجربة في تطوير مثل هذا المشروع الواسع النطاق في كلوجور. في البداية ، كانت هناك مخاوف تتعلق بالطبيعة الديناميكية للغة والسرعة والتسامح مع الخطأ. لحسن الحظ ، لم تتحقق.
يبقى فقط أن تتمنى أن يعمل المكون الجديد طويلًا ونجاحًا مثل سلفه.