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.
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.
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:
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.
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:
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:
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.
Para ajudar a entender a complexidade do projeto, aqui estão alguns dados sobre Teixo:
Gemas declaradas 59, total de joias 134.
No final de 2021, começamos a definir o “Plano”, que tinha vários requisitos principais:
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.
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.
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.
Em cada etapa do processo de atualização, há duas fases de trabalho a serem concluídas.
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:
Também modificamos nosso processo para novos lançamentos do Teixo para lidar com essa dualidade.
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.
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:
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.
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.
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.
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
...
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”
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).