O histórico de refatoração do aplicativo Citimobil



Há pouco mais de um ano, entrei para a equipe CityMobil como desenvolvedor Android. Acostumei-me a um novo projeto, novas abordagens e tecnologias. Naquela época, o Citimobil já tinha uma história bastante longa, como o projeto que adotei, um aplicativo Android para pedir um táxi. No entanto, como geralmente acontece nesses casos, o código carregava os traços característicos de soluções antigas. E agora, depois de refatorar com sucesso o código, quero compartilhar idéias que, na minha opinião, podem ser úteis para quem precisa refatorar um projeto existente. E, acima de tudo, pode ser útil para pequenas empresas com pequenas equipes de desenvolvimento.

Uma empresa geralmente testa suas idéias, direcionando recursos limitados a ela, e tenta obter feedback, testa suas hipóteses o mais rápido possível. Nesses momentos, via de regra, o pensamento de alta qualidade e a implementação da arquitetura do projeto, levando em consideração o futuro, estão desaparecendo. Gradualmente, o projeto adquire novas funcionalidades, novos requisitos de negócios aparecem e tudo isso afeta a base de código. O "CityMobil" a esse respeito não foi exceção. O projeto foi desenvolvido seqüencialmente por várias equipes no escritório antigo e, durante a mudança, foi apoiado e correspondeu parcialmente à terceirização. Então eles começaram a formar uma nova equipe e me entregaram o trabalho no projeto.

Naquela época, o "desenvolvimento" mudou-se para o escritório de Moscou, o trabalho estava em pleno andamento - constantemente surgiram novas tarefas interessantes e ambiciosas. No entanto, o legado colocou cada vez mais paus nas rodas, e uma vez percebemos que havia chegado o momento de grandes mudanças. Infelizmente, não foi encontrada muita literatura útil na época. É compreensível, sabe-se por experiência própria, dificilmente é possível encontrar ou encontrar a receita perfeita que funcione em 100% dos casos.

A primeira coisa a fazer é entender se você realmente precisa de refatoração? Isso deve ser considerado se:

  1. A velocidade de introdução de novos recursos é irracionalmente baixa, apesar do alto nível de especialistas na equipe.
  2. Alterações no código em uma parte do programa podem levar a um comportamento inesperado em outra parte.
  3. A adaptação dos novos membros da equipe está atrasada.
  4. O teste de código é dificultado por uma forte conectividade.

Depois de perceber a existência de um problema, deve-se encontrar respostas para as seguintes perguntas:

  1. O que, de fato, está errado?
  2. O que levou a isso?
  3. O que precisa ser feito para impedir que isso aconteça novamente?
  4. Como consertar a situação?

É quase impossível construir um bom projeto de longa duração sem definir uma certa arquitetura. Em nosso projeto, decidimos introduzir uma arquitetura "em camadas", que já se provou bem.

Inicialmente, o projeto foi escrito principalmente com a ajuda das ferramentas fornecidas pelo próprio SDK do Android. Sem dúvida, a abordagem está funcionando, mas obriga a escrever muito código clichê, o que inibe bastante o desenvolvimento. E considerando que hoje muitos estão acostumados a certas pilhas de tecnologia, a adaptação de novos desenvolvedores levou mais tempo. Gradualmente, chegamos a tecnologias mais convenientes que muitos conhecem e valorizam, e que provaram sua confiabilidade e consistência:

  • MVP - Padrão de Design de Interface do Usuário (Model-View-Presenter).
  • O Dagger 2 é uma estrutura para implementar dependências.
  • RxJava2 é uma implementação do ReactiveX - uma biblioteca para criar programas assíncronos e baseados em eventos usando o padrão Observer para a JVM.
  • Cicerone é uma biblioteca que permite simplificar a navegação no aplicativo.
  • Várias bibliotecas específicas para trabalhar com mapas e locais.

É muito importante adotar um estilo de código comum para a equipe, para desenvolver um conjunto de práticas recomendadas. Você também deve cuidar da infraestrutura e dos processos. É melhor escrever testes para o novo código imediatamente, pois há muitas informações sobre esse assunto.

Dentro da equipe, começamos a realizar a revisão do código sem falhas, não leva muito tempo, mas a qualidade do código se tornou muito maior. Mesmo se você estiver sozinho na equipe, recomendo trabalhar no Git Flow, criando solicitações de mesclagem e, pelo menos, verificando-as.

Todo o trabalho "sujo" pode ser delegado à CI - no nosso caso, este é o TeamCity usando a fastlane. Nós o configuramos para criar ramificações de recursos, executar os testes e apresentar o teste interno. Em casa, configuramos separadamente os assemblies para o ambiente de produção / armazenamento temporário, recurso- (os chamamos pelo número da tarefa com o modelo TASK # task_number) e liberamos ramificações. Isso facilita o teste e, se ocorrer um erro, sabemos imediatamente o que precisa ser corrigido e onde.

Depois de realizar todas as ações preliminares, começamos a trabalhar. Iniciamos uma nova vida em um projeto antigo, criando um pacote (arquitetura limpa). É importante não esquecer o alias de atividade ao mover pontos de entrada para o aplicativo (à la ActivitySplash). Se você negligenciar isso, na melhor das hipóteses, perderá o ícone no iniciador e, na pior das hipóteses, a compatibilidade com outros aplicativos será violada.

<!-- 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> 

Como a experiência sugere, é melhor iniciar a refatoração com pequenas telas pequenas e partes do aplicativo. E quando chegar a hora de processar a parte mais complexa e volumosa do programa, uma parte considerável do código já estará gravada para outros módulos e poderá ser reutilizada.

Além disso, tivemos uma grande tarefa de reprojetar completamente o aplicativo, o que, às vezes, resultava em uma reescrita completa das telas. Começamos melhorando as telas auxiliares, preparando-nos para prosseguir com a coisa principal.

Após reescrever a próxima parte do aplicativo, pesquisamos seções de código na parte antiga do aplicativo e as marcamos com anotações e análogos obsoletos desses: https://github.com/VitalyNikonorov/UsefulAnnotation . Nelas, indicamos o que deve ser feito ao reescrever esta parte do programa, que funcionalidade e onde é implementada.

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

Depois que tudo estava pronto para funcionar na tela principal, eles decidiram não lançar novos recursos por 6-8 semanas. Realizamos a reescrita global em nossa própria filial, à qual adicionamos solicitações de mesclagem. No final da refatoração, eles receberam a solicitação de recebimento cobiçada e um aplicativo quase completamente atualizado.

Após a refatoração, as alterações na funcionalidade do aplicativo se tornaram muito mais fáceis. Então, recentemente, estávamos novamente envolvidos no processamento de telas de autorização.

Inicialmente, eles pareciam os seguintes:



Após o primeiro processamento e refatoração, eles começaram a ficar assim:



Agora eles se parecem com isso:



Como resultado, a primeira iteração levou mais que o dobro do tempo que a segunda. Como além de processar a interface do usuário, tive que entender o código da lógica de negócios localizado no mesmo local, embora isso não fosse necessário, mas a falha foi eliminada, o que reduziu o tempo gasto na tarefa na segunda iteração.

O que temos no momento?

Para tornar o código conveniente para uso e desenvolvimento futuro, aderimos ao princípio de "arquitetura limpa". Eu não diria que temos o Clean canônico, mas adotamos muitas abordagens. A camada de apresentação é gravada usando o padrão MVP (Model-View-Presenter).

  • Anteriormente, tínhamos que discutir incessantemente cada etapa, para esclarecer se a alteração em um módulo afeta a funcionalidade de outro. E agora as despesas gerais por correspondência caíram significativamente.
  • Devido à unificação de componentes e fragmentos individuais, o volume da base de código diminuiu bastante.
  • Como resultado da mesma unificação e processamento da arquitetura, há muito mais classes, mas agora há uma divisão clara de responsabilidades nelas, o que simplifica o entendimento do projeto.
  • A base de código é dividida em camadas, para sua separação e interação, é usada a estrutura de injeção de dependência Dagger 2. Isso reduziu a coerência do código e aumentou a velocidade de teste.

Existem muitos pontos mais interessantes relacionados à refatoração de código legado. Se os leitores se interessarem, escreverei mais sobre eles na próxima vez. Também ficarei feliz se você também compartilhar sua experiência.

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


All Articles