Operasi jantung: bagaimana kami menulis ulang komponen utama sistem DLP

Menulis ulang kode warisan sebagai perjalanan ke dokter gigi - sepertinya semua orang mengerti bahwa mereka harus pergi, tetapi mereka masih menunda-nunda dan mencoba menunda hal yang tak terhindarkan, karena mereka tahu itu akan menyakitkan. Dalam kasus kami, masalahnya bahkan lebih buruk: kami harus menulis ulang bagian kunci dari sistem, dan karena keadaan eksternal kami tidak dapat mengganti potongan kode lama dengan bagian-bagian baru di bagian-bagian, hanya secara bersamaan dan secara penuh. Dan semua ini dalam kondisi kurangnya waktu, sumber daya dan dokumentasi, tetapi dengan persyaratan manajemen bahwa sebagai akibat dari "operasi" tidak ada pelanggan yang harus menderita.

Di bawah potongan, kisah tentang bagaimana kita menulis ulang komponen utama produk dengan sejarah 17 tahun (!) Dari Skema ke Clojure, dan semuanya bekerja segera (well, hampir :)).



17 tahun di "Watch"


Solar Dozor adalah sistem DLP dengan sejarah yang sangat panjang. Versi pertama muncul kembali pada tahun 2001 sebagai layanan yang relatif kecil untuk menyaring lalu lintas surat. Lebih dari 17 tahun, produk ini telah berkembang menjadi paket perangkat lunak besar yang mengumpulkan, menyaring, dan menganalisis informasi heterogen yang berjalan di dalam organisasi dan melindungi bisnis pelanggan dari ancaman internal.

Ketika mengembangkan versi keenam Solar Dozor, kami mengguncang produk dengan tegas, membuang kruk tua dari kode dan menggantinya dengan yang baru , memperbarui antarmuka, merevisi fungsi ke arah realitas modern - secara umum, membuat produk secara arsitektur dan konseptual lebih holistik.

Pada saat itu, di bawah kap Solar Dozor yang diperbarui, ada lapisan besar kode warisan monolitik - layanan yang sangat penyaringan, yang selama 17 tahun ini secara bertahap telah tumbuh menjadi fungsi baru, mewujudkan solusi jangka panjang dan tugas bisnis jangka pendek, tetapi berhasil tetap dalam arsitektur asli paradigma.


Layanan penyaringan

Tak perlu dikatakan, pengenalan setiap perubahan pada kode kuno seperti itu membutuhkan kelezatan khusus. Para pengembang harus sangat berhati-hati untuk tidak secara tidak sengaja merusak fungsi yang dibuat satu dekade lalu. Selain itu, solusi menarik yang cukup baru dipaksa masuk ke dalam arsitektur Procrustean, yang ditemukan pada awal era.

Memahami bahwa kebutuhan telah muncul untuk memperbarui sistem telah muncul beberapa waktu yang lalu. Tetapi semangat menyentuh layanan sistem yang besar dan kuno jelas kurang.

Tidak berusaha menunda yang tak terhindarkan


Produk dengan sejarah panjang pengembangan memiliki fitur yang menarik. Betapapun anehnya fungsi apa pun yang tampak, jika telah berhasil bertahan hingga hari ini, ini berarti ia diciptakan bukan dari ide-ide teoretis para pengembang, tetapi sebagai respons terhadap kebutuhan spesifik pelanggan.

Dalam situasi ini, tidak mungkin ada pembicaraan tentang penggantian bertahap. Mustahil untuk memotong dan mengulang fungsi di bagian-bagian, karena semua bagian ini diminati oleh pelanggan, dan kami tidak bisa "menutupnya untuk rekonstruksi". Itu perlu untuk hati-hati menghapus layanan lama dan menyediakannya dengan penggantian berfitur lengkap. Hanya secara keseluruhan, hanya sekaligus.

Meningkatkan proses pengembangan produk, kecepatan membuat perubahan, dan meningkatkan kualitas secara keseluruhan adalah kondisi yang diperlukan tetapi tidak cukup. Manajemen bertanya-tanya perubahan apa yang akan terjadi pada pelanggan kami. Jawabannya adalah untuk memperluas set antarmuka untuk berinteraksi dengan sistem intersepsi baru, yang akan memberikan umpan balik cepat dan memungkinkan pencegat untuk merespons lebih cepat terhadap insiden.

Kami juga harus berjuang untuk mengurangi konsumsi sumber daya, mempertahankan (dan idealnya meningkatkan) tingkat pemrosesan saat ini.

Sedikit tentang isian


Sepanjang jalur pengembangan produk, tim Solar Dozor cenderung menuju pendekatan fungsional. Ini mengarah ke pilihan bahasa pemrograman yang agak tidak standar untuk industri yang matang. Pada berbagai tahap kehidupan sistem, ini adalah Skema, OCaml, Scala, Clojure, di samping C tradisional (++) dan Jawa.

Layanan penyaringan utama dan layanan lain yang membantu menerima dan mengirimkan pesan ditulis dan dikembangkan dalam bahasa Skema dalam berbagai implementasinya (yang terakhir digunakan oleh Racket). Tidak peduli seberapa banyak orang ingin menyanyikan pujian kesederhanaan dan keanggunan dari bahasa ini, kita tidak bisa tidak mengakui bahwa perkembangannya lebih memenuhi kepentingan akademis daripada yang industri. Kelambatan ini terutama terlihat dibandingkan dengan layanan Solar Dozor lainnya yang lebih modern, yang dikembangkan terutama pada Scala dan Clojure. Layanan baru juga diputuskan untuk diterapkan di Clojure.

Clojure?!


Di sini, tentu saja, saya harus mengatakan beberapa kata tentang mengapa kami memilih Clojure sebagai bahasa implementasi utama.

Pertama, saya tidak ingin kehilangan pengalaman unik tim yang mengembangkan Skema. Clojure juga anggota modern dari keluarga bahasa Lisp, dan beralih dari satu Lisp ke yang lain biasanya cukup sederhana.

Kedua, berkat komitmen pada prinsip-prinsip fungsional dan sejumlah solusi arsitektur yang unik, Clojure memberikan kemudahan memanipulasi aliran data yang belum pernah terjadi sebelumnya. Juga penting bahwa Clojure beroperasi pada platform JVM, yang berarti bahwa Anda dapat menggunakan database bersama dengan layanan lain di Jawa dan Scala, serta menggunakan berbagai alat untuk profil dan debugging.

Ketiga, Clojure adalah bahasa yang ringkas dan ekspresif. Ini membuatnya mudah untuk membaca kode orang lain dan membuatnya mudah untuk menyampaikan kode kepada rekan satu tim.

Akhirnya, kami menghargai Clojure karena kemudahannya membuat prototip dan disebut pengembangan REPL-sentris. Di hampir semua situasi di mana ada keraguan, Anda bisa membuat prototipe dan melanjutkan diskusi dengan cara yang lebih substantif, dengan data baru. Pengembangan berorientasi REPL memberikan pengembalian cepat, karena untuk memeriksa fungsionalitas suatu fungsi, tidak hanya diperlukan untuk mengkompilasi ulang program, tetapi bahkan memulai kembali (bahkan jika program tersebut adalah layanan yang terletak di server jarak jauh).

Ke depan, saya dapat mengatakan: Saya pikir kita tidak kehilangan pilihan.

Menempatkan fungsionalitas sedikit demi sedikit


Ketika kita berbicara tentang penggantian berfitur lengkap, pertanyaan pertama yang muncul adalah pengumpulan informasi tentang fungsi yang ada.

Ini menjadi tugas yang cukup menarik. Tampaknya di sini adalah sistem yang berfungsi, di sini adalah dokumentasi untuk itu, di sini ada orang - ahli yang bekerja sama dengan sistem dan mengajar orang lain tentang itu. Tetapi untuk mendapatkan gambaran lengkap dari seluruh varietas, dan terlebih lagi, persyaratan untuk pengembangan ternyata tidak begitu sederhana.

Pengumpulan persyaratan tidak sia-sia dianggap sebagai disiplin ilmu teknik yang terpisah. Implementasi yang ada secara paradoks ternyata menjadi peran beberapa "standar rusak". Ini menunjukkan bagaimana dan bagaimana cara kerjanya, tetapi pada saat yang sama, pengembang diharapkan bahwa versi baru akan menjadi lebih baik daripada yang asli. Hal ini diperlukan untuk memisahkan momen yang diperlukan untuk implementasi (biasanya terkait dengan antarmuka eksternal) dari yang dapat ditingkatkan sesuai dengan harapan pengguna.


Proses Penyaringan Pesan

Dokumentasi tidak cukup


Apa fungsi sebenarnya dari sistem? Jawaban untuk pertanyaan ini diberikan oleh berbagai deskripsi, seperti dokumentasi pengguna, manual dan dokumen arsitektur, yang mencerminkan struktur layanan dalam berbagai aspek. Tetapi ketika sampai pada itu, Anda mengerti betul seberapa banyak ide dan kenyataan berbeda, berapa banyak nuansa dan tidak terhitung untuk kemungkinan kode lama berisi.

Saya ingin menghubungi semua pengembang. Jaga kode Anda! Ini adalah aset terpenting Anda. Jangan mengandalkan dokumentasi. Percaya hanya kode sumber.

Untungnya bagi kita, kode Skema, karena sifat bahasa yang diciptakan untuk pengajaran pemrograman, cukup mudah dibaca bahkan untuk orang yang tidak terlatih. Hal utama adalah membiasakan diri dengan beberapa bentuk individual yang membawa sentuhan ringan Lisp-archaic.

Bangun proses


Volume pekerjaan sangat besar, dan timnya sangat kecil. Jadi itu bukan tanpa kesulitan organisasi. Alur kerja bug dan memperbaiki permintaan (dan perbaikan kecil) ke layanan penyaringan lama bahkan tidak berhenti. Pengembang secara teratur harus terganggu oleh tugas-tugas ini.

Untungnya, adalah mungkin untuk menangkal permintaan untuk menanamkan potongan-potongan fungsionalitas baru di filter lama. Benar, di bawah janji untuk menyematkan fungsi ini dalam layanan baru. Namun, serangkaian tugas rilis perlahan tapi pasti berkembang.

Faktor lain yang menambah banyak masalah adalah ketergantungan eksternal layanan. Menjadi komponen utama, layanan penyaringan menggunakan berbagai layanan untuk membongkar dan menganalisis konten (teks, gambar, sidik jari digital, dll.). Bekerja dengan mereka sebagian dipandu oleh keputusan arsitektur lama. Selama proses pengembangan, perlu juga menulis ulang beberapa komponen dengan cara modern (dan beberapa dalam bahasa modern).

Dalam kondisi seperti itu, sistem pengujian fungsional langkah demi langkah dibangun. Kami semacam mengembangkan layanan ke kondisi tertentu, yang diperkuat oleh pengujian aktif, dan kemudian beralih ke penerapan yang baru.

Mulai pengembangan


Pertama-tama, kerangka kerja utama layanan, mekanisme dasar untuk menerima pesan dan membongkar file diimplementasikan. Ini adalah minimum absolut yang diperlukan untuk memulai pengujian untuk kecepatan dan kebenaran layanan di masa depan.

Di sini harus diklarifikasi bahwa membongkar mengacu pada proses rekursif untuk mendapatkan bagian dari file dan mengekstraksi informasi yang berguna dari mereka. Jadi, misalnya, dokumen Word tidak hanya berisi teks, tetapi juga gambar, dokumen Excel yang disematkan, objek OLE, dan banyak lagi.

Mekanisme pembongkaran tidak membedakan antara penggunaan perpustakaan internal, program eksternal atau layanan pihak ketiga, menyediakan antarmuka tunggal untuk mengatur jaringan pipa pembongkar.

Pujian lain terhadap Clojure: kami mendapatkan prototipe yang berfungsi, di mana kami menguraikan garis besar fungsi di masa depan, dalam waktu sesingkat mungkin.

DSL untuk politik


Langkah kedua adalah menambahkan validasi pesan menggunakan kebijakan penyaringan.

Untuk menggambarkan kebijakan, DSL khusus dibuat - bahasa sederhana tanpa embel-embel, yang memungkinkan kami untuk menyajikan aturan dan ketentuan kebijakan dalam bentuk yang lebih atau kurang dapat dibaca oleh manusia. Itu disebut MFLang.

Skrip pada MFLang "on the fly" ditafsirkan dalam kode Clojure, menyimpan hasil pemeriksaan pada pesan, menyimpan log kerja yang terperinci (dan, sejujurnya, layak mendapatkan artikel terpisah).

Penggunaan DSL menarik bagi penguji. Turun dengan menggali ke dalam basis data atau format ekspor! Sekarang dimungkinkan untuk hanya mengirim aturan yang dihasilkan untuk verifikasi, dan segera menjadi jelas kondisi apa yang diperiksa. Juga dimungkinkan untuk mendapatkan log verifikasi pesan terperinci, dari mana jelas data apa yang diambil untuk verifikasi dan hasil apa yang dikembalikan oleh fungsi perbandingan.

Kita dapat mengatakan dengan yakin bahwa MFLang ternyata menjadi alat yang sangat berharga untuk fungsi debugging.

Dengan kekuatan penuh


Pada tahap ketiga, mekanisme ditambahkan untuk menerapkan tindakan yang ditentukan oleh kebijakan keamanan untuk pesan, serta ikatan layanan yang memungkinkan dimasukkannya komponen baru di kompleks Solar Dozor. Akhirnya, kami dapat meluncurkan layanan dan mengamati hasil pekerjaan dalam semua keragamannya.

Pertanyaan utama, tentu saja, adalah bagaimana fungsionalitas yang diimplementasikan sesuai dengan apa yang diharapkan dan seberapa penuh mengimplementasikannya.

Saya perhatikan bahwa jika kebutuhan untuk pengujian unit belum dipertanyakan sejak lama (meskipun praktik TDD sendiri masih menyebabkan debat yang hidup), pengenalan pengujian otomatis fungsionalitas sistem sering mengalami resistensi terbuka.

Pengembangan autotests membantu semua anggota tim untuk lebih memahami proses produk, menghemat energi pada regresi, menanamkan rasa percaya diri tertentu dalam kinerja produk. Tetapi proses penciptaan mereka penuh dengan sejumlah kesulitan - mengumpulkan data yang diperlukan, menentukan indikator yang menarik dan pilihan pengujian. Programmer mau tidak mau menganggap penciptaan autotes sebagai opsional, kerja sampingan, yang sebaiknya dihindari jika mungkin.

Tetapi jika Anda berhasil mengatasi perlawanan, dibuatlah fondasi yang agak kokoh yang memungkinkan Anda membangun gagasan tentang kesehatan sistem.

Kami ganti


Dan kemudian momen penting datang: kami memasukkan layanan dalam paket pengiriman. Sejauh ini, bersama dengan yang lama. Dengan demikian, satu tim dapat melakukan perubahan versi dan membandingkan perilaku layanan.

Dalam mode paralel ini, layanan penyaringan baru berlangsung selama satu rilis. Selama waktu ini, kami berhasil mengumpulkan statistik tambahan tentang pekerjaan, menguraikan, dan mengimplementasikan peningkatan yang diperlukan.

Akhirnya, setelah mengumpulkan kekuatan kami, kami menghapus layanan penyaringan lama dari produk. Tahap akhir penerimaan internal berjalan, bug diperbaiki, pengembang mulai secara bertahap beralih ke tugas lain. Entah bagaimana tanpa disadari, tanpa gembar-gembor dan tepuk tangan, sebuah produk dirilis dengan layanan baru.

Dan hanya ketika pertanyaan mulai datang dari tim implementasi, pengertian datang - layanan yang telah kami kerjakan begitu lama, sudah ada di lokasi dan ... bekerja!

Tentu saja, ada bug dan perbaikan kecil, namun, setelah satu bulan digunakan secara aktif, pelanggan mengeluarkan putusan: memperkenalkan produk dengan versi baru dari layanan penyaringan menyebabkan lebih sedikit masalah daripada memperkenalkan versi sebelumnya. Hai! Sepertinya kita berhasil!

Pada akhirnya


Pengembangan layanan penyaringan baru membutuhkan waktu sekitar satu setengah tahun. Lebih lama dari perkiraan semula, tetapi tidak kritis, terutama karena intensitas kerja aktual dari pekerjaan itu bertepatan dengan penilaian awal. Lebih penting lagi, kami dapat memenuhi harapan manajemen dan pelanggan dan meletakkan dasar untuk peningkatan produk di masa depan. Sudah dalam kondisi saat ini Anda dapat melihat pengurangan yang signifikan dalam konsumsi sumber daya - terlepas dari kenyataan bahwa produk masih memiliki banyak peluang untuk optimasi.

Saya dapat menambahkan beberapa kesan pribadi.

Mengganti komponen sentral dengan sejarah panjang adalah menghirup udara segar untuk pengembangan. Untuk pertama kalinya dalam waktu yang lama, ada kepercayaan bahwa kendali produk akan kembali ke tangan kita.

Sulit untuk melebih-lebihkan manfaat dari proses komunikasi dan pengembangan yang terorganisir dengan baik. Dalam hal ini, penting untuk membangun pekerjaan tidak begitu banyak dalam tim, seperti halnya dengan banyak konsumen produk, yang memiliki preferensi dan harapan yang jelas dari sistem, dan keinginan yang agak kabur.

Bagi kami, ini adalah pengalaman pertama dalam mengembangkan proyek berskala besar di Clojure. Awalnya, ada kekhawatiran terkait dengan sifat dinamis dari bahasa, kecepatan dan toleransi kesalahan. Untungnya, mereka tidak terwujud.

Tinggal berharap komponen baru itu berfungsi selama dan berhasil seperti pendahulunya.

Source: https://habr.com/ru/post/id419385/


All Articles