Lições aprendidas da migração para o Python 3

À medida que a data do fim da vida do Python 2 se aproxima, muitos desenvolvedores precisarão considerar a migração de suas compilações para o Python 3. Sentei-me com Dan Palmer para discutir a migração recente do Thread para o Python 3.

Escritórios da Thread em Londres. Este artigo foi publicado originalmente em able.bio

Thread é um serviço de estilo pessoal online. Quando você se inscreve, o website deles faz uma série de perguntas introdutórias sobre seus estilos, tamanhos, orçamento, itens de propriedade, etc., que seus estilistas pessoais usam para fornecer recomendações surpreendentemente bem adaptadas. Sediada em Londres, com cerca de 50 funcionários, o site de e-commerce da Thread, Django, recebe aproximadamente 850 mil visitas por mês.

Rhett: Como é a arquitetura do site do Thread?

Dan: O segmento tem sido fortemente ativado pelo Django desde o primeiro dia. A equipe fundadora tem muita experiência com o Django e, portanto, o projeto foi arquitetado em escala à medida que aumentava a complexidade. Nosso serviço de recomendações é construído com o Flask usando scikit-learn , há muita ciência de dados usada no Thread, e também temos alguns serviços menores que máquinas de estado de energia e alguns serviços de monitoramento, mas na maioria é um monolito do Django. O Monolith recebe um termo ruim como termo, mas acho que o Django se presta muito bem para dividir as coisas em aplicativos e criar essa hierarquia de lógica e modelos.

Rhett: Por que você decidiu migrar para o Python 3?

Dan: Eu estou no Thread há quatro anos e ficou claro depois do primeiro ano que o Python 2 iria desaparecer eventualmente. Eu acho que por um longo tempo houve um limbo onde as pessoas não estavam convencidas de que o Python 3 era o futuro e a comunidade estava pensando em rodar ambas as versões a longo prazo, de uma forma que eu acho que é o tipo de coisa que está acontecendo até hoje. Eu me lembro do primeiro PyCon UK em que estive em 2015 e naquele ano muitas pessoas estavam falando sobre a migração de suas codebases maiores para o Python 3. No ano seguinte, muitas pessoas não estavam falando sobre isso, muitas pessoas pareciam ter acabado de fazer isto.

Em 2017, tínhamos poucas dependências compatíveis apenas com o Python 2. Eles eram muito pequenos ou tinham garfos do Python 3 para os quais poderíamos nos mudar. A data do fim da vida do Python 2 foi confirmada (1 de janeiro de 2020) e o Django também se comprometeu a usar o Python 3. Nós também rodamos nossos sistemas no Debian e eles anunciaram uma data de término para o suporte do Python 2, que era um ano antes. Isto foi devido ao seu ciclo de lançamento, eles não queriam ter um período durante o qual eles estavam suportando o Python 2 enquanto ele não era mais oficialmente suportado pela equipe Python.

Então, acho que poderíamos ter deixado a migração até o ano que vem, mas nos esforçamos para garantir que tenhamos um código legal, que estamos escrevendo código em boas formas de manutenção a longo prazo e, finalmente, é isso que o Python 3 realmente faz para Python . Isso torna mais fácil escrever um código bom e, por isso, havia lugares em que estávamos sentindo a dor de não ter isso, particularmente com coisas como sistemas de tipos, que precisavam ser gerenciados com mypy , e muito do suporte Unicode que temos nunca tive que lidar com isso porque estamos disponíveis apenas no Reino Unido atualmente. No entanto, de vez em quando alguma coisa aparecia e tínhamos um produto que tinha um símbolo Unicode em que não tínhamos servido em algum lugar ou que ingeríamos um arquivo CSV de algum fornecedor e não funcionava.

Nós temos algo chamado Tech 10% na Thread, que é como o tempo de 20% do Google, então as tardes de quarta-feira são quando os desenvolvedores podem trabalhar naquilo que eles acham que é importante. Então, durante esse tempo, assumi a tarefa de começar a nos mover para o Python 3.

Rhett: Como você planejou e se preparou para migrar para o Python 3?

Dan: Curiosamente, uma das coisas que não fizemos tão bem quanto poderíamos ter, foi planejar corretamente. Como a preparação estava acontecendo apenas no Tech 10% de tempo, nós não tínhamos toda a equipe trabalhando nisso e não conseguimos o planejamento completo que qualquer outro grande trabalho de tecnologia na Thread teria, isso é algo que eu acho nós aprendemos para o futuro.

Dito isto, nós tivemos um plano. Começamos atualizando nossas dependências para que elas funcionassem no Python 3. Depois disso, começamos a adaptar nossas práticas de codificação para que elas criassem código compatível com versões futuras, usamos alguns linters do Flake8 e outros plugins para isso. .

Por exemplo, existe um plugin para Flake8 que permite banir certas importações, então nós passamos pela lista de coisas que a seis biblioteca tem sob seis.move então para onde as coisas se moveram em uma biblioteca padrão você pode usar seis.move como um caminho compatível entre Python 2 e Python 3. Então, banimos todas as importações do Python 2, movemos tudo para seis e isso significava que não estávamos importando do lugar errado, então poderíamos fazer isso de forma bastante incremental. Muitas dessas coisas começaram a aparecer de forma incremental e a cada semana nós fazíamos um pequeno PR que apenas mudava algumas coisas. Eu acho que isso foi provavelmente vários meses de trabalho, mas foi apenas algumas horas aqui e ali e ao longo de vários meses começamos a chegar ao ponto em que o código se parecia muito mais com o código do Python 3.

O estágio depois disso foi fazer nossos testes rodarem. Nossos testes nem sequer foram executados no Python 3, para não mencionar a passagem. Então, quando nós tivemos todos os linters no lugar e nós tínhamos corrigido todos esses problemas, os testes estavam muito mais perto de rodar, então havia apenas alguns pequenos bugs que nós poderíamos resolver para colocá-los em funcionamento. Eu acho que a primeira vez que eles correram eles estavam na maior parte certos, nós provavelmente tivemos 80% de nossos testes passando e então isso deu início a três de nossas quatro semanas usando Tech 10% de tempo para fazer todos os testes passarem.

Na maioria das vezes, era muita manipulação de CSV e coisas assim. Temos que lidar com vários armazéns de terceiros e empresas de navegação que têm servidores FTP com arquivos CSV, eles nunca ouviram falar de uma API HTTP, então foi divertido. Depois de corrigir os testes, encontramos lugares onde precisávamos de mais testes. Nós encontraríamos um erro específico em dizer um sistema CSV e então aprenderíamos com isso e iríamos escrever testes para a mesma coisa em todos os nossos outros, apenas para ter certeza de que não estávamos tendo os mesmos problemas em todo o base de código.

Depois que os testes foram aprovados, adicionamos outro trabalho de IC a Jenkins, que executaria todos os testes no Python 3 e nos certificaria de que poderíamos criar o pacote. Então a ideia era essencialmente tratar a build do Python 3 como um cidadão de primeira classe.

Inicialmente, esperávamos que fosse apenas algumas semanas para que a compilação do Python 3 ficasse estável antes de começarmos a enviá-la, mas sabíamos que a mudança seria um grande risco e, por isso, queríamos planejar o futuro. Queríamos ter certeza de que faríamos isso em um momento em que teríamos desenvolvedores suficientes no escritório, se necessário, e fazê-lo em um momento que não fosse um período crítico de vendas. Então acabamos adiando por cerca de três meses e meio e mantendo a compatibilidade do Python 2 e Python 3 durante esse tempo.

Isso era propenso a erros e um pouco doloroso. Eu estava rodando o Python 3 no meu ambiente de desenvolvimento por vários meses e cerca de um mês depois da compatibilidade com Python 3, todos os desenvolvedores usavam o Python 3 em seus ambientes de desenvolvimento, o que resultou em construções flakey no Python 2 como nós escrevíamos algo que funcionava para o Python 3 sem perceber e, em seguida, o pressionávamos e a compilação falhava. Então, isso causou um pouco de atrito, e isso nos custou tempo e então pressionamos um pouco mais para que o Python 3 fosse lançado e, eventualmente, marcássemos a data em uma terça-feira de novembro.

Eu digo que não queríamos que a troca acontecesse em um período de vendas alto, mas acabamos fazendo a semana da Black Friday, que foi outra das coisas que aprendemos com isso. Nós nos comunicamos sobre isso muito bem dentro da equipe de tecnologia, mas não nos comunicamos bem com todos os outros. Acho que estávamos mais confiantes do que talvez devêssemos ter sido.

O resto da empresa não sabia que havia um alto risco de dar errado e, por isso, chegamos em uma manhã de terça-feira e tentamos empurrá-lo ao vivo. Não funcionou e acabamos percebendo por volta das 8 horas que não ia funcionar naquele dia e então voltamos. Então nós chegamos cedo novamente o próximo dia e tentamos novamente. Algumas coisas deram errado, mas menos e acabamos insistindo e corrigindo os problemas à medida que surgiam e estivemos no Python 3 desde então.

Rhett: Você teve algum problema ao migrar pelo Python 3?

Dan: O principal que nos causou a reversão na terça-feira foi basicamente um problema entre nossas sessões e nosso sistema de cache. Isso significava que todos os nossos usuários teriam sido desconectados e precisariam fazer login novamente. Temos nossas sessões armazenadas em cookies assinados e armazenamos alguns de nossos dados de usuário no Memcached como classes Python em pickled. Quando nos mudamos para o Python 3, havia algumas incompatibilidades, até problemas Unicode, que resultavam em uma diferença entre bytes em strings e isso significava que não éramos capazes de validar os cookies que as pessoas tinham contra os dados armazenados no Memcached. .

A experiência de discussão é principalmente sobre roupas de navegação e aprender sobre como se vestir, por isso é uma experiência de usuário muito melhor para nós termos um tempo de expiração relativamente longo em nossos cookies. Sabemos que as pessoas entram e saem da navegação, portanto, em termos de experiência do usuário, desconectar todo mundo não era algo que achávamos razoável, e é por isso que decidimos não prosseguir na terça-feira e passamos o dia escrevendo alguma compatibilidade nosso cache e assinatura de cookies para que, quando o lançássemos no dia seguinte, não tivéssemos esse problema.

Parte do plano de implantação era que nós obteríamos os servidores web em execução, removeríamos tudo, colocaríamos os servidores da web de volta e as filas realmente críticas como nossa fila de processamento de check-out, por exemplo. Então a ideia era que, ao longo do dia, apresentássemos as outras filas de cada vez e observássemos os logs em busca de erros. O nosso armazém abre às 8 da manhã e todo o software que eles usam é o nosso aplicativo Django e por isso tinha que estar funcionando até então e foi um sucesso, mas quando começamos a trazer filas, descobrimos que ainda havia uma feira várias coisas que não estavam funcionando exatamente como esperávamos e acabamos descobrindo no dia seguinte que muitos itens estavam fora de estoque, pós-ordem. O pós-ordem fora de estoque é quando alguém comprou algo e depois enviamos um e-mail para dizer que realmente não o recebemos. Geralmente só acontece em circunstâncias muito raras e temos que reembolsar o cliente, não é uma boa experiência. É uma métrica que acompanhamos de perto e tentamos minimizá-la. Durante esse período, o resultado foi que algumas das maneiras pelas quais nossas filas estavam sendo executadas em um processo serial causaram um problema no qual estávamos executando vários da mesma fila. Eles estavam todos agitando as coisas muito rapidamente causando problemas, jogando erros e marcando muitas coisas como fora de estoque. Nossa equipe de ops provavelmente passou cerca de dois ou três dias limpando as conseqüências disso. Esse foi provavelmente o maior problema que tivemos.

Nós realizamos reuniões chamadas 5 Whys quando algo dá errado em grande escala para descobrir a causa raiz do problema. Nós acabamos executando um 5 Whys para este problema e, finalmente, concluímos que a causa raiz era como uma equipe de tecnologia que não comunicamos todos os riscos e tudo o que poderia ser afetado bem para o resto da equipe. Se tivéssemos feito isso, talvez eles pudessem identificar os problemas mais cedo, talvez se tivéssemos mencionado os riscos que eles teriam dito não fazer isso na Black Friday ou eles teriam visto alguns dos sistemas que estavam indo toque e disse "na verdade, você pode querer escrever alguns testes para esta área em particular, porque isso é uma coisa muito importante para nós".

Portanto, não foi um lançamento sem problemas, mas até o final da semana os únicos problemas que estávamos tendo eram coisas menores, como relatórios que só são gerados uma vez por semana, e poderíamos facilmente corrigir isso e reexecutar o relatório. Nós realmente não tivemos nenhum problema desde então.

Rhett: O que você recomendaria para as pessoas que estão se preparando para realizar a mesma migração?

Dan: Eu diria que planeje as etapas de como você vai ter sua base de código compatível com o Python 3. Invista em ferramentas que ajudarão você a saber que é compatível. Escreva testes, se você não tem testes em certas áreas de sua base de código e você sabe que eles lidam com arquivos de dados que podem estar em diferentes formatos ou coisas que o Python 3 mudou notavelmente, eu diria escrever mais testes para aqueles tipos de coisas. Nós pensamos que nós tínhamos uma boa cobertura de teste, mas havia alguns pedaços onde nós achamos que estávamos faltando. Invista em ferramentas como linters e configure versões paralelas de seu software que é executado no Python 3.

Planeje como você vai chegar ao Python 3 compatível de uma maneira que não interrompa o resto do seu time de desenvolvedores. Isso foi algo que eu acho que acertamos, quando eu fui para desenvolvedores e disse: "Ei, você quer usar o Python 3 na sua máquina?" Tudo na maior parte acabou de funcionar e isso significou que o resto da equipe achou Foi uma coisa boa e comprei para ele.

Eu acho que a outra coisa seria planejar o lançamento, especialmente se você tem um serviço web, onde realmente importa que você esteja pronto e que as pessoas possam fazer transações. Portanto, planeje passo a passo com muito cuidado o que você vai fazer e, a qualquer momento, como você pode perceber que não está funcionando e como vai reverter isso para um estado de trabalho conhecido.

Comunique-se fora de sua equipe de engenharia, certifique-se de que as pessoas saibam quais são os riscos, o que pode parar de funcionar e o que precisam estar assistindo. Certifique-se de que todos saibam como identificar que algo não está funcionando, porque às vezes é difícil saber, especialmente se você tem um sistema grande.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *