
Vor etwas mehr als einem Jahr trat ich als Android-Entwickler dem CityMobil-Team bei. Ich habe mich an ein neues Projekt gewöhnt, an neue Ansätze und Technologien. Zu dieser Zeit hatte Citimobil bereits eine ziemlich lange Geschichte, wie das Projekt, das ich angenommen habe, eine Android-Anwendung für die Bestellung eines Taxis. Wie in solchen Fällen jedoch häufig, enthielt der Code die charakteristischen Spuren alter Lösungen. Und jetzt, nachdem ich den Code erfolgreich überarbeitet habe, möchte ich Ideen teilen, die meiner Meinung nach für diejenigen nützlich sein können, die ein vorhandenes Projekt umgestalten müssen. Und vor allem kann es für kleine Unternehmen mit kleinen Entwicklungsteams nützlich sein.
Ein Unternehmen testet häufig seine Ideen, leitet begrenzte Ressourcen an es weiter und versucht, Feedback zu erhalten. Testen Sie seine Hypothesen so schnell wie möglich. In solchen Momenten tritt in der Regel ein qualitativ hochwertiges Überdenken und Implementieren der Projektarchitektur unter Berücksichtigung der Zukunft in den Hintergrund. Allmählich erhält das Projekt neue Funktionen, neue Geschäftsanforderungen werden angezeigt, und all dies wirkt sich auf die Codebasis aus. "CityMobil" war in dieser Hinsicht keine Ausnahme. Das Projekt wurde nacheinander von mehreren Teams im alten Büro entwickelt, dann während des Umzugs unterstützt und teilweise mit Outsourcing korrespondiert. Dann bildeten sie ein neues Team und gaben mir die Arbeit an dem Projekt.
Zu dieser Zeit zog „Entwicklung“ in das Moskauer Büro, die Arbeit war in vollem Gange - es tauchten ständig neue interessante und ehrgeizige Aufgaben auf. Das Erbe steckte jedoch immer mehr Stöcke in die Räder, und als wir merkten, dass die Zeit für große Veränderungen gekommen war. Leider wurde damals nicht so viel nützliche Literatur gefunden. Es ist verständlich, es ist aus Erfahrung bekannt, es ist kaum möglich, das perfekte Rezept zu finden, das in 100% der Fälle funktioniert.
Das erste, was Sie tun müssen, ist zu verstehen, ob Sie wirklich ein Refactoring benötigen. Dies sollte berücksichtigt werden, wenn:
- Die Geschwindigkeit der Einführung neuer Funktionen ist trotz des hohen Niveaus an Spezialisten im Team unangemessen gering.
- Änderungen am Code in einem Teil des Programms können zu unerwartetem Verhalten in einem anderen Teil führen.
- Die Anpassung neuer Teammitglieder verzögert sich.
- Code-Tests werden durch starke Konnektivität behindert.
Nachdem man das Vorhandensein eines Problems erkannt hat, sollte man Antworten auf die folgenden Fragen finden:
- Was ist eigentlich falsch?
- Was hat dazu geführt?
- Was muss getan werden, um dies zu verhindern?
- Wie kann man die Situation beheben?
Es ist fast unmöglich, ein gutes Langzeitprojekt zu erstellen, ohne eine bestimmte Architektur zu legen. In unserem Projekt haben wir uns entschlossen, eine „geschichtete“ Architektur einzuführen, die sich bereits bewährt hat.
Ursprünglich wurde das Projekt hauptsächlich mit Hilfe der vom Android SDK selbst bereitgestellten Tools geschrieben. Der Ansatz funktioniert zweifellos, zwingt Sie jedoch dazu, viel Code für das Boilerplate zu schreiben, was die Entwicklung stark behindert. Und wenn man bedenkt, dass heutzutage viele an bestimmte Technologiepakete gewöhnt sind, dauerte die Anpassung neuer Entwickler länger. Allmählich kamen wir zu bequemeren Technologien, die viele kennen und schätzen und die ihre Zuverlässigkeit und Konsistenz bewiesen haben:
- MVP - User Interface Design Pattern (Modellansicht-Präsentator).
- Dolch 2 ist ein Framework zum Implementieren von Abhängigkeiten.
- RxJava2 ist eine Implementierung von ReactiveX - einer Bibliothek zum Erstellen asynchroner und ereignisbasierter Programme unter Verwendung des Observer-Musters für die JVM.
- Cicerone ist eine Bibliothek, mit der Sie die Navigation in der Anwendung vereinfachen können.
- Eine Reihe spezifischer Bibliotheken für die Arbeit mit Karten und Orten.
Es ist sehr wichtig, einen gemeinsamen Codestil für das Team zu verwenden, um eine Reihe von Best Practices zu entwickeln. Sie sollten sich auch um die Infrastruktur und Prozesse kümmern. Es ist besser, sofort Tests für den neuen Code zu schreiben, da es viele Informationen zu diesem Thema gibt.
Innerhalb des Teams haben wir begonnen, eine Codeüberprüfung durchzuführen. Es dauert nicht so lange, aber die Qualität des Codes ist viel höher geworden. Selbst wenn Sie alleine im Team sind, empfehle ich, an Git Flow zu arbeiten, Zusammenführungsanforderungen zu erstellen und diese zumindest selbst zu überprüfen.
Alle "schmutzigen" Arbeiten können an CI delegiert werden - in unserem Fall ist dies TeamCity mit Fastlane. Wir haben es so konfiguriert, dass Feature-Zweige erstellt, die Tests ausgeführt und der interne Test erstellt werden. Bei uns haben wir Assemblys für die Produktions- / Staging-Umgebung separat konfiguriert, Feature- (wir nennen sie bei der Aufgabennummer mit der Vorlage TASK # task_number) und Release-Zweige. Dies erleichtert das Testen und wenn ein Fehler auftritt, wissen wir sofort, was wo behoben werden muss.
Nachdem wir alle vorbereitenden Maßnahmen durchgeführt haben, machen wir uns an die Arbeit. Wir haben ein neues Leben in einem alten Projekt begonnen, indem wir ein Paket (cleanarchitecture) erstellt haben. Es ist wichtig, den
Aktivitätsalias nicht zu vergessen, wenn Sie Einstiegspunkte in die Anwendung verschieben (a-la ActivitySplash). Wenn Sie dies vernachlässigen, verlieren Sie im besten Fall das Symbol im Launcher, und im schlimmsten Fall wird die Kompatibilität mit anderen Anwendungen verletzt.
<activity-alias android:name=".SplashActivity" android:targetActivity=".cleanarchitecture.presentation.SplashActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity-alias>
Wie die Erfahrung zeigt, ist es besser, mit kleineren kleinen Bildschirmen und Teilen der Anwendung mit dem Refactoring zu beginnen. Und wenn es Zeit ist, den komplexesten und umfangreichsten Teil des Programms zu verarbeiten, wird ein beträchtlicher Teil des Codes bereits für andere Module geschrieben und kann wiederverwendet werden.
Außerdem hatten wir dann eine große Aufgabe, die Anwendung komplett neu zu gestalten, was zeitweise zu einem vollständigen Umschreiben der Bildschirme führte. Wir haben zunächst die Zusatzbildschirme verbessert und uns darauf vorbereitet, mit der Hauptsache fortzufahren.
Nachdem wir den nächsten Teil der Anwendung neu geschrieben hatten, suchten wir im alten Teil der Anwendung nach Codeabschnitten und markierten sie mit @ Veralteten Anmerkungen und Analoga davon:
https://github.com/VitalyNikonorov/UsefulAnnotation . In ihnen haben wir angegeben, was beim Umschreiben dieses Teils des Programms zu tun ist, welche Funktionen und wo es implementiert ist.
@Deprecated public class ResourceHelper {...}
Nachdem alles bereit war, auf dem Hauptbildschirm zu arbeiten, beschlossen sie, 6-8 Wochen lang keine neuen Funktionen zu veröffentlichen. Wir haben in unserer eigenen Niederlassung ein globales Umschreiben durchgeführt, zu dem wir dann Zusammenführungsanforderungen hinzugefügt haben. Am Ende des Refactorings erhielten sie die begehrte Pull-Anfrage und eine fast vollständig aktualisierte Anwendung.

Nach dem Refactoring wurden Änderungen an der Funktionalität der Anwendung viel einfacher. So haben wir uns kürzlich wieder mit der Bearbeitung von Autorisierungsbildschirmen beschäftigt.
Anfangs sahen sie wie folgt aus:

Nach der ersten Verarbeitung und Umgestaltung sahen sie folgendermaßen aus:

Jetzt sehen sie so aus:

Infolgedessen dauerte die erste Iteration mehr als doppelt so lange wie die zweite. Da ich zusätzlich zur Verarbeitung der Benutzeroberfläche den an derselben Stelle befindlichen Geschäftslogikcode verstehen musste, war dies zwar nicht erforderlich, der Fehler wurde jedoch behoben, wodurch sich die für die Aufgabe in der zweiten Iteration aufgewendete Zeit verringerte.
Was haben wir im Moment?
Um den Code für die zukünftige Verwendung und Entwicklung bequem zu machen, halten wir uns an das Prinzip der "sauberen Architektur". Ich würde nicht sagen, dass wir Canonical Clean haben, aber wir haben viele Ansätze gewählt. Die Präsentationsebene wird mit dem MVP-Muster (Model-View-Presenter) geschrieben.
- Zuvor mussten wir jeden Schritt endlos miteinander diskutieren, um zu klären, ob sich die Änderung in einem Modul auf die Funktionalität eines anderen auswirkt. Und jetzt ist der Aufwand für Korrespondenz erheblich gesunken.
- Aufgrund der Vereinheitlichung einzelner Komponenten und Fragmente hat sich das Volumen der Codebasis stark verringert.
- Aufgrund der gleichen Vereinheitlichung und Verarbeitung der Architektur gibt es viel mehr Klassen, aber jetzt gibt es eine klare Aufteilung der Verantwortlichkeiten in ihnen, was das Verständnis des Projekts vereinfacht.
- Die Codebasis ist in Schichten unterteilt, für deren Trennung und Interaktion wird das Abhängigkeitsinjektions-Framework Dolch 2 verwendet. Dies verringert die Kohärenz des Codes und erhöht die Testgeschwindigkeit.
Es gibt viele weitere interessante Punkte im Zusammenhang mit dem Refactoring von Legacy-Code. Wenn die Leser interessiert sind, werde ich beim nächsten Mal mehr darüber schreiben. Ich würde mich auch freuen, wenn Sie auch Ihre Erfahrungen teilen.