Como atualizamos o núcleo do nosso produto principal sem tempo de inatividade (primeiras etapas)

Ricardo Vila, engenheiro de software da TEIMAS, dá uma visão em primeira mão de seu trabalho no desenvolvimento do Teixo, um produto Saas que foi originalmente escrito no Rails 2.3.

Este é um texto dentro de um bloco div.

Ricardo Vila, engenheiro de software da TEIMAS, fala em primeira mão sobre seu trabalho no desenvolvimento do Teixo, um produto Saas que foi originalmente escrito em Rails 2.3.

Em 2021, iniciamos uma jornada para atualizar o Teixo para a versão mais recente do Rails, e essas postagens são meu diário:

Este artigo está disponível somente em inglês.

1. Introdução

Somos a TEIMAS, uma empresa líder em software especializada na digitalização da cadeia de valor de resíduos, uma das poucas em todo o mundo. Ajudamos as empresas na transição para uma economia circular e descarbonização para proteger os recursos, o meio ambiente e a saúde pública. Nossas soluções de software promovem a reintrodução de resíduos na cadeia de produção, reduzindo a necessidade de novos consumos de recursos.

Uma de nossas principais soluções é o Teixo, um software SaaS projetado especificamente para empresas de gerenciamento de resíduos. O Teixo oferece uma ampla gama de recursos, incluindo:

  1. Geração e processamento de documentação para transferência, tratamento e identificação de resíduos
  2. Controle de processos para agilizar as operações de resíduos e as ofertas de produtos
  3. Simplificação dos procedimentos para garantir a conformidade com os requisitos regulamentares
  4. Integração com todas as plataformas regionais de resíduos e com a plataforma estadual e-SIR (mais de 10 plataformas)
  5. Acessibilidade para usuários da web e sistemas remotos por meio de APIs internas e externas para aplicativos móveis e ERPs de clientes.

Teixo é a principal ferramenta de gestão de resíduos na Espanha, implementada em Mais de 700 instalações de gerenciamento de resíduos Com Mais de 3000 usuários ativos diários.

Esta série de postagens se concentra no projeto de atualizar nosso principal produto, o Teixo, sem nenhum tempo de inatividade e garantindo um serviço ininterrupto a todos os nossos clientes.

Este projeto envolveu não apenas a equipe técnica, mas também membros do nosso departamento de sucesso do cliente e, é claro, muitos usuários e clientes. Portanto, essas postagens abordarão não apenas questões técnicas, mas também gerenciamento de clientes e procedimentos para garantir a qualidade da Teixo.

1.1 Ponto de partida

As primeiras linhas de código de Teixo foram escritas em 2008, usando Rails 2.3 sobre Ruby 1.8.3, que era a tecnologia líder da época. A Teixo usa um banco de dados MySQL que, apesar de seu grande tamanho, tem excelente desempenho.

Nos últimos doze anos, a Teixo evoluiu em um ritmo rápido, adicionando recursos e se adaptando às novas necessidades de infraestrutura com grande sucesso. Os principais componentes do Teixo incluem:

  1. Um aplicativo web clássico com algum conteúdo dinâmico usando jQuery, mas não uma estrutura de front-end/back-end.
  2. Muitas APIs, algumas para uso interno (aplicativos móveis, outros produtos Teimas etc.), outras para uso externo de nossos clientes.
  3. Mais de 50 processos em segundo plano executados de forma assíncrona em seus próprios servidores dedicados. Usamos a joia Por LayedJob Para lidar com isso.
  4. Cerca de 20 cron jobs que são executados em diferentes frequências (por hora, diariamente, semanalmente ou até mensalmente).
  5. Essas peças são construídas juntas em um “monólito” modular.
  6. Atualmente, a Teixo é executada na infraestrutura da AWS usando dois grupos diferentes de escalabilidade automática: um para recursos web e de API e outro para processos em segundo plano. Também atualizamos a versão Ruby de Teixo de 1.8.3 para 2.7.3, com a ajuda de uma versão LTS especial do Rails 2.3 com suporte para Ruby 2.7.3.

No entanto, apesar de atualizar o Ruby, Teixo enfrentou muitos problemas, como gemas desatualizadas, uma API Activerecord obsoleta, solução de problemas de implantação, etc. Isso tornou os novos desenvolvimentos mais complexos e propensos a erros ao longo do tempo.

Em 2021, decidimos reduzir nossa dívida técnica e programamos um projeto para garantir a viabilidade da Teixo por pelo menos mais 10 anos. Decidimos atualizar a versão Rails de Teixo em diferentes etapas ou fases, uma para cada versão principal do Rails:

  • Trilhos 3.2
  • Trilhos 4.2
  • Trilhos 5.2
  • Trilhos 6.1
  • Rails 7.0

Nesse ponto, ainda não tínhamos certeza se seria melhor dividir essas etapas em etapas menores (uma para cada versão secundária, ou seja, Rails 3.0, posterior Rails 3.1 e finalmente 3.2) ou fazer cada grande mudança por vez. Embora o Rails tenha uma boa documentação sobre a atualização de aplicativos de uma versão para a próxima, o Guias de trilhos, esse projeto ainda era extremamente complexo.

1.2 Algumas métricas do Teixo

Para ajudar a entender a complexidade do projeto, aqui estão alguns dados sobre Teixo:

Gemas declaradas 59, total de joias 134.

1.3 O plano

No final de 2021, começamos a definir o “Plano”, que tinha vários requisitos principais:

  1. Devemos ser capazes de executar as versões antiga e nova do Teixo simultaneamente durante cada etapa do processo de atualização.
  2. O desenvolvimento de novos recursos ou correções deve continuar durante o processo de atualização.
  3. Precisávamos melhorar nossa cobertura de testes automáticos para garantir que as novas versões do Teixo atendessem aos nossos padrões de qualidade.

1.3.1 Análise inicial

Também começamos a procurar empresas que pudessem nos ajudar nesse processo — um “Guia” para nossa jornada. Encontramos uma empresa com sede na Filadélfia especializada em atualizar projetos Rails e começamos a trabalhar com ela na fase inicial de verificação da base de código de Teixo para preparar um relatório sobre as etapas e possíveis problemas que poderíamos encontrar neste projeto.

Depois de concluir a análise, eles propuseram um processo de atualização passo a passo, um para cada versão secundária do Rails (ou seja, Rails 3.0, 3.1, 3.2, 4.0 e assim por diante). No entanto, decidimos que essa abordagem demoraria muito para ser concluída, então optamos por etapas maiores para obter a atualização em um prazo razoável. Felizmente, tínhamos um forte departamento de controle de qualidade e sucesso do cliente que nos ajudou a testar e validar as etapas de atualização de forma rápida e confiável, mesmo com mudanças significativas na plataforma.

Sabíamos, e a análise confirmou, que precisávamos nos preparar para esse processo de atualização. Por vários meses, aplicamos o controle de qualidade na Teixo, desenvolvendo muitos novos testes automáticos em nosso ambiente de CI e expandindo os existentes. Também fortalecemos os protocolos do nosso departamento de Sucesso do Cliente para verificar a qualidade da Teixo.

A análise também propôs um mecanismo de inicialização dupla, com uma base de código adequada para inicializar em duas versões do Rails. No entanto, pensamos que essa abordagem seria muito difícil de lidar e aumentaria a sobrecarga de um projeto já muito complexo. Em vez disso, decidimos abordar o projeto de uma forma mais simplificada, fazendo as alterações de código necessárias em cada etapa do projeto.

O relatório incluiu um resumo das mudanças que precisavam ser feitas no Teixo para cada etapa do Rails, que era uma versão adaptada dos Guias do Rails para atualização.

1.3.2 Two Much Teixo

Propomos lidar com o projeto desenvolvendo simultaneamente duas versões do Teixo em cada etapa do processo. Por exemplo, durante a primeira fase, uma versão usará o Rails 2.3, enquanto a outra usará o Rails 3.2.

Nos ambientes de preparação e produção, as duas versões devem funcionar com o mesmo esquema de banco de dados. Somente uma versão deve lidar com migrações de banco de dados, mas ambas devem ser compatíveis com o esquema. Isso requer um cuidado especial com as migrações, garantindo que ambas as versões do código tenham as mesmas migrações ou, pelo menos, não excluam atributos/colunas em apenas uma versão.

1.3.3 Problema duplo

Precisávamos incorporar o processo de atualização em nosso fluxo de trabalho de desenvolvimento regular, então fizemos alguns ajustes em nosso processo de desenvolvimento.

  1. Em nosso repositório, tínhamos uma ramificação master para a versão 'antiga' do Teixo e uma ramificação 'edge' para as alterações necessárias na etapa de atualização atual.
  2. O desenvolvimento normal continuou empurrando as mudanças para o branch master, enquanto as mudanças de atualização do Rails foram empurradas para o novo branch 'edge'.
  3. Cada filial tinha sua própria configuração em nosso ambiente de CI (Jenkins) e executou seu conjunto de testes automatizados.
  4. As mudanças em ambas as filiais precisavam ser revisadas e aprovadas da mesma forma.

Em cada etapa do processo de atualização, há duas fases de trabalho a serem concluídas.

  • A primeira fase se concentra em fazer as mudanças necessárias para que as diferentes partes do Teixo funcionem com a nova versão do Rails e na verificação de erros no processo de teste automatizado no ambiente de CI, reduzindo-os a cada push. Essas mudanças são empurradas para o galho periférico. Durante os push iniciais, os resultados dos testes no ambiente de CI são “ignorados” e mesclados, apesar de haver muitos erros. Podemos chamar essa fase de trabalho técnico “proativo”.
  • Depois que esses erros forem corrigidos e a nova versão parecer estável, a segunda fase começa. Nesse ponto, a equipe técnica inicia seu trabalho “reativo” lado a lado com a equipe de Sucesso do Cliente (que tem maior envolvimento) para validar e testar a nova versão. Esse trabalho posterior nessa fase também envolve determinados clientes.

Se tivéssemos escolhido dar passos menores (pequenas mudanças na versão do Rails), a sobrecarga da segunda fase teria sido excessiva, especialmente para o departamento de Sucesso do Cliente.

Enquanto isso, o fluxo de trabalho normal de desenvolvimento da Teixo continuou. Durante essa segunda fase, tivemos que mudar nosso procedimento usual nos seguintes aspectos:

  1. A cada lançamento do Teixo, o novo código na ramificação master deve ser mesclado e revisado novamente na ramificação periférica. Vale ressaltar que o Teixo frequentemente tem pequenos lançamentos, geralmente um por semana, o que ajuda a manter as duas ramificações de código próximas o suficiente.
  2. Os desenvolvedores do branch master devem ter cuidado ao fazer alterações incompatíveis com a nova versão do Rails. Nesses casos, as alterações devem ser marcadas no código-fonte e o processo de mesclagem deve levar essas mudanças especiais em consideração.

Também modificamos nosso processo para novos lançamentos do Teixo para lidar com essa dualidade.

  1. Costumávamos ter um ambiente de teste em que a nova versão era implantada, testada e validada antes da publicação final em produção. Agora, temos dois ambientes de teste, um para o branch master e outro para o edge. Ambos compartilham o mesmo banco de dados.
  2. As implantações em teste são feitas em ambos os ambientes.
  3. Os testes e a validação do departamento de Sucesso do Cliente, antes do lançamento de uma nova versão, são realizados nos dois ambientes.

2. Rails 3.2 (I de II): Primeiros passos

Em abril de 2022, começamos a trabalhar na primeira e, de longe, a mais complexa etapa que daríamos: atualizar do Rails 2.3 para o 3.2 sem nenhuma etapa intermediária.

2.1 Colaborações

Contratamos um desenvolvedor especialista da nossa empresa 'Guide' para trabalhar com dois desenvolvedores seniores da TEIMAS. Infelizmente, essa colaboração não melhorou o projeto conforme o esperado, devido a vários obstáculos que dificultaram a colaboração:

  • A comunicação entre as equipes era ruim, principalmente devido a problemas de fuso horário entre a Espanha e onde o desenvolvedor externo estava localizado (com apenas uma hora de sobreposição). Esse era um problema significativo no trabalho diário.
  • A Teixo tem uma lógica de negócios altamente complexa que não é fácil de entender, a menos que você trabalhe no setor de gerenciamento de resíduos. A equipe externa não entendeu a lógica de negócios por trás do código, e mais orientações da nossa parte poderiam ter ajudado. Como alternativa, uma comunicação fluente entre as equipes teria sido benéfica.
  • A equipe externa não estava confortável com nossas ferramentas (Monday.com, Gerrit) e nosso fluxo de trabalho. Eles preferiram etapas menores e o mecanismo de inicialização dupla.
  • A forma de lidar com os problemas era muito diferente. Nosso objetivo era resolver problemas transversais de forma transversal, enquanto sua abordagem era fazer pequenas mudanças cada vez que surgia um problema. Por exemplo, Teixo usou intensamente a função replace_html (quase 300 aparições em mais de 50 arquivos) que foi descontinuada no Rails 3.2. A abordagem deles era corrigir cada instância dessa função, enquanto escolhemos definir nosso replace_html personalizado adaptado ao Rails 3.
  • Finalmente, descobrimos que a análise deles não abrangia todos os problemas que enfrentamos, mesmo vários que tiveram um impacto significativo (e que consideramos óbvios). Sua experiência na atualização de aplicativos Rails não se manifestou durante nossa colaboração.

Por fim, nossa colaboração terminou em termos amigáveis e mútuos antes de concluir a primeira etapa. Como resultado, concluímos a primeira atualização sem ajuda externa.

2.2 Testes, testes e mais testes

Antes de começar a trabalhar com uma nova ramificação para o Rails 3.2, fizemos um esforço significativo para aumentar a cobertura de testes automatizados da Teixo, atingindo quase 70%. Esse trabalho foi um dos componentes mais críticos para o sucesso do projeto.

Além disso, abordamos os avisos de descontinuação, como “ActiveRecord: :Base #class_name está obsoleto”, e implementamos outras melhorias, como organizar o código de forma mais eficaz e limpar partes não utilizadas do projeto.

2.3 Trabalho técnico

Esta seção aborda as várias mudanças técnicas, problemas e problemas que encontramos ao migrar do Rails 2.3 para o 3.2. Esse trabalho foi realizado principalmente na primeira parte da etapa de atualização, mas alguns também foram realizados durante a fase de “trabalho reativo” para resolver problemas e erros não detectados anteriormente, que foram trazidos à nossa atenção pelo departamento de Sucesso do Cliente e até mesmo por alguns clientes confiáveis.

2.3.1 Versão do Bump Rails para 3.2 LTS

2.3.1.1 Gemas e mais Gemas

Conforme mencionado anteriormente, atualizamos para uma versão do Rails 3.2 LTS suportada pela Makandra, uma equipe de desenvolvedores e engenheiros de operações veteranos do Rails, conforme eles se definem.

Usamos o Bundler e, depois de atualizar o gemfile, usamos:

gem 'rails', '~> 3.2.22.27'

tivemos que corrigir alguns problemas com o Gem.

1. Nossa versão da gema Mysql2 (0.5.3) era incompatível com esta versão do Rails. Como resultado, tivemos que mudar para uma versão diferente da gem Mysql2, que estava disponível no repositório Makandra:

gem 'mysql2', git: 'https://github.com/makandra/mysql2', branch: 'master'

2. Também tivemos que fazer um fork da gem activerecord de Makandra para modificá-la para oferecer suporte a gemas do Mysql maiores que as nossas. Isso envolveu simplesmente alterar uma linha em lib/active_record/connection_adapters/mysql2_adapter.rb.

requer 'active_record/connection_adapters/abstract_mysql_adapter'

gema 'mysql2', '> 0.3.10'

requer 'mysql2'

módulo ActiveRecord

Classe base

...

  1. O plugin JRails foi excluído e substituído por jquery-rails.
  2. Atualizamos várias outras gemas de forma direta.
  3. Algumas gemas exigiram testes e solução de problemas adicionais, mas não encontramos nenhum problema significativo. Por exemplo, usamos a gem Postmark, e seu comportamento mudou ao usar a API do Postmark. Como resultado, tivemos que redefinir a forma como o usamos dentro da Teixo.

2.3.1.2 Ajuste a configuração e o código

A configuração e vários outros arquivos exigiam dependências usando a API File, que foi alterada. Consequentemente, tivemos que fazer mudanças em várias áreas, incluindo:

requer File.dirName (__FILE__) + '/.. /config/boot '

Para

requer o arquivo.expand_path ('../.. /config/boot ', __FILE__)

Também tivemos que atualizar vários arquivos de inicialização e configuração junto com muitos inicializadores.

Configuração: confg.ru, por exemplo, foi alterado em várias linhas, principalmente alterado:

execute ActionController: :Dispatcher.new

Para

execute Teixo: :Application

Aplicação: Tivemos que construir um novo config/application.rb com a configuração do aplicativo (extraída do arquivo config/environment.rb) declarando o aplicativo Teixo como:

Módulo Teixo

classe Aplicação < Rails: :Aplicação

Bota: config/boot.rb foi simplificado para simplesmente carregar o Gemfile e o bundler.

requer 'rubygems'

# Configure as gemas listadas no Gemfile.

ENV ['BUNDLE_GEMFILE'] ||= arquivo. expand_path ('../.. /Gemfile ', __FILE__)

requer 'bundler/setup' se o arquivo existir? (ENV ['BUNDLE_GEMFILE'])

Rakefile: Tem que definir como carregar a configuração e o aplicativo

requer o arquivo.expand_path ('.. /config/aplicativo ', __FILE__)

...

Tissue: :Application.load_tasks

Rotas: A alteração mais demorada foi reescrever o arquivo config/routes.rb. A declaração de rota mudou significativamente de Rails 2 para 3. Apesar de ter uma ferramenta disponível para traduzir rotas de 2 para 3, nosso arquivo routes.rb não estava muito bem definido. Como resultado, tivemos que converter manualmente muitas das rotas para o novo formato.

Foi muito útil experimentar os auxiliares de rotas do console (quando o fizemos funcionar), por exemplo:

irb (principal): 00:20 > app.admin_users_path

=> “/admin/usuários”

2.3.1.3 Deixe-o funcionar

Apesar da atualização e das mudanças na configuração do Gems, o projeto não conseguiu lançar um console Rails nem o servidor. Precisávamos fazer várias outras mudanças.

RAILS_ENV não era mais suportado, tivemos que alterá-lo para Rails.env em todo o projeto.

Todas as referências ao pacote Controlador de ação: :xxxxxx Tinha mudado para Despacho de ação: :xxxxx (por exemplo, ActionController: :Routing: :Routes)

Todos Declarações de escopo deve mudar de 'named_scope' para simplesmente 'scope'. Além disso, alguns escopos em superclasses não funcionavam bem em subclasses, então tivemos que reescrever alguns deles usando “escopo”. Por exemplo:

named_scope:not_draft, lambda {{:conditions => ["# {table_name} .state! =?” , DOCUMENT_STATES [:rascunho]]}}

Tornou-se:

defina self.not_draft

scoped (:conditions => ["# {table_name} .state! =?” , DOCUMENT_STATES [:rascunho]])

Fim

Mudamos alguns attr_accessor_with_default usado no Teixo porque não era mais suportado. Corrigimos cada caso de acordo com a necessidade. Principalmente usando attr_writer para declarar o campo e/ou definir um método getter.

Salvar sem validações não estava mais disponível com um 'salvar (falso) ' call, agora é save (:validate => false).

Vários Métodos auxiliares usados em parciais de Haml (e Erb) não funcionava mais usando o operador -, tivemos que alterá-lo para o operador =.

- form_tag session_path, :id => 'login_form' de

Alterado para

= form_tag session_path, :id => 'login_form' de

O Flash O método no Rails 2 retornou um objeto que respondeu à API Hash, no Rails 3 isso não é mais verdade, então tivemos que chamar to_h em cada uso:

se flash.values.empty?

Alterado para

f_hash = flash.to_h

se f_hash.values.empty?

O uso de @template Os controladores internos não são mais suportados. Em vez disso, é recomendado usar o método view_context (No Rails 3, o novo Controlador abstrato foi introduzido).

Data
25/5/23
Categoria
Tecnologia
Rótulos
Compartilhe no
NOTÍCIAS

Assine o boletim informativo

Quer receber nossas novidades em sua caixa de entrada?