L'histoire de la refactorisation de l'application Citimobil



Il y a un peu plus d'un an, j'ai rejoint l'équipe CityMobil en tant que développeur Android. Je me suis habitué à un nouveau projet pour moi, de nouvelles approches et technologies. A cette époque, Citimobil avait déjà une assez longue histoire, comme le projet que j'ai adopté, une application Android pour commander un taxi. Cependant, comme cela arrive souvent dans de tels cas, le code portait les traces caractéristiques d'anciennes solutions. Et maintenant, après avoir réussi à refactoriser le code, je veux partager des idées qui, à mon avis, peuvent être utiles à ceux qui doivent refactoriser un projet existant. Et surtout, cela peut être utile pour les petites entreprises avec de petites équipes de développement.

Une entreprise teste souvent ses idées, y consacre des ressources limitées et essaie d'obtenir des commentaires, de tester ses hypothèses le plus rapidement possible. Dans de tels moments, en règle générale, la réflexion et la mise en œuvre de haute qualité de l'architecture du projet, compte tenu de l'avenir, s'effacent en arrière-plan. Progressivement, le projet acquiert de nouvelles fonctionnalités, de nouvelles exigences métier apparaissent, et tout cela affecte la base de code. "CityMobil" à cet égard n'a pas fait exception. Le projet a été développé séquentiellement par plusieurs équipes de l'ancien bureau, puis, lors du déménagement, il a été accompagné et correspondait en partie à l'externalisation. Ensuite, ils ont commencé à former une nouvelle équipe et ils m'ont confié le travail sur le projet.

A cette époque, le «développement» s'installe au bureau de Moscou, le travail bat son plein - sans cesse de nouvelles tâches intéressantes et ambitieuses apparaissent. Cependant, l'héritage a de plus en plus mis des bâtons dans les roues, et une fois que nous avons réalisé que le moment était venu pour de grands changements. Malheureusement, peu de documents utiles ont alors été trouvés. C'est compréhensible, on le sait par expérience, il n'est guère possible de trouver ou de trouver la recette parfaite qui fonctionne dans 100% des cas.

La première chose à faire est de comprendre si vous avez vraiment besoin de refactoring? Cela devrait être pris en compte si:

  1. La vitesse d'introduction de nouvelles fonctionnalités est déraisonnablement faible, malgré le haut niveau de spécialistes de l'équipe.
  2. Les modifications de code dans une partie du programme peuvent entraîner un comportement inattendu dans une autre partie.
  3. L'adaptation des nouveaux membres de l'équipe est retardée.
  4. Les tests de code sont entravés par une forte connectivité.

Après avoir réalisé l'existence d'un problème, on devrait trouver des réponses aux questions suivantes:

  1. Qu'est-ce qui ne va pas?
  2. Qu'est-ce qui a mené à cela?
  3. Que faut-il faire pour éviter que cela ne se reproduise?
  4. Comment régler la situation?

Il est presque impossible de construire un bon projet de longue durée sans poser une certaine architecture. Dans notre projet, nous avons décidé d'introduire une architecture «en couches», qui a déjà fait ses preuves.

Initialement, le projet a été écrit principalement à l'aide des outils fournis par le SDK Android lui-même. L'approche fonctionne sans aucun doute, mais elle vous oblige à écrire beaucoup de code passe-partout, ce qui entrave considérablement le développement. Et étant donné qu'aujourd'hui beaucoup sont habitués à certaines piles technologiques, l'adaptation des nouveaux développeurs a pris plus de temps. Progressivement, nous sommes arrivés à des technologies plus pratiques que beaucoup connaissent et apprécient, et qui ont prouvé leur fiabilité et leur cohérence:

  • MVP - Modèle de conception d'interface utilisateur (modèle-vue-présentateur).
  • Dagger 2 est un cadre pour l'implémentation des dépendances.
  • RxJava2 est une implémentation de ReactiveX - une bibliothèque pour créer des programmes asynchrones et basés sur des événements en utilisant le modèle Observer pour la JVM.
  • Cicerone est une bibliothèque qui vous permet de simplifier la navigation dans l'application.
  • Un certain nombre de bibliothèques spécifiques pour travailler avec des cartes et des emplacements.

Il est très important d'adopter un style de code commun pour l'équipe, afin de développer un ensemble de meilleures pratiques. Vous devez également prendre soin de l'infrastructure et des processus. Il est préférable d'écrire tout de suite des tests pour le nouveau code, car il y a beaucoup d'informations à ce sujet.

Au sein de l'équipe, nous avons commencé à effectuer un examen du code sans faute, cela ne prend pas beaucoup de temps, mais la qualité du code est devenue beaucoup plus élevée. Même si vous êtes seul dans l'équipe, je vous recommande de travailler sur Git Flow, de créer des demandes de fusion et au moins de les vérifier vous-même.

Tout le travail "sale" peut être délégué à CI - dans notre cas, c'est TeamCity utilisant fastlane. Nous l'avons configuré pour créer des branches de fonctionnalités, exécuter les tests et disposer le test interne. Chez nous, nous avons configuré séparément les assemblys pour l'environnement de production / de transfert, la fonctionnalité (nous les appelons par le numéro de tâche avec le modèle TASK # task_number) et les branches de publication. Cela facilite les tests, et si une erreur se produit, nous savons immédiatement ce qui doit être corrigé et où.

Après avoir effectué toutes les actions préliminaires, nous nous mettons au travail. Nous avons commencé une nouvelle vie dans un ancien projet en créant un package (architecture propre). Il est important de ne pas oublier l' alias d'activité lors du déplacement des points d'entrée vers l'application (à la ActivitySplash). Si vous négligez cela, alors, dans le meilleur des cas, vous perdrez l'icône dans le lanceur, et dans le pire des cas, la compatibilité avec d'autres applications sera violée.

<!-- android:name=".SplashActivity" - old launcher activity --> <!-- android:targetActivity=".cleanarchitecture.presentation.SplashActivity" - new launcher activity --> <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> 

Comme l'expérience le suggère, il est préférable de commencer la refactorisation avec de petits écrans mineurs et des parties de l'application. Et le moment venu de traiter la partie la plus complexe et la plus volumineuse du programme, une partie considérable du code sera déjà écrite pour d'autres modules et pourra être réutilisée.

De plus, nous avons eu une grosse tâche pour repenser complètement l'application, ce qui a parfois entraîné une réécriture complète des écrans. Nous avons commencé par améliorer les écrans auxiliaires, en nous préparant à passer à l'essentiel.

Après avoir réécrit la partie suivante de l'application, nous avons recherché des sections de code dans l'ancienne partie de l'application et les avons marquées avec des annotations @Deprecated et des analogues de celles-ci: https://github.com/VitalyNikonorov/UsefulAnnotation . Nous y avons indiqué ce qui devrait être fait lors de la réécriture de cette partie du programme, quelles fonctionnalités et où il est implémenté.

 /** * This class deprecated, you have to use * com.project.company.cleanarchitecture.utils.ResourceUtils * for new refactored classes */ @Deprecated public class ResourceHelper {...} 

Une fois que tout était prêt à fonctionner sur l'écran principal, ils ont décidé de ne pas publier de nouvelles fonctionnalités pendant 6-8 semaines. Nous avons effectué une réécriture globale dans notre propre branche, à laquelle nous avons ensuite ajouté des demandes de fusion. À la fin de la refactorisation, ils ont reçu la demande de pull convoitée et une application presque entièrement mise à jour.

Après refactoring, les modifications apportées aux fonctionnalités de l'application sont devenues beaucoup plus faciles. Ainsi, récemment, nous nous sommes de nouveau engagés dans le traitement des écrans d'autorisation.

Au départ, ils ressemblaient à ceci:



Après le premier traitement et refactoring, ils ont commencé à ressembler à ceci:



Maintenant, ils ressemblent à ceci:



Par conséquent, la première itération a pris plus de deux fois plus de temps que la seconde. Étant donné qu'en plus de traiter l'interface utilisateur, je devais également comprendre le code de logique métier situé au même endroit, bien que cela ne soit pas nécessaire, mais la faille a été éliminée, ce qui a réduit le temps consacré à la tâche lors de la deuxième itération.

Qu'avons-nous en ce moment?

Pour rendre le code pratique pour une utilisation et un développement futurs, nous adhérons au principe de "l'architecture propre". Je ne dirais pas que nous avons canonique Clean, mais nous avons adopté de nombreuses approches. La couche de présentation est écrite en utilisant le modèle MVP (Model-View-Presenter).

  • Auparavant, nous devions sans cesse discuter de chaque étape les uns avec les autres, afin de clarifier si le changement dans un module affecte la fonctionnalité d'un autre. Et maintenant, les frais généraux par correspondance ont considérablement diminué.
  • En raison de l'unification des composants et des fragments individuels, le volume de la base de code a considérablement diminué.
  • En raison de la même unification et du même traitement de l'architecture, il y a beaucoup plus de classes, mais maintenant il y a une division claire des responsabilités en elles, ce qui simplifie la compréhension du projet.
  • La base de code est divisée en couches, pour leur séparation et interaction, le cadre d'injection de dépendances Dagger 2 est utilisé, ce qui réduit la cohérence du code et augmente la vitesse de test.

Il existe de nombreux autres points intéressants liés à la refactorisation de code hérité. Si les lecteurs sont intéressés, j'en écrirai plus la prochaine fois. Je serai également heureux si vous partagez également votre expérience.

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


All Articles