Prévia do material em texto
Implementação, Testes e Qualidade de Software no CVDS Ágil João Paulo Faria 2022 2 Implementação, Testes e Qualidade de Software no CVDS Ágil João Paulo Faria © Copyright do Instituto de Gestão e Tecnologia da Informação. Todos os direitos reservados. 3 Sumário Capítulo 1. Processos e cultura .................................................................................. 5 Processos ...................................................................................................................... 5 Cultura ........................................................................................................................... 9 DevOps ........................................................................................................................ 10 Capítulo 2. Repositórios ........................................................................................... 13 Tipos de repositórios .................................................................................................. 13 Branches ...................................................................................................................... 14 Capítulo 3. Continuous Integration .......................................................................... 19 Como funciona um CI .................................................................................................. 20 Ferramentas de Integração Contínua ......................................................................... 22 Capítulo 4. Continuous Delivery (CD) ....................................................................... 24 Estratégias de Deploy ................................................................................................. 25 Principais Ferramentas para CD .................................................................................. 26 Capítulo 5. Qualidade ............................................................................................... 28 Testes Unitários .......................................................................................................... 28 TDD – Test Driven Development ................................................................................ 30 Análise estatística de códigos ..................................................................................... 33 Segurança .................................................................................................................... 35 Vulnerabilidade de Pacotes ........................................................................................ 36 4 Pirâmide de testes ...................................................................................................... 37 Capítulo 6. Gestão de Mudanças ............................................................................. 39 Configurações ............................................................................................................. 39 Feature Toggles ........................................................................................................... 41 Wiki ............................................................................................................................. 43 Capítulo 7. Monitoramento ..................................................................................... 46 Logs ............................................................................................................................. 47 Alertas ......................................................................................................................... 49 Referências………………….. ................................................................................................. 51 5 Capítulo 1. Processos e cultura Processos Conceitualmente, processo de desenvolvimento de software é um conjunto de procedimentos estratégicos e organizados com o propósito de se pensar (arquitetar), desenvolver (codificar), testar e manter o software, garantindo seu bom funcionamento. Softwares bem desenvolvidos, oferecem benefícios para os negócios. Trata-se de um produto capaz de otimizar áreas e serviços de todos os tipos de segmento, auxiliando na automação e otimização de tarefas, e na redução de custos. Para que seja bem entregue, o processo de desenvolvimento de software deve cumprir etapas definidas, aplicando-se nelas, as melhores práticas para cada circunstância. O desenvolvimento de software possibilita unir tecnologia e estratégias, provendo soluções para problemas de um negócio. Um software possui objetivos únicos e é responsável por aumentar o nível de produtividade das atividades em que for empregado. Tecnologias embarcadas em um sistema podem permitir maior controle dos processos da empresa. O que possibilita que tarefas, antes manuais, sejam feitas com eficácia e assertividade, minimizando erros, por exemplo. Por envolver muito planejamento e estratégia, além de muitas análises, manutenções, implementações e testes, o software possibilita automatizar diversas aplicações. Cada codificação tem suas particularidades justamente para personalizar o produto de acordo com as necessidades do cliente. 6 A importância do desenvolvimento de sistemas está na eficiência de solucionar problemas de modo automatizado. Isso contribui para a otimização de tempo, serviços, funções e custo dentro da empresa. O termo processo provém da palavra latina procedere, implicando em seguir adiante, dar os próximos passos, avançar. O processo, então, pode ser conceituado como uma série de passos ou etapas lógicas que buscam como objetivo final o alcance de algum fim realizável. Processos de Desenvolvimento de Software, nesse sentido, são as etapas necessárias para que se atinja sua produção ou construção. Os processos de Desenvolvimento de Software são uma área da Engenharia de Softwares, especificamente responsáveis pela forma em que as aplicações são imaginadas, elaboradas, codificadas, testadas, implantadas e mantidas. Essa área do desenvolvimento intelectual humano vem sofrendo diversas atualizações, modificações e melhorias à medida que o modo como operamos e enxergamos os softwares mudam. Computadores, aparatos antes vistos como inalcançáveis, hoje são encontrados em abundância em nossa sociedade, e os mecanismos que os fazem interagir com os humanos, o software, mantém-se acompanhando esse progresso. Softwares, antes destinados a fins mais específicos, muitas vezes acadêmicos ou industriais, tornaram-se, nessa progressão do hardware, mais inclusivos, generalistas e amigáveis aos utilizadores. Sendo assim, a maneira como são desenvolvidos (processo de desenvolvimento), também passou por evoluções. Genericamente, o processo de desenvolvimento do software, baseia-se algumas etapas bem definidas: a) Tomada de Requisitos – Momento em que se captura ideias para uma solução. 7 b) Especificação – Momento em que se transforma ideias em artefatos mais técnicos. c) Desenvolvimento – Momento em que se codifica e cria o software, com base nos artefatos técnicos. d) Testes – Momento em que se testa o produto codificado. e) Entrega ou Implantação - Momento em que se disponibiliza o software para o uso. f) Manutenção – Momento em que se corrige e melhora o software. Fonte: https://artia.com/blog/processo-de-desenvolvimento-de-software/. A evolução constante da engenharia de desenvolvimento de Software, somada às demandas mais recentes de entrega de software, tem provocado mudanças drásticas nas etapas desses processos, necessárias para sua produção. No passado, pensávamos em processos muito mais controlados, acompanhando o próprio funcionamento dos softwares, que possuía etapas isoladas e estáticas em sua construção. O processo baseado no modelo “waterfall”,por exemplo, https://artia.com/blog/processo-de-desenvolvimento-de-software/ 8 dependia do cumprimento de cada uma das etapas para a progressão para a posterior. Modelo que gerou soluções enrijecidas, que não atendem mais os rápidos requisitos atuais. Os processos atuais baseiam-se em metodologias com entregas mais curtas, ciclos de vidas mais interativos, comunicação e atitudes mais transparentes entre os times e gestão mais participativa, valores provindos de métodos e processos mais enxutos, como o lean, kanban e scrum. Entre os processos atuais, mais próximos da realidade de desenvolvimento de software hoje, temos aqueles provenientes de evoluções do RUP (Rational Unified Process) da IBM (e Rational). Entre eles, desenvolvido por um dos autores do RUP, Scott Ambler, temos o AUP (Agile Unified Process), desenvolvido na primeira década dos anos 2000, que tentou aproximar o processo empresarial aos modelos de gestão e desenvolvimentos ágeis. Fases mais curtas, entregas mais interativas, e redução burocrática marcaram esse modelo de processo de desenvolvimento de software. 9 O AUP encerrou seu ciclo de elaboração em 2.006, quando, na percepção do autor, entendeu-se que as mudanças necessitavam ser ainda mais dinâmicas e rápidas, ao mesmo tempo em que não perdesse o espírito empresarial. Em 2.009, Scott Ambler, desenvolvou o Disciplined Agile Delivery (DAD). Um modelo ainda mais coeso, com entregas e ciclos mais interativos e curtos, atendendo também às novas técnicas de fusão de times, como aquelas propostas pelo DevOps. O DAD recebeu incrementos e atualizações até o ano de 2.019, quando foi adquirido pelo Instituto PMI, passando então, a fazer parte de seu portifólio de produtos. Cultura Conceitualmente, entende-se cultura como um conjunto complexo de valores, crenças e ações que definem a forma como uma organização é e conduz seu negócio. A cultura de uma empresa funciona como um guia de comportamento e mentalidade entre os colaboradores, com relação as suas práticas, hábitos, comportamentos, princípios, políticas, crenças, entre outros fatores. 10 A importância da cultura nas organizações está na formação do Capital Humano, de proporção não quantificável, que envolve valores como ética, integridade, positivismo, valorização humana, confiança, engajamento, transparência etc. A cultura, como vetor intelectual da empresa, define-se com o passo do tempo, com práticas vindas de cima para baixo e de dentro para fora. Se constrói por meio da educação formal (cursos, treinamentos, workshops, palestras) e se traduz em resultados reais para a empresa e reconhecimento desta perante a sociedade. DevOps DevOps, termo cunhado na segunda década dos anos 2000, refere-se, primordialmente, à união de silos no desenvolvimento e entrega de software. Trata-se da junção das funções de “Desenvolvimento” e “Operações”, com o intuito de melhorar o ciclo de entregas no processo de desenvolvimento de software. As melhorias significativas nas automações computacionais, com a modernização de elementos da virtualização nos computadores modernos, permitiram se falar em união de pessoas, com uso intensivo de ferramentas e processos. O objetivo desta união, foi o de criar um novo paradigma para equipes de TI, em que a entrega de valor, se tornou preocupação central dos times. 11 A figura demonstra, de forma resumida, as etapas relacionadas ao DevOps. Trata-se de uma cultura colaborativa e incremental. Temos definidas as seguintes etapas: • Planificação – Momento em que se configura o planejamento dos objetivos a serem atingidos ante uma necessidade. O planejamento associado ao processo de uma organização, em constante mudança. Podendo-se, a cada entrega, planejar-se novamente, com base nos resultados obtidos. • Construção – Momento em que se constrói a solução ou o produto. No ciclo de vida de desenvolvimento de software, está relacionado a codificação do software em si. Passada a etapa de planificação, unem-se as equipes, visando materializar os objetivos definidos. É o momento de participação dos times, independentemente de suas áreas de atuação. • Integração Contínua – Nessa fase acontecem as primeiras automatizações para a construção do produto, que serão executadas durante todo o ciclo de desenvolvimento. Os processos executados nessa etapa são basicamente a construção da aplicação (build), execução de testes (test) e preparação de uma versão da aplicação (deploy). Trata-se de um processo incremental e de retroalimentação constante, que facilita encontrar e resolver problemas antecipadamente. • Entrega Contínua – Segunda parte da automação iniciada nos processos anteriores. Essa etapa coleta o artefato compilado e efetua sua publicação em um determinado ambiente e pode executar outros processos, como o de aprovação por responsáveis ou aplicação de validações de segurança ou testes de integração. 12 • Operação – A etapa seguinte do produto é ser publicado em um ambiente, para então ser mantido e monitorado. Originalmente atribuída ao time de operações, com as premissas do DevOps passa a ser distribuído entre vários papéis do time. O objetivo central é manter a aplicação e monitorá-la com o intuito de evoluí-la, respondendo às mudanças dos processos e produto. • Feedback Contínuo – Estão presentes durante todo o ciclo de vida do software, desde a elaboração, construção, testes e manutenção, objetivando encontrar os problemas mais rapidamente, disparando os processos de correção e melhoria. As práticas do DevOps objetivam melhoria na cadeia de desenvolvimento e entrega das aplicações, colaborando para entrega constante de valor. 13 Capítulo 2. Repositórios Conceitualmente, repositórios são meros locais onde se armazenam algo. Uma gaveta, um armário ou um pen-drive, são exemplos de repositórios. Repositórios de Códigos são espaços apropriados para a gestão de códigos fontes de programas de computadores. Tratam-se, literalmente, de software para armazenagem de software. Essas ferramentas de armazenamento funcionam associadas a outras funções, como o versionamento, controle de acessos, lock, merge, comparadores, além de outras que veremos a seguir. Repositórios são parte essencial no processo de desenvolvimento de software moderno. O gerenciamento do código-fonte é sua principal função. Mas além disso, repositórios auxiliam na organização e desenvolvimento de produtos, permitindo acesso múltiplo e simultâneo de diversas equipes de trabalho, contando com rastreabilidade e controle, como benefícios adicionais. Tipos de repositórios Existem dois tipos de repositórios: os centralizados e os descentralizados. • Os centralizados – Trata-se de um tipo de repositório que depende de um servidor central para funcionar. Seu modelo baseia-se em cliente-servidor. Onde as máquinas da equipe do projeto precisam estar conectadas ao servidor central que contém o sistema de controle de versão com os arquivos versionados do projeto. As máquinas dos desenvolvedores contêm apenas uma cópia local dos arquivos e todo o resto fica no servidor central, onde ficarão armazenadas as versões do projeto. Quando o membro do time do projeto termina de executar sua alteração, ele deve fazer um commit/check-out para que sua 14 alteração seja guardada no servidor. O principal ponto falho desse processo é que se o servidor central ficar fora do ar, não é possível trabalhar com o seu time e nem gerar novas versões do seu código-fonte. • Os descentralizados – Os repositórios descentralizados não dependem de um servidor central para guardar todas as versões do código-fonte. Quando um membro do time necessita desenvolver um projeto, primeiro realiza um clone do código fonte, que é copiado dabase de dados completa para máquina dele. Ele permite gerar novas versões e até mesmo olhar e voltar para versões antigas do arquivo que desejar. Dentre as vantagens desse modelo, permite-se que os membros da equipe continuem sendo produtivos mesmo que não estejam conectados no servidor. Operações comuns (por exemplo, commit, ver o histórico, reverter versões) são mais rápidas nesse modelo, pois não precisam se comunicar com os servidores. Permitem que os membros de equipe possam trabalhar em versões de teste que eles não desejam que outros vejam. No entanto, o processo inicial de trazer os arquivos para a máquina do desenvolvedor é mais lento em comparação com o modelo centralizado, pois durante o processo de clonagem, todo o histórico, branches são copiadas para sua estação de trabalho. Além disso, a falta de um controle mais granular nas ações que um usuário pode fazer dentro do sistema de controlador de versão, pode se tornar um empecilho. Branches Qualquer que venha a ser a escolha do repositório, uma técnica fundamental para se garantir a boa construção e gestão dos códigos, é o uso de branches. 15 Branches são ramificações, cópias exatas, feitas no momento de sua criação, de outra branch (que pode, inclusive, ser da própria branch principal). Ao trabalhar com a ramificação, se está atuando em elementos copiados, não imputando qualquer perigo aos dados originais. Este processo, de trabalhar com a cópia, permite-se que ao final (desta sub- etapa), realize-se uma mesclagem, voltando-se ao estado de possuir somente uma linha de construção. Ao se criar um repositório, sempre se criará, pelo menos, uma branch. No Git, esta branch se chamará main ou master. Com muitos membros da equipe trabalhando na mesma linha de desenvolvimento (ou branch), complexidades podem surgir, já que algum trabalho pode ser relativo a funcionalidades novas, outro a correções de problemas e outros ainda a ajustes pontuais. Essa gestão complexa pode ser minimizada com uso de diferentes estratégias de paralelismo de códigos ou uso de branches, como veremos. Cada estratégia de branch vai depender da escolha da organização, mas é recomendável utilizar estratégias de referências, pelo menos, como um ponto de partida. As seguintes estratégias são comumente empregadas: a) trunk based; b) release based; c) feature based; 16 d) story/task based; e) stage based; f) gitflow. Vejamos algumas delas com mais detalhes: a) Trunk Based – Estratégia baseada em utilização de, basicamente, uma branch (a própria main ou trunk). Apesar de ser, às vezes, mais difícil de se gerenciar, torna os processos de mesclagem de código mais fáceis e a integração com ferramentas de CI/CD mais naturais e automatizadas. Não há a proibição de se criar branches, mas, nesse cenário, desencoraja-se o uso de branches de longa duração, pautando-se por aquelas para uma única funcionalidade ou correção pontual. Normalmente utilizada em concomitância com a estratégia “stage based”. Fonte: https://trunkbaseddevelopment.com/. b) Release Based – Estratégia baseada em utilização de releases como métrica de controle de branches. A cada nova release, cria-se uma nova branch, que https://trunkbaseddevelopment.com/ 17 será mesclada com a principal no ponto de lançamento, onde também será transferida para um novo staging. c) Feature based – Estratégia baseada na utilização de features como métrica de controle de branches. Cada nova feature a ser desenvolvida pela aplicação, independentemente do tamanho, irá levar a criação de uma nova branch. Features simultâneas gerarão branches também simultâneas que serão mescladas com a branch original ao seu término. Os releases não possuem relação com a entrega de features, seguindo calendários próprios. d) Story/Task based – Estratégia similar a Feature Based, porém com a utilização de Storys como métrica de controle. Aqui também múltiplas histórias podem gerar múltiplas branches ou pode-se optar por basear-se em Epics, como métrica de controle. Os releases também não possuem relação com a entrega da Story, seguindo seu próprio calendário. e) Stage based – Prática comum na escolha de estratégias de controle de versão. Normalmente, divide-se o código em DEV, HOMOLOG e PROD, e incrementa-se o código por release. Não se trata de uma estratégia a ser utilizada exclusivamente, mas sim combinada com outras estratégias. g) GitFlow – De acordo com o portal da Atlassian: “o Gitflow é um modelo alternativo de ramificação do Git que consiste no uso de ramificações de recursos e várias ramificações primárias. Comparado ao desenvolvimento baseado em troncos, o Gitflow tem mais ramificações de vida longa e commits maiores. Sob esse modelo, os desenvolvedores criam uma ramificação de recurso e retardam o merge com a ramificação de tronco principal até que o recurso esteja completo. Essas ramificações de recursos de longa duração exigem mais colaboração para fazer o merge e têm um 18 risco maior de se desviarem da ramificação do tronco. Elas também podem introduzir atualizações conflitantes1”. Fonte: https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow- workflow. 1 Disponível em: https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow-workflow (acesso em 07/072022) https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow-workflow https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow-workflow https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow-workflow 19 Capítulo 3. Continuous Integration O processo conhecido como Integração Contínua (ou Continuous Integration - CI) refere-se à automação de atividades executadas no momento exatamente posterior ao envio do código fonte ao repositório de códigos, feito pela equipe de construção. A ação de ‘commit’, dispara eventos programáticos, que dão início a automatização de uma sequência de tarefas, em especial de construção e de validação de qualidade, de todos os elementos que foram desenvolvidos. A Integração Contínua surgiu como uma prática recomendada para que os desenvolvedores de software integrem suas alterações no repositório. A demora no tempo de integração pode gerar problemas entre eles e a necessidade de execução de merges complexos. O uso da Integração Contínua complementa, de maneira poderosa, às estratégias de branch e vesionamento, permitindo, por exemplo, boas práticas de revisão, como o code review, realizado por meio do Pull Request. Fonte: https://www.exoscale.com/syslog/what-is-continuous-integration/. https://www.exoscale.com/syslog/what-is-continuous-integration/ 20 Funciona assim: quando uma feature ou correção é finalizada, o desenvolvedor envia um “Pull Request” para o gestor do repositório central. Esse gestor, ou alguém por ele delegado, realiza uma análise do código direcionado para a adequação dos parâmetros de qualidade de desenvolvimento. Esse responsável pode aprovar ou solicitar alterações e, quando aprovados, os códigos são mesclados no branch. O uso da Integração Contínua em conjunto com a ferramenta do code review proporciona uma maneira rápida e constante de informações devolvidas para a equipe de desenvolvimento, evitando ações tardias e proporcionando processos de correções rápidas. Como funciona um CI A Integração Contínua é um processo automatizado. Um conjunto de tarefas que são executadas sequencialmente. A configuração das etapas de automatização vai mudar um pouco de ferramenta para ferramenta. É importante que na escolha das ferramentas opte-se por ferramentas que sejam extensíveis, que permitam a instalação de novas funcionalidades para a criação da Integração. Se possível, deve-se levar em consideração o uso de ferramentasque permitam a criação de scripts para a definição dos subprocessos, de forma que estes possam também serem versionados, já que o processo de automatização também deve possuir governança. Geralmente, scripts são versionados no mesmo repositório da aplicação. Esses subprocessos são também chamados de Pipelines. Uma ferramenta de Integração Contínua altamente customizável, permitirá a inclusão ou exclusão de tarefas, de acordo com a necessidade de cada solução. 21 Figura 1 - Exemplo de um script de CI. Ao ser executado, esse pipeline tentará construir a aplicação levando em consideração algumas tarefas. Havendo problemas, o fluxo será interrompido, conforme é exibido abaixo: Figura 2 - Exemplo de Integração Contínua na Ferramenta Azure DevOps. 22 Nesse exemplo, o processo de Integração foi executado, resultando, porém, em falha no processo de compilação e, portanto, foi interrompido. Após a execução do processo, havendo sucesso ou falha, ações poderão ser disparadas ao time de desenvolvimento, fazendo com que ações corretivas sejam tomadas o mais breve possível. De maneira complementar, outras tarefas importantes podem ser realizadas: 1) Execução de Testes Unitários; 2) Análise Estática do Código. Esses passos adicionais complementam a automatização proporcionada pela Integração Contínua, tornando o processo de resposta, quanto ao código desenvolvido, mais eficiente, rápido e confiável. Ferramentas de Integração Contínua Principais ferramentas atuais (2022) para se trabalhar com CI: a) Jenkins; b) Circle CI; c) Team City; d) Bamboo; e) GitLab CI; f) Buddy; g) Travis CI; h) Codeship; i) Semaphore; j) Azure Pipelines; 23 k) Git Actions. A ferramenta mais adequada vai depender da necessidade de cada organização. Neste curso utilizaremos a plataforma Azure da Microsoft, por conter os principais itens relacionados ao DevOps. Por meio dessa ferramenta, faremos: ‒ Gestão e Controle de Repositórios; ‒ Implementação de Pipelines CI/CD; ‒ Garantia de qualidade de software; e ‒ Compartilhamento de Recursos. 24 Capítulo 4. Continuous Delivery (CD) O Continuous Delivery (ou Entrega Contínua) faz parte da esteira de desenvolvimento de software e é complementar ao CI (Integração Contínua). Sua principal função é a entrega dos artefatos de software em seus respectivos ambientes. Nesses termos, tão logo ocorre a geração de um artefato, através do processo de CI, é então disparada uma trigger para a promoção desse artefato em ambientes distintos. O processo pode modificar-se da mera entrega para um verdadeiro “deploy”, tornando-se, nesse caso, um processo de “Continuous Deploy”. Essa mudança de processo para o modo de entrega, está associado à publicação de versões da aplicação em todos os ambientes, incluindo o ambiente de produção, automaticamente. Isso exige um nível muito alto de maturidade do time e um processo bastante consistente e seguro, já que, uma vez submetido o código do desenvolvedor, imediatamente poderá estar pronto para o usuário final. Casos como esses são mais raros de se usar na indústria, já que os riscos são altos, mesmo assim podem ser utilizados e exigem uma cobertura mais ampla dos testes, além de análise estática do código. Bom monitoramento e um SLA agressivo para atendimento de demandas urgentes também é requerido para casos como esses. Dev Homolog Prod 25 No modo delivery (mais comum) mantém-se as características acima citadas, mas o processo é feito de forma mais acompanhada e assistida, dependendo de mais aprovadores, antes que qualquer versão seja submetida para a produção. Estratégias de Deploy Diferentes métodos podem ser utilizados para que a entrega do aplicativo em produção aconteça de maneira menos abruptas para o usuário. Além da tradicional virada de chave, onde o antigo é imediatamente substituído pelo novo, outros processos de deploy podem ser aplicados, vejamos: a) A/B Test - Também conhecido como Teste de Balde ou Teste de Split-Run. Trata-se de uma abreviação para um experimento controlado e randomizado, no qual duas amostras (A e B) de uma única variável vetorial são comparadas. Esses valores são semelhantes, exceto por uma variação que pode afetar o comportamento de um usuário. Os ‘A/B tests’ são considerados a forma mais simples de experimento controlado. No entanto, adicionando mais variantes ao teste, sua complexidade cresce. Os Testes A/B são úteis para entender o engajamento do usuário e a satisfação de recursos on-line, como um novo recurso ou produto. Grandes sites de mídia social como LinkedIn, Facebook e Instagram usam testes A/B para tornar as experiências do usuário mais bem sucedidas e como uma maneira de agilizar seus serviços. b) Blue-Green - O deploy blue-green consiste em ter dois ambientes idênticos (também chamado de mirror) e na frente desses ambientes existe um load balancer que permite o direcionamento do tráfego para o ambiente desejado. Pode-se subir a nova versão da aplicação em produção e somente a versão atual (blue) receberá as requisições. 26 Assim que finalizarem-se todos os testes na nova versão (green), troca-se a chave do load balancer e as novas requisições já irão apontar para a nova versão do código. c) Canary - Consiste em colocar a nova versão em produção, porém, apenas para uma parcela dos usuários. Ou seja, coloca-se a nova versão e configura- se para que apenas uma porcentagem de usuários passe por esse novo serviço. Essa configuração na maioria das ferramentas pode ser feita através de feature toggle, isso significa que podemos liberar o novo serviço apenas para usuários do sexo feminino, ou para usuários com mais de 30 anos, por exemplo. A grande vantagem dessa abordagem é poder ir testando a nova versão aos poucos com usuários reais e acompanhar os logs para ver se tudo está correndo bem, assim liberando a nova versão gradualmente. d) Rolling - consiste em ir subindo os serviços com a nova versão do código, substituindo a versão antiga. A versão antiga só é “desligada” quando a nova versão está pronta para ser executada, ou seja, em algum momento dessa estratégia de deploy vamos ter N+1 instâncias do mesmo serviço sendo executada. Essa são apenas algumas das estratégias existentes. O importante é planejar corretamente o envio de atualizações em outros ambientes de maneira segura. Principais Ferramentas para CD Ferramentas que fazem o CI normalmente e complementarmente, realizam o CD, neste sentido, algumas ferramentas a seguir serão as mesmas do CI: a) Jenkins; 27 b) Circle CI; c) Team City; d) GitLab CI; e) Buddy; f) Codeship; g) Semaphore; h) Azure Pipelines; i) Git Actions; j) Harness. 28 Capítulo 5. Qualidade Em qualquer produto ou entrega, prezar pelo processo de qualidade, é fundamental. Mas como podemos, afinal, identificar se um programa ou artefato de código está suficientemente qualificado para se provisionar em ambientes produtivos, sem passar por revisões manuais? Atender a tal requisito, requer a criação de mecanismos de verificação e checagem do trabalho publicado. Envolvendo, primordialmente, testes unitários, testes integrados, testes de coerência de negócio, qualidade de código, testes de segurança e testes de carga. Vejamos alguns processos a seguir. Testes Unitários Testes que objetivam verificar ou validar um determinado artefato de código, comparando-o com métricas de aceitação e integridade, conforme estabelecido nas definições de regras de negócio. Vejamos um exemplo para melhor entendimento. Suponha que tenhamos a seguinte função em um de nossos artefatos: Trata-se de uma função que deveria retornar a soma de dois elementos (numéricos). Nessesentido, podemos criar uma função unitária que teste este código: A função “expect” faz parte da biblioteca de testes. Ela, efetivamente, “espera” que o resultado alcançado seja igual àquele manualmente inserido. Caso o valor seja 29 diferente do esperado, a função levanta uma exceção, informando que houve uma falha no programa testado. Essa prática é importante, não somente para se criar uma etapa a mais no desenvolvimento da solução, mas para contribuir com uma melhor escrita do código e prover segurança ao garantir o que foi desenvolvido. Quanto mais testes unitários aplicados a todos os casos possíveis desenvolvidos, maior qualidade pode-se esperar de um software ou aplicação. Testes unitários são poderosos, mas sozinhos nem sempre garantem que o software responda da maneira esperada. Por também ser um elemento de código, um erro na implementação de algum teste pode pôr em risco uma funcionalidade inteira da aplicação. E há ainda casos, onde testes unitários são impossíveis. Por exemplo, quando um método, objeto ou componente não puder ser testado de maneira isolada, já que depende de outro para realizar sua ação. Nesses casos, são necessários testes integrados, que nem sempre são passíveis de automação. Ainda assim, Testes Unitários são extremamente importantes para o bom funcionamento do software e não devem ser relegados ao final do desenvolvimento (como se fazia utilizar em modelos como waterfall). Vale lembrar que os Testes Unitários não servem primariamente para se verificar se uma função está ou não funcionando, mas sim para garantir que a aplicação continua funcional após alteração em sua base de código. Metodologias mais atuais, promovem que os testes unitários sejam construídos juntamente com o software, havendo inclusive técnicas para se desenvolver softwares através de testes (TDD, que já foi explorado em módulos passados e serão revisados a seguir). 30 TDD – Test Driven Development TDD significa Test Driven Development (ou Desenvolvimento Orientado por Testes). Trata-se de uma prática de desenvolvimento de software onde a codificação das funcionalidades começa a partir da escrita de testes unitários. Essa técnica foi criada por Kent Beck e é um dos pilares do XP (Extreme Programming). O TDD consiste em um ciclo apelidado de RED, GREEN, REFACTOR. Que funciona da seguinte maneira: a) Escrevemos um teste para uma funcionalidade a qual queremos implementar. Ao executar esse teste, ele deve falhar, pois ainda não temos a implementação e assim passamos pelo passo RED. b) Após o nosso teste falhar implementamos a funcionalidade mínima e executamos o nosso teste novamente, dessa vez o teste passa com sucesso, com isso passamos pelo passo GREEN do TDD. c) Com os testes funcionando chegamos ao passo REFACTOR. Que, como o nome diz, faz-se necessário refatorar o código procurando pontos de melhora e aplica-se boas práticas de programação. Esse ciclo pode se repetir quantas vezes o engenheiro julgar necessário e a cada nova implementação é interessante que todos os testes que já estão em funcionamento sejam executados, assim é validado que todos os testes continuam funcionando e que as novas implementações não impactaram nas funcionalidades já desenvolvidas. 31 Fonte: https://res.cloudinary.com/. Os principais benefícios do TDD são: 1) Maior cobertura de testes unitários; 2) Testes são executados com maior frequência; 3) O código se torna mais limpo. Além disso, deixar para realizar os testes de funcionalidades para depois pode acabar não acontecendo como deveria. Testes tardios aumentam o risco de realizar-se alterações no código por conta de testes que não passaram, ou deixar de fazê-las por testes que não foram feitos, aumentando a possibilidade de introduzirmos problemas causados com essas modificações. Vejamos um exemplo de TDD: 1) Comecemos escrevendo o teste: https://res.cloudinary.com/ 32 O código nem sequer compila, já que não temos ainda a classe Area. Vamos criá-la: O teste, agora executado, retorna RED, já que o cálculo da área não foi implementado. Vamos fazê-lo: 33 O resultado continua sendo RED, já que o valor que informamos para assegurar- se está equivocado (afinal, 5 x 4 = 20, que dividido por 2 deverá ser 10), então alteramos o assert: Temos, então, um passo GREEN. Podemos passar para a refatoração. Esse procedimento, repete-se a cada interação até se esgotarem todos os casos de testes. Essa característica da implementação com testes provê rastreabilidade e consistência no ciclo de desenvolvimento do software. Análise estatística de códigos Uma das práticas mais interessantes da atualidade, automatizada quanto a filtragem de codificações mal escritas, é a análise estática de códigos. Trata-se de uma verificação, ferramental, realizada antes que o código seja disponibilizado em ambientes produtivos. Esta verificação, realiza-se por meio de ferramentas, que, munidas de um conjunto de regras pré-estabelecidas, é capaz de atestar se algum programa foi ou não escrito com os cuidados tidos como exigíveis pela equipe definidora. Uma destas ferramentas, é o SONARQUBE. Com a principal característica de analisar o código desenvolvido, tanto durante o processo de CI quanto durante a codificação. As análises proferidas por essas aplicações geram relatórios com informações sobre falhas de implementação, vulnerabilidades de segurança, débitos técnicos, cobertura, aplicação de padrões e outros. 34 O SonarQube é uma plataforma de código aberto e atua na inspeção contínua da qualidade do código, realizando revisões automáticas com análise estática de código para detectar problemas, suporta código em 17 linguagens de programação. Oferece relatórios sobre código duplicado, padrões de codificação, testes unitários, cobertura de código, complexidade de código, comentários, bugs e recomendações de segurança. Ainda, trata-se de uma ferramenta extensível, que permite a inclusão e definição de regras, seguindo padrões definidos pela corporação. Neste sentido, pode-se escolher os tipos de análises que fazem mais sentido ao modo de desenvolvimento de cada empresa. Após as análises um relatório é gerado contendo uma métrica relacionada às definições de cada projeto. Uma métrica se assemelha a uma nota, que quando aplicada a uma integração contínua (CI) pode ou não aprovar o processo. Figura 3 - Ciclo de utilização do SonarQube. Fonte: https://docs.sonarqube.org/latest/. Um típico processo de desenvolvimento com o SonarQube: Os engenheiros desenvolvem e mesclam código a partir de um IDE e fazem check-in em seu código para sua plataforma de DevOps (repositório de códigos). A https://docs.sonarqube.org/latest/ 35 ferramenta de integração contínua (CI) verifica, constrói e executa testes de unidade, e um scanner SonarQube integrado analisa os resultados. O scanner publica os resultados para o servidor SonarQube, que fornece feedback aos desenvolvedores através da interface SonarQube, ou por e-mail, ou por notificações na própria IDE, uma vez aprovado, remete a solicitações de “Pull Request” ou merge. Segurança Dentre as questões mais relevantes concernentes à estabilidade e confiabilidade dos softwares estão suas constrições de segurança. Ou seja, os métodos e ferramentas que foram implementados de tal modo a proteger a aplicação contra vulnerabilidades e riscos. Todos os artefatos de software devem passar por rigorosos testes, em especial naquilo que se refere a segurança, obedecendo ordem de importância, em decorrência de sua criticidade ou exposição. Durante os processos de integração do DevOps, tanto nas etapas de Integração Contínua quanto na de Entrega Contínua podemos especificar testes para verificar potenciaisvulnerabilidades. Times de segurança podem unir-se aos de Desenvolvimento e Operações, formando um novo time, conhecido como DevSecOps. Ferramentas podem realizar um bom trabalho nesse sentido, podemos citar: a) CheckMarx; b) Owasp Zap; c) Acunetix; d) IriusRisk; e) Codacy; f) Aqua Security. 36 Ferramentas de segurança normalmente são disponibilizadas em versões pagas, existindo poucas opções realmente livres (free). Algumas ferramentas oferecem níveis de utilização gratuitos, mas limitam suas capacidades, destinando as principais funcionalidades às versões pagas. Uma ferramenta gratuita e de código aberto é o Owasp Zap (https://www.zaproxy.org/). Trata-se de um scanner para os modos passivos e ativos de HTTP e Websockets (esse último somente escaneamento passivo) e pode ser integrado em algumas esteiras CI/CD, facilitando a entrega da aplicação. Vulnerabilidade de Pacotes Outra prática importante aplica-se ao uso de ferramentas de checagem de vulnerabilidades em libraries e pacotes embarcados em nossa aplicação. Afinal, utilizar bibliotecas de terceiros é prática comum em nosso trabalho. Uma sugestão desse tipo de ferramentas é o Mend Bolt, que possui uma versão livre (free) facilmente integrável em esteiras CI/CD e IDEs de mercado. https://www.zaproxy.org/ 37 O Mend Bolt permite você trabalhar com seus fluxos existentes, sejam em repositórios públicos ou privados. Verificações desse tipo são importantes para garantir, automaticamente, a saúde e segurança das dependências dos projetos. Como o Mend Bolt funciona? Ao instalar o plugin em seu repositório, o aplicativo fornece uma interface para verificação das vulnerabilidades, apresentando um relatório ao final do processo de escaneamento. Pirâmide de testes Proposta por Martin Fowler, a pirâmide de testes é um direcionador de qualidade para projetos de software, definindo contextos sobre o que e quando testar. Fonte: https://martinfowler.com/bliki/TestPyramid.html. https://martinfowler.com/bliki/TestPyramid.html 38 Na Pirâmide de Testes vemos mais testes unitários na base, por serem mais rápidos de desenvolver e rodar. A quantidade de testes diminui conforme vamos subindo na pirâmide, pois aumentam-se os esforços necessários e o tempo de execução de cada tipo. Para fazer um teste de UI, por exemplo, são necessários muito mais esforços e tempo para rodar e ter algum feedback. Por isso, segundo a pirâmide, devemos ter menos testes desse tipo. Os níveis mais altos da pirâmide, são testes mais complexos de se criar, consequentemente, tem-se um aumento dos custos, tanto de dinheiro (esforço) quanto de tempo. A pirâmide de testes é um guia para as equipes, no que diz respeito a qualidade. Ela ajuda a definir os momentos corretos de execução dos testes, inclusive nos processos automatizados. Importante ressaltar que o autor da ideia, Martin Fowler, disponibiliza em seu wiki uma visão pessoal do correto modelo de implementação. Material que pode ser consultado aqui: https://martinfowler.com/bliki/TestPyramid.html. https://martinfowler.com/bliki/TestPyramid.html 39 Capítulo 6. Gestão de Mudanças Gestão de mudanças é uma área da tecnologia da informação, que visa prover as equipes, de ferramentas necessárias para transições naturais sofridas pelos projetos. Em mercados demandantes e voláteis, mudanças em longos projetos são uma realidade constante e imparável. Entender como se planejar, gerir e conduzir essas mudanças, podem levar ou não ao sucesso do projeto. Sendo prática inerente ao mercado de desenvolvimento de software. Além das mudanças normais, que decorrem de fatores externos, há que se pensar ainda, naquelas surgida pelo próprio feedback e aprendizado do desenvolvimento, naquele processo natural de melhoria contínua, que se torna fundamental nos dias de hoje. Entender e gerir as mudanças é peça chave para o sucesso dos projetos, e algumas ferramentas e mecanismos podem facilitar este grandioso trabalho. Configurações Um projeto, pode ser mais ou menos customizável, a depender de seu número de configurações parametrizadas. Configurações essas, que, muitas vezes podem (e devem) ser gerenciadas fora da aplicação, de tal forma que o desacoplamento permita ainda mais dinamismo. Boas práticas de desenvolvimento, sugerem que as configurações, por variarem de solução para solução, devem ser geridas por ferramentas externas, para que sua manipulação ocorra segura e eficazmente. 40 Se uma aplicação possui contextos, ideal que sejam construídos em bases seguras, evitando uso de “hardcodes” dentro do código fonte da própria aplicação. Por exemplo, credenciais de conexões com bancos de dados, chaves (tokens) de autorização de serviços, acessos para ferramentas integradas, todos devem ser mantidos o mais escondido possível, preferencialmente, pelo uso de “secrets”. Ferramentas deste tipo, não apenas tornam indireto o acesso ao ferramental acessório, mas facilitam o processo de manutenção, mudança e até de governança. Em códigos grandes ou pequenos, uma boa prática é fazer uso de um serviço de Configuração de Servidor (Config Server), como o AWS App Config, o Azure App Configuration ou o Spring Cloud Config. Um serviço de ‘Config Server’ armazena a configuração de todos os seus aplicativos em um local universal e hospedado. Ele permite a gestão de configurações de forma mais eficaz e confiável, em tempo real, sem afetar os aplicativos, evitando reimplantações demoradas. Como as configurações são recuperadas dinamicamente, dependendo do contexto, aplicações de transformação de scripts no momento de deploy passam a ser desnecessárias. 41 Fonte: https://azure.microsoft.com/en-us/services/app-configuration/. Aplicações como essas funcionam como pontes, armazenando remotamente informações de configuração, que são entregues mediantes chaves contextualizadas. Esse tipo de implementação reduz erros no processo de deploy e agiliza a implantação, ao mesmo tempo em que mantém informações de configuração reservadas e ocultas da aplicação. Feature Toggles Uma outra técnica poderosa, mas complexa, são os ‘Feature Toggles’ ou ‘Feature Flags’. E o que são estas técnicas? São comportamentos implementados em nossas soluções, que habilitam ou desabilitam funcionalidades por meio de controles de liga/desliga. Essa técnica permite, dentre outras coisas, que seja possível ativar ou desativar uma funcionalidade em produção em tempo real sem fazer um novo deploy. Além disso, é possível, por meio https://azure.microsoft.com/en-us/services/app-configuration/ 42 dela, disponibilizar uma funcionalidade para uma parte dos seus clientes fazerem uma experimentação. Outra característica é a de desativar uma funcionalidade, sem precisar fazer rollback do código ou ativar uma feature somente para testes, definindo quem vai ter ou não acesso a essa funcionalidade. Trata-se de uma estratégia muitas vezes utilizada para apoio do desenvolvimento com a estratégia “Trunk based” para o branch, mas há muito mais. E como isso funciona afinal? Vejamos o exemplo de código abaixo: Notem que no início de nossos testes incluímos uma opção para a opção “one.click.checkout” estar habilitada. Nesse caso disponibilizaremos ao nosso cliente aquele código, caso contrário não. Depois, em um painel agregado, podemos controlar quais “flags” serão liberadas para cada versão da aplicação: 43 Podemos utilizar esse recurso de forma sutil em nossos aplicativos, por exemplo, configurar o download instantâneo de vídeos, sons e imagens, automaticamente ao se abrir feeds ou receber mensagens. Essa técnica pode ser utilizada para aplicação de testes A/B, promovendo mudanças nas aplicações, como por exemplo, habilitandorecursos para alguns usuários e não para outros, ou provendo recursos específicos nas aplicações. Apesar do aumento de complexidade para a implementação, o uso de ‘Feature Toggles’ pode aumentar a autonomia para a equipe de negócios, já que estes podem, no âmbito do negócio, habilitar ou desabilitar alguma função do sistema, testando as melhores abordagens ou aquelas que geram mais resultados. Wiki Processos bem documentados, fazem parte de projetos de sucesso. Bem documentar, não significa burocratizar o desenvolvimento do software, mas prover as 44 equipes e interessados, informações relevantes, que estabeleçam os patamares organizacionais, aceitos como conformidade, alinhando todos os membros do time. Artefatos críticos, que envolva decisões, padrões, riscos e problemas, devem ser documentados. Neste sentido, um ambiente bem munido, deve ater-se, minimamente a: a) Documentação arquitetural – Documento com o desenho arquitetural da solução, que exiba as decisões mais importantes, os componentes que comporão a aplicação, os detalhes de ambientes, os elementos responsáveis, métricas de controle de risco, métricas gerais do projeto e conjunto de ferramentas que atenderão a solução. Este conjunto exemplificativo de elementos entregáveis, pode ainda apresentar mais níveis de detalhamento, por exemplo, direcionando a outros documentos mais específicos e especializados. b) Gestão de código - Qual é a estratégia de branch será definida para o projeto, qual o padrão de nomenclatura e definição de classes, funções e variáveis, como será feito o versionamento, como serão os comentários, quando realizar os pull requests, quem serão os code reviewers etc. c) Diretrizes de revisão de código - Todo o time é responsável por fazer as revisões de códigos. Mas deve-se prever um documento que especifique o que é esperado para uma revisão, de acordo com o contexto. d) Processos – Dentro do possível, apresentar os desenhos dos processos (as is e should be), contextualizando cada projeto. Ainda, definidos os planejamentos, que ritos serão utilizados, os modelos de gestão, os tipos de itens de trabalho (Épicos, Sprints, Tarefas, etc). 45 Pode-se utilizar modelos para prover essas informações, mas as referências devem estar bem estabelecidas, e as peculiaridades devem estar claras, atendendo as nuances de cada projeto. e) Glossário e padrões de nomenclatura – Deve-se claramente definir o padrão de linguagem e expressões que farão parte do projeto. Sejam conceitos de negócio, regras jurídicas, comportamentais, e mesmo técnicas. f) Informações adicionais do projeto – Por fim, ainda que esta não seja uma lista exaustiva, definir as demais informações do projeto, dentre aquelas que ainda não tenham sido tratadas, como: finalidades do projeto? Problemas que ele resolve; Membros e responsáveis em cada time, entre outras. Um Wiki é uma ferramenta interessante, através da qual essas informações poderão estar disponíveis para todos os envolvidos, de forma ágil e atualizada. Ferramentas como o Azure DevOps possuem o Wiki disponível, mas outras poderão ser utilizadas, como: 1) Wiki.js; 2) DokuWiki; 3) MediaWiki; 4) XWiki; 5) BookStack; 6) Outline. 46 Capítulo 7. Monitoramento Para que um ciclo de vida de um software esteja completo e atinja altas taxas de disponibilidade é necessário fazer um monitoramento constante da aplicação, para que eventuais problemas possam ser corrigidos no menor tempo possível e, preferencialmente, de maneira preventiva. Monitorar permite: a) Reduzir o tempo de detecção de anomalias (TDD); b) Reduzir o tempo de mitigação dos problemas (TMP); c) Reduzir o tempo necessário para a resolução dos problemas (TRP). Como segundo objetivo pode-se destacar a rastreabilidade de uso, como validação de resultados. Nesse caso utiliza-se do monitoramento como uma oportunidade de validação e experimentação de respostas que apoiam determinadas hipóteses. Hipóteses ou testes falhos, no que diga respeito ao uso realizado pelo cliente, podem ser rapidamente alternados quando percebidos com antecipação. Do contrário, hipóteses confirmadas podem reafirmar o bom caminho da solução direcionando esforços dos times de desenvolvimento. Esse tipo de análise vem sendo atribuído pela literatura como verdadeira arte de telemetria (processo ou técnica de obtenção, processamento e transmissão de dados a longa distância). Diversas ferramentas podem ser utilizadas neste sentido, provendo os desenvolvedores de informações ricas e detalhadas, muitas vezes exibidas em gráficos consolidados ou painéis de informações. 47 Um monitoramento controlado e profundo, permite que as equipes de desenvolvimento façam entregas mais rápidas e eficazes, já que facilitam a obtenção de informações vindas da própria aplicação e desta forma, que evidenciem soluções que melhor atendam e retenham seus clientes. Trata-se de um dos pilares do DevOps e não deve ser negligenciado por equipes que desejam respostas sérias e direcionadas com respeito ao rumo das aplicações. Ferramentas que fazem cumprem bem esse papel: 1) Azure Monitor; 2) AWS CloudWatch; 3) Dynatrace; 4) DataDog; 5) Splunk. A importância de monitorar soluções de software é fortemente recomendado, não devendo, entretanto, restringir-se tão somente a coleta de dados, mas em especial para a validação dos problemas de uma aplicação. O monitoramento deve ser feito também, como aprendizado, em especial quanto ao uso da aplicação por parte dos usuários, entendendo seus comportamentos e verificando como a aplicação se comporta em cada cenário. Logs Logs são arquivos de texto gerados por um software para descrever eventos sobre o seu funcionamento, utilização por usuários ou interação com outros sistemas. Normalmente são utilizados para descrever o processo de registro sobre eventos relevantes em uma aplicação. Esses registros podem ser utilizados para restabelecer o 48 estado original de um sistema ou para que um administrador conheça o seu comportamento no passado. Um arquivo de log pode ser utilizado para auditoria, além de diagnóstico de problemas em sistemas computacionais. Mas o arquivo de log, por si só, possui pouco valor quando deixado de lado. E a análise de logs pode se tornar uma tarefa maçante ou tediosa quando não realizada por ferramentas que facilitem sua visualização e gestão. Nesse sentido podemos indicar diversas ferramentas dedicadas a gestão de logs: 1) SolarWinds Log Analyzer; 2) Elastic Stack; 3) Datadog; 4) Splunk; 5) LogDNA; 6) Graylog; 7) Sentry. Dentre as ferramentas recomendadas, um destaque para a DataDog, um software de monitoramento de servidores, bancos de dados, ferramentas e serviços via plataforma de análise de dados baseada em SaaS para aplicativos que buscam escalar na nuvem. Pontos fortes: a) É possível usá-la para rastrear virtualmente “qualquer coisa”; 49 b) Permite a configuração de alertas, advertências e reduzir drasticamente os falsos positivos; c) Runbooks fornecem orientação aos membros das equipes DevOps sobre como agir em relação aos alertas; d) Possui recursos poderosos de análise de dados; e) Possui uma API REST simples que possibilita integração com (virtualmente) qualquer aplicativo; f) Permite a criação de uma fonte centralizada de dados; g) Possui uma boa documentação de API. Por meio de seu painel de informações (dashboard), pode-se criar alertas de bugs ou problemas, informando, quando necessário, times de desenvolvimento, correção ou manutenção. Alertas Os alertas, como citado anteriormente, servem para munir os desenvolvedores de informações emergenciais. Análises em tempo real, podem provocar “gatilhos” que iniciem processos de avisos rápidos e eficientes.Serviços de alertas estão disponíveis amplamente em plataformas de computação em nuvem, possuindo ampla gama de ferramentas e integrações que facilitarão o time de desenvolvimento a tomar ações com base em eventos. Alertas podem disparar envio de mensagens, através de e-mail ou de outras plataformas de comunicação, podem mudar configurações de máquinas ou até provisionar novos ambientes, se este for o caso mais adequado. 50 O Amazon Simple Notification Service é um exemplo desse serviço em nuvem. O SNS é um serviço de mensagens totalmente gerenciado para a comunicação de aplicação para aplicação (A2A) e de aplicação para pessoa (A2P). Sua funcionalidade publish/subscribe fornece tópicos para sistemas de mensagens de alta taxa de transferência baseados em push e de muitos para muitos entre sistemas distribuídos, microsserviços e aplicações sem servidor orientadas por eventos. Os tópicos do SNS permitem que os sistemas possam repassar mensagens para muitos sistemas de assinantes, incluindo filas do Amazon SQS, funções do AWS Lambda e endpoints HTTPS e o Amazon Kinesis Data Firehose para processamento paralelo. Como conclusão deste capítulo podemos observar que o uso de monitoramento resulta em feedback constante no ciclo de desenvolvimento. Cada processo de desenvolvimento envolve fatores importantes que devem ser monitorados de forma a manter a saúde das soluções e gerar dados constantes e seguros para tomadas de decisões. 51 Referências .NET Architecture Guide. Microsoft, c2022. Disponível em: <https://dotnet.microsoft.com/learn/dotnet/architecture-guides>. Acesso em: 06 jul. 2022. DOCUMENTAÇÃO técnica da Microsoft. Microsoft Docs, c2022. Disponível em: <https://docs.microsoft.com>. Acesso em: 06 jul. 2022. HODGSON, Pete. Feature Toggles (aka Feature Flags). martinFowler.com, 9 out. 2017. Disponível em: <https://martinfowler.com/articles/feature-toggles.html>. Acesso em: 06 jul. 2022. VOCKE, Ham. The pratical Test Pyramid. martinFowler.com, 26 fev. 2018. Disponível em: <https://martinfowler.com/articles/practical-test-pyramid.html>. Acesso em: 06 jul. 2022. WHAT is DevOps?. Microsoft Docs, 14 jul. 2022. Disponível em: <https://docs.microsoft.com/pt-br/azure/devops/learn/what-is-devops>. Acesso em: 06 jul. 2022. https://dotnet.microsoft.com/learn/dotnet/architecture-guides https://docs.microsoft.com/ https://martinfowler.com/articles/feature-toggles.html https://martinfowler.com/articles/practical-test-pyramid.html?ref=hackernoon.com https://martinfowler.com/articles/practical-test-pyramid.html?ref=hackernoon.com https://docs.microsoft.com/pt-br/azure/devops/learn/what-is-devops