Herzchirurgie: Wie wir die Hauptkomponente eines DLP-Systems umgeschrieben haben

Umschreiben des Legacy-Codes als Reise zum Zahnarzt - es scheint, als ob jeder versteht, dass er gehen sollte, aber sie zögern immer noch und versuchen, das Unvermeidliche zu verzögern, weil sie wissen, dass es wehtun wird. In unserem Fall war es noch schlimmer: Wir mussten den Schlüsselteil des Systems neu schreiben, und aufgrund äußerer Umstände konnten wir die alten Codeteile nicht durch neue Teile in Teilen ersetzen, nur auf einmal und vollständig. Und das alles unter Bedingungen von Zeit-, Ressourcen- und Dokumentationsmangel, aber mit der Anforderung des Managements, dass infolge des „Betriebs“ kein Kunde leiden sollte.

Unter dem Schnitt die Geschichte, wie wir die Hauptkomponente des Produkts mit einer 17-jährigen Geschichte (!) Von Schema zu Clojure umgeschrieben haben, und alles hat sofort funktioniert (na ja, fast :)).



17 Jahre in der "Uhr"


Solar Dozor ist ein DLP-System mit einer sehr langen Geschichte. Die erste Version erschien 2001 als relativ kleiner Dienst zum Filtern des E-Mail-Verkehrs. Über 17 Jahre hat sich das Produkt zu einem großen Softwarepaket entwickelt, das heterogene Informationen innerhalb des Unternehmens sammelt, filtert und analysiert und das Geschäft der Kunden vor internen Bedrohungen schützt.

Bei der Entwicklung der 6. Version von Solar Dozor haben wir das Produkt entscheidend geschüttelt, alte Krücken aus dem Code entfernt und durch neue ersetzt , die Benutzeroberfläche aktualisiert, die Funktionalität in Richtung moderner Realität überarbeitet - im Allgemeinen das Produkt architektonisch und konzeptionell ganzheitlicher gestaltet.

Zu dieser Zeit gab es unter der Haube des aktualisierten Solar Dozor eine riesige Schicht monolithischen Legacy-Codes - den Filterdienst, der in all den 17 Jahren allmählich zu neuen Funktionen gewachsen ist, die sowohl langfristige Lösungen als auch kurzfristige Geschäftsaufgaben verkörpern, aber innerhalb der ursprünglichen Architektur bleiben konnten Paradigmen.


Filterdienst

Unnötig zu erwähnen, dass die Einführung von Änderungen an einem solchen alten Code besondere Sorgfalt erforderte. Die Entwickler mussten äußerst vorsichtig sein, um die vor einem Jahrzehnt erstellte Funktionalität nicht versehentlich zu ruinieren. Darüber hinaus mussten sich ganz neue interessante Lösungen in das zu Beginn der Ära erfundene prokrustäische Architekturbett drängen.

Das Verständnis, dass die Notwendigkeit einer Aktualisierung des Systems aufgetreten ist, ist vor einiger Zeit aufgetaucht. Aber der Geist, einen riesigen und alten Systemdienst zu berühren, fehlte eindeutig.

Nicht versuchen, das Unvermeidliche zu verzögern


Produkte mit einer langen Entwicklungsgeschichte haben ein interessantes Merkmal. Unabhängig davon, wie seltsam eine Funktionalität erscheinen mag, wenn sie bis heute erfolgreich überlebt hat, bedeutet dies, dass sie nicht aus den theoretischen Ideen der Entwickler erstellt wurde, sondern auf die spezifischen Bedürfnisse der Kunden zugeschnitten ist.

In dieser Situation konnte von einem schrittweisen Austausch keine Rede sein. Es war unmöglich, die Funktionalität in Teilen zu schneiden und zu wiederholen, da alle diese Teile von den Kunden nachgefragt wurden und wir sie nicht „für den Wiederaufbau schließen“ konnten. Es war notwendig, den alten Service sorgfältig zu entfernen und ihn mit einem voll ausgestatteten Ersatz zu versehen. Nur ganz, nur auf einmal.

Die Verbesserung des Produktentwicklungsprozesses, die Geschwindigkeit, mit der Änderungen vorgenommen werden, und die Verbesserung der Qualität insgesamt waren eine notwendige, aber nicht ausreichende Bedingung. Das Management fragte sich, welche Vorteile eine Änderung für unsere Kunden bringen würde. Die Antwort bestand darin, die Schnittstellen für die Interaktion mit neuen Abhörsystemen zu erweitern, um schnelles Feedback zu erhalten und es den Abfangjägern zu ermöglichen, schneller auf Vorfälle zu reagieren.

Wir mussten auch kämpfen, um den Ressourcenverbrauch zu senken und die aktuelle Verarbeitungsrate beizubehalten (und im Idealfall zu erhöhen).

Ein bisschen über das Füllen


Während des gesamten Produktentwicklungspfades tendierte das Solar Dozor-Team zu einem funktionalen Ansatz. Dies führt zu einer eher ungewöhnlichen Auswahl an Programmiersprachen für eine reife Branche. In verschiedenen Phasen des Systemlebens waren dies neben traditionellem C (++) und Java Scheme, OCaml, Scala, Clojure.

Der Hauptfilterdienst und andere Dienste, die das Empfangen und Senden von Nachrichten unterstützen, wurden in der Schemasprache in ihren verschiedenen Implementierungen geschrieben und entwickelt (letzterer wurde von Racket verwendet). Egal wie sehr man die Einfachheit und Eleganz dieser Sprache loben möchte, man kann nur zugeben, dass ihre Entwicklung mehr akademischen als industriellen Interessen entspricht. Die Verzögerung macht sich insbesondere im Vergleich zu anderen, moderneren Solar Dozor-Diensten bemerkbar, die hauptsächlich auf Scala und Clojure entwickelt werden. Der neue Service sollte auch in Clojure implementiert werden.

Clojure ?!


Hier muss ich natürlich ein paar Worte darüber sagen, warum wir Clojure als Hauptimplementierungssprache gewählt haben.

Erstens wollte ich nicht die einzigartige Erfahrung des Teams verlieren, das sich auf Scheme entwickelt. Clojure ist auch ein modernes Mitglied der Lisp-Sprachfamilie, und der Wechsel von einem Lisp zum anderen ist normalerweise recht einfach.

Zweitens bietet Clojure dank der Verpflichtung zu Funktionsprinzipien und einer Reihe einzigartiger Architekturlösungen eine beispiellose einfache Manipulation von Datenströmen. Es ist auch wichtig, dass Clojure auf der JVM-Plattform ausgeführt wird. Dies bedeutet, dass Sie eine gemeinsame Datenbank mit anderen Diensten in Java und Scala verwenden sowie zahlreiche Tools zum Erstellen von Profilen und zum Debuggen verwenden können.

Drittens ist Clojure eine prägnante und ausdrucksstarke Sprache. Dies erleichtert das Lesen des Codes eines anderen und das Weitergeben von Code an einen Teamkollegen.

Schließlich schätzen wir Clojure für seine einfache Prototypenerstellung und die sogenannte REPL-zentrierte Entwicklung. In fast allen Situationen, in denen Zweifel bestehen, können Sie einfach einen Prototyp erstellen und die Diskussion mit neuen Daten inhaltlicher fortsetzen. Die REPL-orientierte Entwicklung bietet eine schnelle Rückkehr, da zur Überprüfung der Funktionalität einer Funktion das Programm nicht nur neu kompiliert, sondern sogar neu gestartet werden muss (selbst wenn es sich bei dem Programm um einen Dienst auf einem Remote-Server handelt).

Mit Blick auf die Zukunft kann ich sagen: Ich denke, wir haben die Wahl nicht verloren.

Die Funktionalität Stück für Stück setzen


Wenn wir über einen Ersatz mit vollem Funktionsumfang sprechen, stellt sich als erstes die Sammlung von Informationen über vorhandene Funktionen.

Dies ist eine interessante Aufgabe geworden. Es scheint, dass hier ein funktionierendes System ist, hier ist die Dokumentation dafür, hier sind Leute - Experten, die eng mit dem System zusammenarbeiten und andere darüber unterrichten. Um jedoch ein vollständiges Bild von der gesamten Vielfalt zu erhalten, und noch mehr, erwiesen sich die Anforderungen an die Entwicklung als nicht so einfach.

Das Sammeln von Anforderungen wird nicht umsonst als eigenständige technische Disziplin betrachtet. Die bestehende Implementierung stellt sich paradoxerweise als die Rolle eines „beschädigten Standards“ heraus. Es zeigt, wie und wie es funktionieren sollte, aber gleichzeitig wird von den Entwicklern erwartet, dass die neue Version besser als das Original wird. Es ist notwendig, die für die Implementierung erforderlichen Momente (normalerweise in Bezug auf externe Schnittstellen) von denen zu trennen, die gemäß den Erwartungen der Benutzer verbessert werden können.


Nachrichtenfilterungsprozess

Die Dokumentation reicht nicht aus


Was ist die eigentliche Funktionalität des Systems? Die Antwort auf diese Frage wird durch verschiedene Beschreibungen wie Benutzerdokumentation, Handbücher und Architekturdokumente gegeben, die die Struktur des Dienstes in verschiedenen Aspekten widerspiegeln. Aber wenn es darauf ankommt, verstehen Sie sehr gut, wie sehr die Ideen und die Realität voneinander abweichen, wie viele Nuancen und Möglichkeiten, die der alte Code enthält, nicht berücksichtigt werden.

Ich möchte alle Entwickler kontaktieren. Pass auf deinen Code auf! Dies ist Ihr wichtigstes Kapital. Verlassen Sie sich nicht auf die Dokumentation. Vertrauen Sie nur dem Quellcode.

Glücklicherweise ist der Schema-Code aufgrund der Art der Sprache, die für den Programmierunterricht erstellt wurde, selbst für eine nicht geschulte Person ziemlich einfach zu lesen. Die Hauptsache ist, sich an einige individuelle Formen zu gewöhnen, die einen leichten Hauch von Lisp-Archaik tragen.

Erstellen Sie einen Prozess


Das Arbeitsvolumen war enorm und das Team ist sehr klein. Es war also nicht ohne organisatorische Schwierigkeiten. Der Workflow von Fehlern und Korrekturanforderungen (und geringfügigen Verbesserungen) für den alten Filterdienst wurde nicht einmal gestoppt. Entwickler mussten regelmäßig von diesen Aufgaben abgelenkt werden.

Glücklicherweise war es möglich, Anfragen nach dem Einbetten neuer Teile mit großer Funktionalität in den alten Filter abzuwehren. Richtig, unter dem Versprechen, diese Funktionalität in einen neuen Dienst einzubetten. Die Anzahl der Release-Aufgaben wuchs jedoch langsam aber sicher.

Ein weiterer Faktor, der viel Ärger verursachte, waren die externen Abhängigkeiten des Dienstes. Als zentrale Komponente nutzt der Filterdienst zahlreiche Dienste zum Auspacken und Analysieren von Inhalten (Texte, Bilder, digitale Fingerabdrücke usw.). Die Arbeit mit ihnen wurde teilweise von alten architektonischen Entscheidungen geleitet. Während des Entwicklungsprozesses war es auch notwendig, einige Komponenten auf moderne Weise (und einige in einer modernen Sprache) neu zu schreiben.

Unter solchen Bedingungen wurde ein System zur schrittweisen Funktionsprüfung aufgebaut. Wir haben den Service auf einen bestimmten Zustand gebracht, der durch aktive Tests verstärkt wurde, und sind dann zur Implementierung eines neuen übergegangen.

Starten Sie die Entwicklung


Zunächst wurden das Hauptframework des Dienstes, die grundlegenden Mechanismen zum Empfangen von Nachrichten und zum Entpacken von Dateien implementiert. Dies war das absolute Minimum, das erforderlich war, um mit der Prüfung der Geschwindigkeit und Richtigkeit des zukünftigen Dienstes zu beginnen.

Hier muss klargestellt werden, dass sich das Entpacken auf den rekursiven Prozess bezieht, Teile aus einer Datei abzurufen und nützliche Informationen daraus zu extrahieren. So kann beispielsweise ein Word-Dokument nicht nur Text, sondern auch Bilder, ein eingebettetes Excel-Dokument, OLE-Objekte und vieles mehr enthalten.

Der Entpackungsmechanismus unterscheidet nicht zwischen der Verwendung interner Bibliotheken, externer Programme oder Dienste von Drittanbietern und bietet eine einzige Schnittstelle zum Organisieren von Entpackungs-Pipelines.

Ein weiteres Kompliment an Clojure: Wir haben einen funktionierenden Prototyp erhalten, in dem wir die Konturen der zukünftigen Funktionalität in kürzester Zeit skizziert haben.

DSL für die Politik


Der zweite Schritt bestand darin, die Nachrichtenüberprüfung mithilfe von Filterrichtlinien hinzuzufügen.

Zur Beschreibung der Richtlinien wurde ein spezielles DSL erstellt - eine einfache Sprache ohne Schnickschnack, mit der wir die Regeln und Bedingungen der Richtlinie in einer mehr oder weniger lesbaren Form darstellen konnten. Es heißt MFLang.

Das Skript auf MFLang "on the fly" wird im Clojure-Code interpretiert, speichert die Ergebnisse von Überprüfungen der Nachricht zwischen, führt ein detailliertes Arbeitsprotokoll (und verdient offen gesagt einen separaten Artikel).

Die Verwendung von DSL hat die Tester angesprochen. Runter mit dem Graben in die Datenbank oder das Exportformat! Jetzt war es möglich, die generierte Regel einfach zur Überprüfung zu senden, und sofort wurde klar, welche Bedingungen überprüft wurden. Es war auch möglich, ein detailliertes Nachrichtenüberprüfungsprotokoll zu erhalten, aus dem hervorgeht, welche Daten zur Überprüfung herangezogen wurden und welche Ergebnisse von der Vergleichsfunktion zurückgegeben wurden.

Wir können mit Zuversicht sagen, dass sich MFLang als ein absolut unschätzbares Werkzeug für das Debuggen von Funktionen herausgestellt hat.

In voller Kraft


In der dritten Phase wurde ein Mechanismus hinzugefügt, mit dem die in der Sicherheitsrichtlinie definierten Aktionen auf die Nachricht sowie Servicebindungen angewendet werden können, mit denen neue Komponenten in den Solar Dozor-Komplex aufgenommen werden können. Schließlich konnten wir den Dienst starten und das Ergebnis der Arbeit in seiner ganzen Vielfalt beobachten.

Die Hauptfrage war natürlich, wie die implementierte Funktionalität den Erwartungen entspricht und wie vollständig sie implementiert wird.

Ich stelle fest, dass die Einführung des automatisierten Testens der Systemfunktionalität häufig auf offenen Widerstand stößt, wenn die Notwendigkeit von Unit-Tests lange Zeit nicht in Frage gestellt wurde (obwohl die TDD-Praktiken selbst immer noch lebhafte Debatten auslösen).

Die Entwicklung von Autotests hilft allen Teammitgliedern, den Prozess des Produkts besser zu verstehen, spart Energie bei der Regression und vermittelt ein gewisses Vertrauen in die Leistung des Produkts. Der Prozess ihrer Erstellung ist jedoch mit einer Reihe von Schwierigkeiten verbunden - das Sammeln der erforderlichen Daten, das Bestimmen der Indikatoren von Interesse und das Testen von Optionen. Programmierer empfinden die Erstellung von Autotests unweigerlich als optionale Nebenbeschäftigung, die nach Möglichkeit am besten vermieden wird.

Wenn Sie es jedoch schaffen, den Widerstand zu überwinden, wird eine ziemlich solide Grundlage geschaffen, auf der Sie sich ein Bild von der Gesundheit des Systems machen können.

Wir ersetzen


Und dann kam ein wichtiger Moment: Wir haben den Service in das Lieferpaket aufgenommen. Bisher zusammen mit dem alten. Somit könnte ein Team eine Versionsänderung durchführen und das Verhalten von Diensten vergleichen.

In diesem parallelen Modus dauerte der neue Filterdienst eine Version. In dieser Zeit gelang es uns, zusätzliche Statistiken über die Arbeit zu sammeln, die notwendigen Verbesserungen zu skizzieren und umzusetzen.

Nachdem wir unsere Kräfte gesammelt hatten, entfernten wir schließlich den alten Filterservice aus dem Produkt. Die letzte Phase der internen Akzeptanz ging, Fehler wurden behoben, die Entwickler begannen allmählich, auf andere Aufgaben umzusteigen. Irgendwie unmerklich, ohne Fanfare und Applaus, wurde ein Produkt mit einem neuen Service veröffentlicht.

Und erst als Fragen vom Implementierungsteam kamen, kam das Verständnis - der Service, an dem wir so lange gearbeitet hatten, war bereits vor Ort und ... funktionierte!

Natürlich gab es Fehler und kleinere Verbesserungen, doch nach einem Monat aktiver Nutzung gaben die Kunden ein Urteil ab: Die Einführung eines Produkts mit einer neuen Version des Filterdienstes verursachte weniger Probleme als die Einführung früherer Versionen. Hey! Sieht so aus, als hätten wir es geschafft!

Zusammenfassend


Die Entwicklung eines neuen Filtrationsdienstes dauerte etwa anderthalb Jahre. Länger als ursprünglich angenommen, aber nicht kritisch, zumal die tatsächliche Arbeitsintensität der Arbeit mit der anfänglichen Bewertung zusammenfiel. Noch wichtiger ist, dass wir die Erwartungen von Management und Kunden erfüllen und den Grundstein für zukünftige Produktverbesserungen legen konnten. Bereits im aktuellen Stand ist eine deutliche Reduzierung des Ressourcenverbrauchs zu verzeichnen - trotz der Tatsache, dass das Produkt noch zahlreiche Optimierungsmöglichkeiten bietet.

Ich kann einige persönliche Eindrücke hinzufügen.

Das Ersetzen einer zentralen Komponente durch eine lange Geschichte ist ein Hauch frischer Luft für die Entwicklung. Zum ersten Mal seit langer Zeit besteht das Vertrauen, dass die Produktkontrolle wieder in unsere Hände zurückkehrt.

Es ist schwierig, die Vorteile eines ordnungsgemäß organisierten Kommunikations- und Entwicklungsprozesses zu überschätzen. In diesem Fall war es wichtig, die Arbeit nicht so sehr im Team zu etablieren, sondern bei zahlreichen Verbrauchern des Produkts, die sowohl seit langem klare Präferenzen und Erwartungen an das System als auch eher vage Wünsche hatten.

Für uns war dies die erste Erfahrung bei der Entwicklung eines solchen Großprojekts auf Clojure. Anfänglich gab es Bedenken hinsichtlich der Dynamik der Sprache, der Geschwindigkeit und der Fehlertoleranz. Zum Glück kamen sie nicht zustande.

Es bleibt nur zu wünschen, dass die neue Komponente so lange und erfolgreich arbeitet wie ihre Vorgängerin.

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


All Articles