Raumschiffe am Verbrennungsmotor. Überlebe den Kampf mit technischen Schulden



Wie kann man den Kampf mit technischen Schulden überleben? Was tun, wenn Sie ein schweres Vermächtnis haben? In dem Artikel möchte ich am Beispiel von drei Fällen herausfinden, wie der Prozess der Arbeit mit technischen Schulden aufgebaut werden kann und welche technischen Ansätze hierfür verwendet werden sollen.

Mein Name ist Denis, ich bin der Backend-Teamleiter bei Wrike. Ich bin verantwortlich für die Bereitstellung in meinem Team und für das Wachstum der Entwickler in mehreren Teams. So kam es, dass fast meine gesamte Erfahrung bei Fintech liegt. Ich habe für zwei große Banken gearbeitet und jetzt arbeite ich bei Wrike.

In Banken lernen Sie, mit Systemen zu arbeiten, bei denen Zuverlässigkeit und Fehlertoleranz wichtig sind. Und da gibt es viel Vermächtnis, und ich habe das alles selbst als Entwickler erlebt und anderen geholfen, mich als Führungsteams zu erleben.

Wrike ist eine SaaS-Team-Collaboration-Lösung, die wir an unsere Kunden verkaufen. Wir machen Wrike mit Wrike, um die Entwicklung zu organisieren.

Wrike entwickelt dreißig Scrum-Teams. Der Service ist rund um die Uhr verfügbar, daher müssen die Entscheidungen, die wir treffen, zuverlässig sein. Wenn etwas schief geht, wirkt sich dies auf die Arbeit fast aller Teams aus.

Warum Raumschiffe?


Starship ist eine Metapher, mit der ich beschreibe, mit was wir Programmierer arbeiten. Die Anwendung beginnt mit mehreren Modulen. Dann beginnt es zu wachsen: Es entsteht eine große Last, Microservices erscheinen, Kommunikation zwischen ihnen, Integrationen, externe APIs, Clients. Es stellt sich ein großes und verbundenes Ökosystem heraus. Je größer und älter die Anwendung ist, desto enger wird das Ökosystem verbunden. Und umso wichtiger ist es, die wichtigen Knoten auf dem neuesten Stand und in einem zuverlässigen Zustand zu halten. Es wird sehr traurig, wenn die wichtigsten von ihnen entweder die heutigen Anforderungen nicht erfüllen oder scheitern.
Ihr Raumschiff springt nicht in den Warp, wenn es auf AI-95 funktioniert. Das System navigiert nicht durch vier Galaxien, wenn der zentrale Computer Intel Celeron ist.

Was ist technische Verschuldung?


Stellen Sie sich vor, Sie haben eine Funktion, in der ständig Fehler auftreten. Ein Tester kommt zu Ihnen und sagt: "Diese Woche haben wir dort vier neue Fehler gefunden." Ist es eine technische Schuld oder nicht? Und wenn das Vorhandensein dieser Funktion andere Geschichten blockiert, die getan werden müssen? Und wenn es nur schwer ist, mit der Lösung zu arbeiten, und Sie sie jedes Mal umgestalten? Wenn Benutzer sich über eine Funktion beschweren? Und wenn sie die Anforderungen, die ihr heute gestellt werden, nicht erfüllt oder die Gefühle von Entwicklern verletzt, die nach Spitzenleistungen streben?

In dem Artikel werde ich unter technischer Verschuldung diejenigen Merkmale, Lösungen oder Ansätze verstehen, die die weitere Produktentwicklung beeinträchtigen. Alle oben beschriebenen Probleme sind das Ergebnis technischer Schulden. Dies sind spezifische Gründe: Warum er ist und warum Sie mit ihm arbeiten müssen.

Technischer Schuldenalgorithmus


Um effektiv mit technischen Schulden zu arbeiten, müssen Sie drei grundlegende Fragen stellen.

  1. Wozu? Warum verpflichten wir uns, etwas mit ihm zu tun? Warum brauchen wir diese Arbeit? Warum Unternehmensgeld, Entwicklungsstunden, Zeit für Ingenieure ausgeben? Es sollte ein klares Verständnis dafür vorhanden sein, welchen Nutzen wir durch die Lösung eines bestimmten Problems erzielen können. Aus der Definition geht hervor, dass technische Schulden die Entwicklung des Produkts blockieren. Wenn wir damit arbeiten, erhalten wir neue Funktionen und Möglichkeiten.
  2. Was? Wir müssen klar verstehen, wo die technische Verschuldung beginnt, wo sie endet, wie sie aussieht und wie viel Arbeit getan werden muss, um sie zu beseitigen.
  3. Wie? Maßnahmen, die mit technischen Schulden durchgeführt werden müssen, um sie loszuwerden.

Um die letzte Frage zu beantworten, gibt es einen einfachen iterativen vierstufigen Algorithmus. Der erste Schritt besteht darin, die technische Verschuldung hervorzuheben und ihre Grenzen zu verstehen. Der nächste Schritt besteht darin, es von allem anderen zu trennen: Kapselung, Eingabe eines Arbeitsvertrags zwischen der von Ihnen zugewiesenen Lösung und dem Rest des Systems. Danach können Sie eine neue Lösung in der Nähe erstellen, ersetzen und somit ein Teil der technischen Schulden die Anwendung verlassen. Wenn Sie diese Iteration mehrmals wiederholen, erhalten Sie eine vorgefertigte Lösung.



Lassen Sie uns nun anhand mehrerer Fälle als Beispiel sehen, wie der Algorithmus in der Realität funktioniert.

Erster Fall


Es gibt eine Anwendung, die mit Bestellungen von Kunden arbeitet - das Auftragsverwaltungssystem. Die Anwendung wurde vor langer Zeit im Jahr 2010 geschrieben und basiert auf den neuesten Technologien dieser Zeit. Die Anwendung wurde in den letzten 9 Jahren erfolgreich in der Produktion eingesetzt, aber heute versteht das Unternehmen, dass es notwendig ist, neue Märkte zu erschließen und das System weiterzuentwickeln. Gleichzeitig ist es wichtig, Daten zu speichern und neue Funktionen im System zu erweitern.



Es stellt sich heraus, dass es Technologien gibt, die schon lange tot sind, aber es gibt auch Daten, die nicht verloren gehen können. Nicht alle Funktionen können mit alten Technologien in einer Anwendung implementiert werden. Daher sieht die Situation, um ganz ehrlich zu sein, ungefähr so ​​aus:



Das Problem liegt hier nicht in den alten Frameworks, sondern in der Situation, die wir haben: Die Anwendung wird nicht unterstützt, es ist fast unmöglich, Entwickler für Frameworks vor einem Jahrzehnt zu finden. Wir müssen etwas dagegen tun.

Lassen Sie uns den Algorithmus ausführen. Mehrere Teile der technischen Verschuldung können unterschieden werden und nähern sich diesem Prozess iterativ an. Lassen Sie uns zunächst Frontend behandeln. Wir können das neue Frontend mit dem alten Backend starten. Wir werden in der Lage sein, das neue Frontend zu erweitern, es an moderne Technologien anzupassen und unsere Ziele zu erreichen. Wir können uns entweder vollständig auf das alte Backend verlassen oder es muss leicht modifiziert werden, um mit dem neuen Frontend zu arbeiten. Der nächste Schritt ist die Einkapselung. Bei der Kapselung hilft uns die Architektur hier. Der Kapselungspunkt ist in diesem Fall ein Vertrag mit dem Backend. Nachdem wir das neue Frontend gestartet haben, können wir den alten Teil des Frontends entfernen. Jetzt wird unsere gesamte Anwendung immer grüner.



Der nächste Schritt ist die Arbeit mit dem Backend. Hier ist der Kapselungspunkt bereits die Datenbankschicht. Es stellt sich heraus, dass die Architektur diese Kapselung wieder für uns übernehmen wird. Und wir können eine neue Lösung in der Nähe erstellen, mit denselben Daten arbeiten und das Frontend darauf übertragen. Jetzt haben wir die alte Lösung komplett aufgegeben und können sie wegwerfen. Dadurch können wir das Ziel erreichen, das wir uns für dieses Projekt gesetzt haben.



Zweiter Fall


Nehmen Sie den Fall schwieriger. Es gibt eine Anwendung mit einer speziellen Funktion, die für das Speichern von Währungspaaren in der Datenbank verantwortlich ist. Zum Beispiel Rubel-Dollar, Dollar-Yen und so weiter. Informationen werden in einer Datenbank in einer Tabelle gespeichert. Und um es ein bisschen lustiger zu machen, fügen Sie ein paar Abhängigkeiten hinzu: Es gibt einen Verbraucher, der Daten direkt aus der Datenbank empfängt, und einen Datenanbieter, der sie wiederum direkt in die Datenbank liefern kann.



Wir sind mit dem Datenformat und der Art und Weise, wie diese Daten in die Datenbank gelangen, nicht zufrieden. Aber Sie müssen es sorgfältig beheben, es gibt viele Abhängigkeiten.

Wählen Sie dazu ein bestimmtes Stück - Daten. Sie müssen sie kapseln. Geben Sie dazu die Zwischenschicht ein. Ihre Aufgabe ist es sicherzustellen, dass der Verbraucher und der Lieferant keine Änderungen bemerken. Dies ist die Bedeutung der Einkapselung. Jetzt können wir die Speicherstruktur ändern, da dies keine Auswirkungen auf externe Abhängigkeiten hat. Danach können wir eine neue Lösung erstellen, die Daten in einem neuen Format aufzeichnet. Der letzte Schritt besteht darin, die alten Daten in ein neues Format zu übertragen und das zu erhalten, was wir von unserem Projekt wollten: Daten in einem neuen Format, und die alte Logik kann aus der Anwendung entfernt werden.


In diesem Prozess werden Datenkonsumenten die Änderungen nicht bemerken, was bedeutet, dass wir sie vollständig sicher durchgeführt haben und gleichzeitig die Abwärtskompatibilität beibehalten haben. Bei Bedarf können Sie dann mit dem Verbraucher und dem Datenanbieter zusammenarbeiten, damit diese auch das neue Format verwenden.

Dritter Fall


Stellen Sie sich vor, es gibt ein großes Projekt, eine große Codebasis und eine Art Schlüsselfunktionalität, die sich auf absolut alle Punkte der Anwendung auswirkt, um die Skalierbarkeit zu erhöhen und die Funktionsweise in großen Projekten zu verstehen. Andere Teile des Backends verwenden es, es hat Zugriff auf die öffentliche API, dh die Daten gehen irgendwo verloren. Die Funktion wird im Frontend, in externen Systemen und sogar im Anhang verwendet und geht direkt von der Datenbank zur Analyse. Um das Beispiel noch lustiger zu machen, fügen Sie hier eine Prise Legacy hinzu. Na ja, ein bisschen.



Zwei lustige Fakten:

  1. Es funktioniert definitiv.
  2. Niemand weiß, wie es funktioniert. Deshalb Legacy.

Wenn Sie mit einem solchen Fall arbeiten, in dem es viele Berührungspunkte und viele Unbekannte gibt, sollten Sie verstehen: Mit was arbeiten wir tatsächlich, wie sieht diese Lösung aus und welche Fähigkeiten hat sie? Es ist wichtig zu verstehen, wie die Lösung, die wir überarbeiten oder entfernen möchten, mit dem Rest der Anwendung interagiert. Wir müssen alle Gemeinsamkeiten finden: verstehen, wie sie funktionieren, ihre Verträge verstehen, um etwas anderes anbieten zu können.

Hier gibt es verschiedene Ansätze, die helfen können, insbesondere wenn das Ausmaß der Katastrophe in Bezug auf die Codemenge groß genug ist:

  • . Java , , , . , , , , ;
  • . , , , . , , . , ;
  • -.

Wenn wir im Code Gemeinsamkeiten finden, können Sie diese verwenden und die Lösung mit Kapselungspunkten umschließen. Wir führen neue Verträge für die Interaktion der Anwendung mit unserer Funktion ein.



Hier wurde die Menge des Vermächtnisses reduziert, da wir bereits in diesem Moment beginnen, das, was wir wollen, in die Anwendung einzugeben.

Als nächstes müssen Sie den ersten Schritt in Richtung einer neuen Lösung machen - Tests. Sie werden die sehr lustige Tatsache über Legacy Nr. 2 schließen, die ich zuvor erwähnt habe. Tests zeigen, wie Ihre Lösung funktioniert. Zusätzlich zur Überprüfung des Schlüsselflusses müssen Sie sicherstellen, dass er genau dort liegt, wo Sie ihn von ihm erwarten und genau so, wie Sie ihn von ihm erwarten.

Es kommt häufig vor, dass eine einmal für Geschäftszwecke erstellte Lösung zu 100% verwendet wurde und sich heute nur zu 30% und zu 70% mit den aktuellen Zielen überschneidet - nicht. In der heutigen Realität sind diese 70% nicht mehr wichtig. Die Tests, die Sie geschrieben haben, werden die 30% hervorheben. Nachdem Sie den beschichteten Test ausgeführt haben, können Sie verstehen, welcher Code überhaupt nicht verwendet wird, ihn löschen und die Konnektivität und Komplexität Ihrer Lösung verringern.

Wenn wir, nachdem wir die Tests geschrieben und verstanden haben, wie sie funktionieren, beginnen, eine neue Lösung im gekapselten Bereich einzuführen, werden wir nach und nach alles ersetzen, was wir nicht benötigen, das Erbe entfernen und die Lösung durch eine neue ersetzen, die unseren Anforderungen entspricht.



Die neue Lösung sollte einfach und verständlich sein und Ihr Problem speziell heute und für bestimmte unmittelbare Ziele lösen. Es besteht keine Notwendigkeit, tiefer in die Überentwicklung einzusteigen, da wir das heutige Endziel schließen.

Und dies ist der Ort, an dem Sie anhalten, im Moment hängen und denken sollten: „Warum machen wir das?“ In dieser Phase erhalten Sie viele Informationen darüber, wie die Lösung funktioniert, was darin enthalten ist und was nicht. Sie haben Tests geschrieben, den Code markiert, ein Diagramm erstellt und jetzt verstehen Sie eine Größenordnung mehr. Vielleicht ist dies der Punkt, an dem es sich zu stoppen und zu verstehen lohnt: Ist es möglich, ein viel größeres Problem zu lösen? Und das hat einst die Reihenfolge des Entwicklungsquartals für unser Projekt gerettet, weil wir die richtige Richtung für die Entwicklung des Projekts gewählt haben.

Wie man die Arbeit organisiert


Wir verfolgen einen Ansatz, bei dem wir versuchen, die neue Lösung in Iterationen zu zerlegen. Dies sind seine spezifischen Teile, die in der Produktion ausgerollt werden können und die etwas daran ändern werden.

Um zu verstehen, was Iteration ist und welche Aufgaben darin enthalten sind, haben wir das Konzept der Definition von Done von Scrum übernommen. Dies ist eine Reihe von Kriterien, die erfüllt sein müssen, damit eine Geschichte als erfüllt betrachtet wird.

Hier verwende ich dieses Konzept in einer etwas anderen Form. Unter Definition von Fertig verstehe ich die Beschreibung dessen, was sich in der Anwendung ändern wird, wenn eine bestimmte Iteration in Produktion geht.

Erinnern wir uns an das erste Beispiel mit dem Auftragsverwaltungssystem. Darin kann der Benutzer mithilfe der neuen Benutzeroberfläche und des alten Backends eine neue Bestellung erstellen. Dies ist die Art der Iteration - ein spezifischer Teil der Funktionalität, die wir in die Produktion einführen können, und sie wird funktionieren. Oder der Benutzer kann sich mit dem neuen Zugriffsrechtsmodell anmelden - dies ist auch eine qualitative Änderung. So können Sie beschreiben, was genau jede Iteration in Ihrem Kreuzzug im Kampf gegen technische Schulden bringt.

Wenn Sie die Lösung in Iterationen aufteilen, kann es viele Probleme geben. Es wird eine Abhängigkeit zwischen ihnen geben, wir erhalten eine ganze Grafik. In dieser Spalte gibt es verschiedene Möglichkeiten, um das Endergebnis zu erzielen. Sie können die umgekehrte Planung verwenden - ein Tool, mit dem Sie die Arbeitszeit verkürzen können. Wir beginnen am letzten Punkt des Projekts und stellen die Frage: „Was muss getan werden, um dieses Ziel zu erreichen?“. Wir verstehen, dass wir den vorherigen Schritt tun müssen, um dieses Ziel zu erreichen. Wir bewegen uns also vom Ende zum Anfang und beantworten bei jedem Zwischenschritt diese Frage, indem wir den kritischen Pfad passieren. Einmal hat uns ein solcher Ansatz das Entwicklungsquartal erspart.

Ein Tool namens Gantt-Diagramm eignet sich gut zur Visualisierung von Arbeiten. Dies ist ein Diagramm, das die Beziehungen zwischen Aufgaben, ihre Dauer und das visuelle Erscheinungsbild des Projekts zeigt. Auf dem Bild ist ein Screenshot von Wrike zu sehen. Wir haben dieses Tool, wir nutzen es aktiv, um mit Projekten zu arbeiten.



Wenn Sie an einer umfassenden Lösung arbeiten, kann es vorkommen, dass jemand den Code ändert, den Sie überarbeiten, anpassen, in Ihrem Prozess leiten und den Sie gerade durchdacht haben. Diese Änderungen können es für Sie schwierig machen, mit technischen Schulden umzugehen.

Sie können sich auf verschiedene Weise vor solchen Änderungen schützen:

  • — . , , - , .
  • , . git hook, git commit git push. unit test, , - , , , : .

Sie können auch ein externes Überwachungssystem für Ihren Code konfigurieren. Bei Wrike verwenden wir PMD. Das System startet und überprüft jede neue Codezeile auf Einhaltung bestimmter Regeln. Das Bild ist ein Beispiel aus dem Build-Protokoll. Hier wurde die Regel „Ergebnisse öffentlicher Methoden müssen unveränderlich sein“ verletzt, PMD spricht darüber und zeigt in welcher Zeile - hier ist es die Methode „falsche Methode“. Unten finden Sie einen Hinweis darauf, was Sie damit tun müssen, um das Problem zu beheben. So wissen wir immer, wo gegen die Regel verstoßen wird und wie sie behoben werden kann.



Wie man durch den Endgegner kommt


Eine wichtige Sache, über die wir nicht gesprochen haben, ist der Endgegner, der bei der Arbeit mit technischen Schulden durchmachen muss. Unternehmen, Produktbesitzer, Kunde - jeder hat einen anderen Namen dafür. Dies kann die Weiterentwicklung unserer technischen Initiativen in die Produktion beeinträchtigen.

Wir alle kennen Fälle, in denen der Product Owner aus technischer Sicht nicht die optimalsten Lösungen gefunden hat. Aber selbst wenn Ihr Produktbesitzer auf das Titanblech blickt, können Sie trotzdem mit ihm verhandeln. Wenn Sie mit technischen Schulden arbeiten, eröffnen sich Ihnen neue Möglichkeiten, und der Product Owner benötigt sie, um sein Produkt zu entwickeln.

Sie können ein Zeitkontingent vereinbaren. Zum Beispiel widmen sich 10% der Zeit Entwickler der technischen Verschuldung. Eine solche Quote wird es nicht erlauben, technische Schulden loszuwerden, aber es wird nicht zulassen, dass sie anschwillt.

Offensichtlich ist es schwierig, über technische Schulden zu sprechen, wenn nicht klar ist, worum es genau im Gespräch geht. Daher sollte Ihr Team über einen technischen Rückstand verfügen, in dem es Aufgaben gibt, die von Entwicklern bewertet und priorisiert werden.

Mit den oben genannten Tools können jedoch keine Großprojekte bearbeitet werden. In diesem Fall ist es wichtig, Ihren Kunden die Vorteile des Projekts im Hinblick auf die zuvor aufgeführten Punkte (3 Fragen zur technischen Verschuldung, zum Projekt im Tracker, zum kritischen Pfad in der Grafik usw.) erklären zu können. Und je ausführlicher die Geschichte, desto mehr Verständnis werden Sie ihr vermitteln. Ein Unternehmen hat Ziele, die Sie erreichen. Es ist wichtig, dass Sie auf dem Laufenden bleiben, was nicht nur jetzt passiert, sondern auch über Pläne, die in bestimmten Quartalen Wirklichkeit werden. Kommunizieren Sie daher mit dem Unternehmen, verstehen Sie, was es tun möchte und wie diese Ziele erreicht werden können, und verwenden Sie dieses Wissen, um Ihre technischen Ziele und Geschäftsziele zu kombinieren.

Vielleicht fallen die Ziele nicht sofort zusammen, sondern in einem Viertel oder sogar in einem Jahr. Auf diese Weise können Sie jedoch nachvollziehen, wann das Unternehmen für Änderungen bereit ist.

Wenn Sie es schaffen, Ziele zu synchronisieren, erhalten Sie alle Boni: Priorisierung, Ressourcen und Organisation aller Arbeiten. Der Product Owner hilft Ihnen dabei. Die Hauptaufgabe ist es, ihm zuzustimmen.

Gesamt


Wenn wir über Änderungen in Schlüsselbereichen des Produkts sprechen, wird ein einfacher vierstufiger Algorithmus kompliziert:



Zwei weitere Schritte werden hinzugefügt. Das erste ist ein Verständnis dafür, womit wir arbeiten. Die zweite - nach der Kapselung - ein Verständnis der Funktionsweise der Lösung und der Verhinderung von Änderungen in Ihrem Code.

Mit diesem Algorithmus und meinen Empfehlungen zur Organisation des Prozesses können Sie mit jeder technischen Verschuldung arbeiten.

Der Artikel basiert auf meiner Rede auf dem Treffen, Sie können den Videobericht sehen .

All Articles