Nossa equipe é responsável pela operação e desenvolvimento de um grande produto corporativo.
No início de 2017, interrompendo uma grande implementação e relendo as “lições aprendidas”, decidimos firmemente revisar o desenvolvimento e a entrega de nosso aplicativo. Estávamos preocupados com a baixa velocidade e qualidade da entrega, não nos permitindo fornecer o nível de serviço que os clientes esperam de nós.
Era hora de passar das palavras para as ações - para mudar os processos.
Este artigo falará brevemente sobre onde começamos, o que fizemos, qual é a situação agora, que dificuldades encontramos, o que temos que deixar para trás e o que mais planejamos fazer.
Iniciar
Um pouco sobre o sistema
O aplicativo é um exemplo clássico de um aplicativo corporativo monolítico do "vazamento arquitetônico dos anos 2000":
- Operado e desenvolvido ao longo de 15 anos.
- É um conjunto de meia dúzia de WinForms, serviços Windows e aplicativos ASP .Net vinculados a um único banco de dados MS SQL.
- Tamanho da base de código: ~ 1MLOC para C #, ~ 9000 objetos de banco de dados. Grande parte da lógica de negócios é executada no lado do banco de dados.
- O aplicativo consiste em ~ 250 + soluções para criar um cliente win / web (uma solução por grupo de formulários relacionados). Este é um legado do processo de desenvolvimento anterior e da arquitetura do cliente.
- O aplicativo suporta vários tipos de processos (clientes) alterando a configuração interna: definindo processos, permissões, campos flexíveis etc. nas tabelas de configuração do banco de dados do sistema. Ao mesmo tempo, a base de código do aplicativo é a mesma para todos os clientes.
- O aplicativo é implantado e suportado em mais de 25 sites (cada site é uma instância independente do sistema) e atende a um total de vários milhares de usuários finais em diferentes fusos horários.
- O desenvolvimento e a montagem da aplicação final e de seus componentes são realizados pelo contratado.
- o código foi armazenado na lateral do contratante (versão local do MS TFS). O código é transmitido ao cliente mensalmente na forma de um arquivo da versão atual da ramificação do repositório principal.
- a entrega foi realizada com a entrega de "atualizações delta": para o aplicativo (conjunto de dll, exe etc.) e componentes do banco de dados (conjunto de scripts sql create / alter). O aplicativo foi construído e os pacotes delta preparados pelo contratado.
- o processo de implantação foi suportado pelo sistema de transporte, as alterações foram aplicadas automaticamente.
A entrega é realizada como parte dos lançamentos mensais (como eu organizei, eu lhe disse anteriormente aqui ).
Problemas existentes
Falta de controle
- Apesar da propriedade formal do código, a montagem real do aplicativo pelo cliente era impossível.
- Como resultado, é impossível verificar a operacionalidade do código transmitido ao cliente.
- alterações no código - não transparentes para o cliente. Não é possível corresponder às alterações solicitadas e reais no produto.
- a análise de código é difícil para SQL e impossível para componentes C #
Entrada de trabalho e erros
- A preparação de "pacotes delta" é um procedimento demorado para o desenvolvimento, uma fonte de erros e certos custos do projeto.
- A implantação de um aplicativo Delta Packet requer o rastreamento da ordem dos pacotes. Um erro de pacote fora de ordem é um grande problema de implantação e uma fonte significativa de incidentes.
- Regressões ocorrem regularmente: erros que parecem ter sido reparados e correções implementadas no produto apareceram novamente.
Limitações
- a capacidade de restaurar o estado do sistema em um momento no passado (alterações de reversão) está praticamente ausente.
- a capacidade de escalar efetivamente os recursos de desenvolvimento e testes iniciais, atraindo funcionários dos clientes, está praticamente ausente.
Resultados esperados
No início do projeto, estabelecemos metas óbvias para resolvermos os problemas identificados acima.
- Transferir repositório de códigos para o controle do cliente
- Mova o processo de criação do aplicativo para o lado do cliente
- Modifique o processo de distribuição das mudanças, abandonando o “delta das mudanças” em favor de uma atualização completa
Além disso, usando as soluções obtidas quando os dois primeiros objetivos foram alcançados, calculamos:
- Melhore a qualidade técnica das soluções resultantes através do controle de código
- Aumente o envolvimento e a usabilidade do teste, fornecendo a implantação de autoatendimento.
Etapas de um longo caminho
Análise do estado atual dos processos de desenvolvimento
Primeiro passo: analise o processo de desenvolvimento do contratado existente. Isso ajudou a planejar as alterações para, se possível, não interromper o trabalho.
Infelizmente, o conhecimento do processo de desenvolvimento mostrou que, no entendimento da indústria de TI, atualmente, o processo estava ausente.
- O código do banco de dados e a lógica de negócios para ele não foram atualizados no repositório. O principal motivo: a falta de ferramentas que implementam o assembly a partir do código no repositório e a implantação do resultado. Portanto, o código no repositório é apenas documentação.
- A versão "real" do código do banco de dados está no "banco de dados de desenvolvimento" comum, no qual dezenas de desenvolvedores trabalham.
- O código do aplicativo cliente (C #, ASP.NET) foi mantido no repositório, mas a qualidade e a pontualidade das confirmações não eram garantidas.
- A montagem dos componentes (não toda a aplicação) foi realizada nas estações do desenvolvedor. Não está totalmente claro como o código foi atualizado antes da montagem. O componente montado foi disposto em uma pasta compartilhada compartilhada. A partir daí, um "pacote delta" foi formado para o cliente.
- A completa falta de prática de manter ramos de desenvolvimento. Por sinais indiretos, suspeitamos disso por um longo tempo - mas depois de mergulhar no processo, tudo ficou óbvio.
Mudando para um novo repositório e sistema de controle de versão
A dependência das plataformas e padrões corporativos da MS determinou a escolha do ambiente de desenvolvimento - Team Foundation Server.
No entanto, quando iniciamos o projeto diretamente (abril de 2017), a versão do Visual Studio Team Services foi lançada. O produto parecia muito interessante, foi designado como uma direção estratégica para o MS, oferecendo repositórios git, montagem e implantação para on-prem e cloud.
O TFS corporativo no local ficou para trás da versão e funcionalidade do VSTS, a migração para a nova versão estava apenas no processo de discussão. Não queríamos esperar. Decidimos mudar imediatamente para o VSTS, pois isso reduzia nossos custos indiretos de suporte à plataforma e nos fornecia controle total sobre como e o que fazemos.
No momento do início das alterações, a equipe de desenvolvimento possuía experiência com o TFSVC, o código do aplicativo era armazenado em um repositório desse tipo. Por outro lado, o GIT há muito se tornou o padrão para a comunidade de TI - os clientes e consultores terceirizados recomendaram a mudança para esse sistema.
Queríamos que a equipe de desenvolvimento estivesse envolvida na decisão sobre um novo sistema de controle de versão e fizesse uma escolha informada.
Implementamos dois projetos no VSTS com repositórios diferentes - TFSVC e GIT. Foi definido um conjunto de cenários, proposto para testar e avaliar a usabilidade em cada um dos sistemas.
Entre os cenários avaliados estavam:
- Criar e mesclar ramificações
- Organização de trabalho conjunto (em um ou diferentes ramos)
- Alterar operações da cadeia (confirmar, desfazer)
- Integração de terceiros
- A capacidade de continuar trabalhando quando o servidor não está disponível.
Como resultado, como esperado, o GIT foi escolhido, e até agora ninguém se arrependeu.
Como um processo, começamos a usar o GitFlow. Esse processo forneceu controle suficiente sobre as alterações e permitiu a entrega de lançamentos, como estamos acostumados.
- Defendemos o ramo de desenvolvimento com uma política que exigia que todas as alterações passassem por solicitações de recebimento.
- Tentamos aderir à prática de "um ticket - uma requisição pull". Alterações de tickets diferentes nunca são combinadas em uma alteração. Tentamos fazer o possível para testar a ramificação de recursos para evitar a situação com correções nas solicitações de pull subsequentes.
- Ao mesclar no desenvolvimento, todas as alterações são mescladas em uma única confirmação (squash).
- As ramificações de liberação são criadas a partir do desenvolvimento.
- Se necessário, na ramificação da versão, você pode adicionar as alterações mais recentes seletivamente (seleção de cereja) ou todas (rebase). Não realizamos a correção diretamente no ramo de lançamento.
- Depois de implantar a versão mais recente no produto, ela passa a ser dominada por força de envio (apenas algumas pessoas têm esse direito)
Automação de montagem de produtos
A aplicação era um grande número de montagens, centenas de soluções. Como se constatou durante a auditoria do processo, tudo isso foi coletado separadamente e “manualmente”.
No primeiro estágio, decidimos não refazer tudo do zero (para não interromper a entrega existente), mas "agrupar" o assembly em um conjunto de scripts msbuild - um script por componente.
Assim, rapidamente obtivemos scripts que executavam todos os artefatos intermediários necessários e, no final, o produto final.
Uma história separada é um design de banco de dados. Infelizmente, o sistema contém vários componentes CLR que não foram bem estruturados. As dependências não permitem uma base de implantação simples com conteúdo. No momento, isso está sendo resolvido por um script de pré-implantação.
Além disso, devido ao cenário desigual do sistema (as versões 2008 e 2014 do SQL Server foram instaladas em diferentes pontos), foi necessário organizar a montagem do projeto base para as versões 2.0 e 4.0 do .Net.
Depois que todos os scripts estavam prontos e testados, eles foram usados no script de construção VSTS.
Imediatamente antes do início da montagem, as versões de todos os produtos foram atualizadas para um número padrão comum, incluindo o número de construção da construção. O mesmo número foi armazenado no script pós-implantação. Assim, todos os componentes - o banco de dados e todos os aplicativos clientes - saíram consistentes e igualmente numerados.
Implantação na bancada de testes
Depois que a versão inicial do processo de compilação foi concluída, prosseguimos com a preparação do script de implantação.
Espera-se que o banco de dados tenha sido o mais problemático.
A implantação de uma cópia "superior" de um banco de dados real revelou muitos conflitos entre a montagem e o estado dos sistemas reais:
- Versões inconsistentes no GIT e no sistema real
- Esquemas de banco de dados pertencentes a usuários que foram planejados para serem excluídos.
Estabilização do processo de desenvolvimento
É claro que é estranho falar sobre isso, e mais ainda escrever aqui, mas a mudança mais séria para os desenvolvedores foi a introdução do princípio "se isso não está no git, isso não existe". Anteriormente, o código era confirmado "para reportar ao cliente". Agora - sem isso, é impossível entregar qualquer coisa.
O mais difícil foi com o código do banco de dados. Após mudar para a implantação do banco de dados a partir do repositório, através da montagem e implantação usando o sqlpackage, a abordagem "delta" foi substituída pela abordagem "estado desejado". Pacotes eram coisa do passado; tudo tinha que ser implantado automaticamente.
Mas! Até a transição completa para o novo processo de implantação, as alterações ainda precisavam ser entregues. E era necessário fazer isso da maneira antiga - "atualizações delta".
Enfrentamos a tarefa de garantir consistência completa e constante do estado do sistema ao entregar pacotes delta e o conteúdo do repositório.
Para isso, organizamos o seguinte processo:
- Regularmente, o código do repositório era coletado e implantado em um banco de dados "modelo" vazio.
- Com base na base "modelo", um autoteste especial estava sendo preparado. Para cada objeto do banco de dados "modelo", as somas de verificação foram calculadas. O autoteste contém todas essas somas de verificação e, na inicialização, calcula as somas de verificação dos objetos correspondentes do banco de dados "verificado". Qualquer discrepância na composição dos objetos ou em suas somas de verificação leva a uma queda no teste.
- O teste de "queda" proibiu automaticamente a transferência de pacotes do ambiente de teste mais abaixo no cenário. Essa integração já foi implementada no sistema de transporte anterior.
Assim, usando o controle automático, foi possível atualizar relativamente rápido o código do banco de dados do produto no git e mantê-lo sem esforço adicional por parte da equipe do projeto. Ao mesmo tempo, os desenvolvedores começaram a se acostumar com a necessidade de confirmar o código de maneira correta e oportuna no repositório.
Implantação do produto em ambientes de teste de integração
Após concluir o estágio anterior, prosseguimos diretamente para a implantação do aplicativo em um ambiente de teste. Paramos completamente de aplicar pacotes delta aos sistemas de teste e mudamos para a implantação automática usando o VSTS.
A partir desse momento, toda a equipe começou a receber os primeiros frutos dos esforços gastos anteriormente: a implantação ocorreu sem nenhum esforço adicional. O código personalizado foi coletado, implementado e testado automaticamente.
Infelizmente, como entendemos posteriormente, o "alinhamento do repositório" levou ao fato de termos uma versão da versão estável do "develop", mas a versão da "produção" ainda estava indisponível. E, portanto, não havia nada além do ambiente de teste com QAS e PRD.
O código do aplicativo no lado do banco de dados pode ser comparado ao código produtivo e entender as diferenças. Não havia nada com o qual comparar aplicativos clientes - havia apenas uma versão produtiva atualizada na forma de um conjunto de arquivos executáveis e, a partir do qual eles foram criados, era impossível ter certeza.
Testando o produto como resultado da montagem automática
Após alterar a abordagem de montagem, o produto passou por extensos testes de regressão. Era necessário ter certeza de que o aplicativo estava funcionando e nada foi perdido.
Ao testar, ficou mais fácil com a funcionalidade localizada ao lado do banco de dados. Felizmente, desenvolvemos um conjunto significativo de autotestes que cobrem áreas críticas.
Mas não houve testes para C # - portanto, tudo foi verificado manualmente. Foi uma quantidade significativa de trabalho e a verificação levou algum tempo.
Salto de fé - implantação produtiva piloto
Apesar dos testes, a implantação em um produto pela primeira vez foi assustadora.
Tivemos sorte - tínhamos acabado de planejar a próxima implantação do sistema em um novo site. E decidimos usar essa chance para uma implantação piloto.
Os usuários não viram, os possíveis erros da nova montagem foram fáceis de corrigir, o trabalho produtivo real ainda não foi iniciado.
Implementamos o sistema e, por várias semanas, ele estava no modo pré-produtivo (carga baixa, um determinado padrão de uso que pode ser ignorado no produto). Durante esse período, vários defeitos que foram perdidos durante o teste foram revelados. Eles foram corrigidos conforme foram encontrados e a nova versão foi lançada imediatamente para verificação.
Após o lançamento oficial e uma semana de suporte pós-lançamento, anunciamos que esta é a primeira cópia montada e entregue "de uma nova maneira".
Esta versão do assembly tornou-se a primeira versão estável da ramificação principal, foi pendurada com tags fisrt_deployment (não solicitamos ícones com um hash do commit).
Escale a implantação em todo um cenário produtivo
Como James Bond disse: "A segunda vez é muito mais simples". Após o sucesso da implantação piloto, conectamos rapidamente as instâncias restantes de sistemas de um tipo semelhante.
Mas o sistema possui vários tipos de uso - uma funcionalidade pode ser usada para um tipo e não usada em outros casos. Consequentemente, a funcionalidade testada na implementação do primeiro tipo não garantiu necessariamente o sucesso para outros casos.
Para testar a funcionalidade dos demais tipos de uso, começamos a usar projetos ativos que estavam em desenvolvimento. A idéia era semelhante e a primeira implantação - começamos a usar montagens automáticas, "deslizando" para os usuários junto com a funcionalidade de design. Assim, os usuários, trabalhando com a versão "projeto" do produto, ao mesmo tempo verificaram a funcionalidade antiga.
O dimensionamento em si revelou problemas técnicos inesperados:
Cenário não homogêneo do sistema
Além de implantar diretamente o aplicativo, tivemos que primeiro cuidar para que tudo acontecesse em todos os lugares - versões .Net, Powershell e módulos. Tudo levou bastante tempo.
Conexão de rede
Em alguns sites, a conexão de rede simplesmente não permitia bombear todos os componentes da montagem. Houve tempos limite, danos durante a transferência. Verificamos e tentamos muitas coisas - sem muito sucesso.
Eu tive que me debruçar sobre a seguinte solução: o script de compilação foi finalizado para que todos os resultados fossem compactados em um arquivo grande, que foi cortado em pequenos fragmentos (2 mb cada). Finalizamos o cenário de implantação para eliminar a simultaneidade ao fazer o download de artefatos, aceitamos todos os fragmentos de 2 megabytes e restauramos deles o que já pode ser expandido.
Conflito com antivírus
Outro problema estranho que encontramos é um conflito entre o software antivírus e uma das etapas de implantação: quando todos os tipos de arquivos "suspeitos", como .js, .dll, são extraídos dos arquivos de artefatos, o antivírus começa a observá-los de perto. E o mais estranho é que o antivírus começa a correr para o arquivo antes do final da descompactação e o processo de descompactação cai com a mensagem "o arquivo está ocupado por outro processo". Enquanto estamos lutando com isso, excluindo a pasta local com artefatos da verificação, não é muito bom, mas não criamos mais nada.
Melhoria de processo
, " " — .
- (service-now.com) VSTS Work Items. develop — .
- CI feature . —
- "self-service"
- — . , .
- : , CI/CD ,
- ( )
- - ( ) . — , .
- VSTS , , .
Sumário
- MS VisualStudio Team Services ( — Azure Devops) . — GIT
- (/)
- git / GitFlow .
- code review .
- CI. , feature , .
- . , .
- ( ) — . - .
- - 1 . - .
- "" .
№ | | |
---|
1 | — , | 6 |
2 | Desde a primeira implantação até um ambiente de teste - da primeira versão piloto a uma produção | 3 meses |
3 | Da implantação piloto à produtividade, à primeira versão para todas as instâncias | 5 meses |
Duração total - 14 meses
A duração, especialmente na fase final, foi amplamente determinada pela coordenação e um calendário acordado para a manutenção do sistema.
Custos trabalhistas
O custo total dos funcionários envolvidos do cliente e da organização contratante para todo o trabalho relacionado à mudança é de aproximadamente 250 pessoas * dias.