Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Critérios de Aceite de Código Para Times Internos ou Terceirizados

280 views

Published on

Esse documento exemplifica critérios objetivos de aceite de código desenvolvido por terceirizadas, devendo também ser seguido por times internos para que o nível de qualidade seja análogo e os resultados de produtividade e qualidade das entregas possam ser analisados.

Published in: Software
  • Be the first to comment

Critérios de Aceite de Código Para Times Internos ou Terceirizados

  1. 1. Critérios de Aceite de Código Para Times Internos ou Terceirizados Marcio Marchini 2015/01/19 Introdução ..................................................................................................................................................... 1 1 - Requisitos Funcionais ........................................................................................................................ 1 1.1 - Formato: User Stories e BDD ................................................................................................. 2 1.2 - Serem Executáveis: Imprescindível para dar o Aceite (DONE) .............................. 2 REST ....................................................................................................................................................... 3 GUI WEB ............................................................................................................................................... 3 DONE ...................................................................................................................................................... 4 2 - Requisitos Não Funcionais .............................................................................................................. 4 2.1 - Performance .................................................................................................................................. 5 2.2 - Engenharia de Software ........................................................................................................... 5 2.3 - Dashboards ................................................................................................................................. 15 2.4 - Deployment ................................................................................................................................ 16 2.5 - Evolução de Database Schema: Migrations .................................................................. 16 2.6 – Escalabilidade ........................................................................................................................... 17 2.7 – Segurança ................................................................................................................................... 17 Introdução Ajudando clientes percebemos um tema constante: alta viscosidade do software, tornando excepcionalmente caro adicionar novas funcionalidades. O impacto de qualquer mudança é gigante e quebra diversos pontos no sistema. E como se chegou a esse ponto? “É de pequenino que se torce o pepino”. Características internas do software simplesmente não foram controladas (faltou gestão técnica para tal). Esse documento exemplifica critérios objetivos de aceite de código desenvolvido por terceirizadas, devendo também ser seguido por times internos para que o nível de qualidade seja análogo e os resultados de produtividade e qualidade das entregas possam ser analisados. 1 - Requisitos Funcionais Em demandas solicitadas ao time (através de um mecanismo qualquer de backlog), serão explicitados ao time os requisitos de funcionalidades observáveis. Trataremos do formato e de detalhes de ferramenta.
  2. 2. 1.1 - Formato: User Stories e BDD Para requisitos funcionais deverá ser usada a abordagem ágil de User Stories do XP (Extreme Programming). Mais especificamente, no seguinte formato canônico. Como <papel>, devo poder <ação> para que <valor/motivação da funcionalidade>. Um exemplo de simples compreensão escrito no FitNesse: Tais User Stories terão cada qual uma série de critérios de aceite com suas nuances de regras de negócio1. Tais critérios serão escritos no formato Given-When-Then do BDD: DADO QUE <pré-condição>, QUANDO < ação> ENTÃO <pós-condição> Eis um exemplo simples no caso do Login: Note que é uma especificação-cenário, com dados exemplares (concretos e não abstratos – usuário tal-e-tal, etc). É a abordagem de Specification by Example. 1.2 - Serem Executáveis: Imprescindível para dar o Aceite (DONE) O conceito de DONE muitas vezes tem compreensão distinta entre o cliente e o fornecedor da implementação. Visando eliminar esse tipo de problema, os requisitos 1 O que acontece se o login for válido? O que acontece se o login for invalido? O que
  3. 3. ágeis serão executáveis via ferramenta FitNesse ou similares2. Através da utilização das fixtures como Xebium e RestFixture, tanto a camada REST quanto a camada GUI poderão ser validadas contra a User Story e os GIVEN-WHEN-THEN. REST Exemplo no FitNesse de User Story sendo executada contra a camada REST: GUI WEB Exemplo da User Story sendo executada contra a camada de UI WEB: 2 Robot Framework, por exemplo.
  4. 4. Note que mesmo cenários específicos de uma camada apenas – GUI WEB – podem ser descritos com a mesma abordagem (no caso, autocomplete de um campo da tela): DONE Como vimos anteriormente, o conceito de DONE de um requisito é quando existe uma spec para ele e esta passa (verde) quando executada no FitNesse. O objetivo é ter essas validações ocorrerem de forma automática no build. Não há mais ambiguidades. 2 - Requisitos Não Funcionais Há vários requisitos não funcionais (não são features de funcionalidades direta aos usuários) mas que beneficiam os usuários direta ou indiretamente. Por exemplo, a escalabilidade de um servidor/serviço pode ser avaliada dessa forma.
  5. 5. 2.1 - Performance No caso de necessidade de escalabilidade e performance, esse requisito deve ser escrito também em formato de User Story e GIVEN-WHEN-THEN. Exemplo: Como patrocinador do projeto, devo poder hospedar milhares de usuários simultaneamente sem degradação de performance para que eu maximize a utilização de recursos e o lucro. DADO QUE há 1000 usuários logados simultaneamente comigo QUANDO solicito os dados do processo xyz123 ENTÃO a resposta chega em menos que 500 ms em 95% dos casos Esse GIVEN-WHEN-THEN torna-se então o critério de aceite da User Story, que foi escrita sem parâmetros concretos (Quão rápido? Quantos milissegundos?). Temos mais uma nuance de regra de negócio. Nesse caso, um requisito de performance, onde se descreve o percentil (95%) e o indicador de resposta (500ms por request). É responsabilidade dos Product Owners (usualmente da contratante) explicitar esses requisitos não funcionais de forma precisa, sem ambiguidades. 2.2 - Engenharia de Software Sabe-se que cerca de 44% do esforço de software vai em adaptá-lo para novas funcionalidades (67% de 67%):
  6. 6. O custo dessa adaptabilidade é ligado diretamente às práticas de engenharia de software que são utilizadas. Existência de testes automatizados, tamanho das classes ou dos métodos etc todas contribuem para uma adaptabilidade mais cara ou mais barata. Visando baratear o TCO (Total Cost of Ownership) desse código fonte, faz-se necessária a utilização de boas práticas de software de maneira mensurável, idealmente numa ferramenta de build como Jenkins, usualmente combinado com SONAR. Microarquitetura e métricas A um nível micro, de classes e métodos, há um conjunto de métricas que devem ser observadas. Dentre as principais destacamos algumas, que são mostradas em ferramentas diversas como o SourceMonitor: As métricas acima devem permanecer dentro da área verde (uma faixa). Essa faixa de valores deve obedecer os seguintes valores default para as métricas:
  7. 7. Macroarquitetura e métricas Sistemas mal modularizados tendem a ser difíceis de manter. APIs bem projetadas permitem que esses sistemas evoluam de forma separada, com baixo impacto das partes no todo. Tais componentes macro devem ser construídos aplicando os princípios SOLID: • Single Responsibility Principle • Open Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle A não conformidade a esses princípios pode ser observada indiretamente com várias métricas diferentes, que veremos a seguir. Tangle e FAT Duas métricas presentes na ferramenta Structure101 para analisar uma boa arquitetura são: Tangle e FAT. A nível de componentes a DSM deverá ser triangular inferior, sem nenhum valor acima da diagonal da matriz. Exemplos bons:
  8. 8. São bons pois não há elementos acima da diagonal. Exemplo inaceitável (alto débito técnico):
  9. 9. A métrica de triangularização inferior é fácil de ser computada: somatório das células abaixo da diagonal dividido pelo somatório de todas as células. O resultado deve dar 1. Qualquer valor inferior a isso significa presença de TANGLE. A DSM pode ser visualizada em ferramentas como IntelliJ IDEA (Java), SONAR (no caso de Java) ou Structure 101 (várias linguagens). Haja vista que uma forma de burlar uma DSM imperfeita é amalgamar um ou mais componentes “emaranhados” em um só (mais monolítico – aumentando o débito técnico), a métrica FAT deve ser observada para o projeto terceirizado também. O monolito seria um único componente, de métrica FAT máxima – algo indesejável. Como balancear o tamanho dos componentes? O ponto de Tangle X Fat deve ficar na região verde (e não na região vermelha), segundo o Structure 101. Eis um exemplo indesejável: Que técnicas usar? É preciso aplicar heurísticas para controlar a coesão e o acoplamento de tais módulos: • Reuse-release equivalence principle (REP) • Common-reuse principle (CRP) • Common-closure principle (CCP) • Acyclic Dependencies Principle (ADP) • Stable-dependencies principle (SDP) • Stable-abstractions principle (SAP) Maiores detalhes podem ser encontrados na bibliografia ágil (Bob Martin, etc) ou até mesmo no wikipedia.
  10. 10. Instabilidade e Abstração A utilização de Interfaces e classes abstratas permite que clientes e provedores sejam desacoplados. Sendo assim, precisamos também de métricas que avaliem quão concretos ou abstratos são os sistemas sendo utilizados. Felizmente as métricas de Instabilidade e Distância da Main Sequence ( conhecidas como métricas Bob Martin ) nos permitem avaliar o grau de maleabilidade de uma solução de software. Basicamente queremos componentes que evitem a zona da dor e a zona da inutilidade: Exemplo aceitável: As ferramentas stan4j e php_depend são exemplos de utilitários que plotam esse tipo de gráfico:
  11. 11. Outra alternativa é srccheck https://github.com/sglebs/srccheck#oo-instability- and-abstractness-plots :
  12. 12. O Dependency Inversion Principle deve ser seguido para que não haja elementos na zona da dor. O ralo do grafo de dependência deve ser abstrato e estável (Interfaces ou classes abstratas). Arquitetura Intencional Explícita e em Camadas A arquitetura do software deve ser algo intencional e controlado. Uma visão de caixinhas e não violação de camadas de forma visual deve ser projetada e acompanhada em ferramentas apropriadas. Eis um exemplo onde os desenvolvedores violaram a arquitetura desejada pelo arquiteto, por falta desse tipo de controle:
  13. 13. Em uma arquitetura Model-View-Presenter (MVP), por exemplo, a camada modelo não deve ter conhecimento da (referências estáticas para a) camada View e nem Presenter, e isso deve ser explicitado na ferramenta e garantido no build. Exemplo real: Similarmente, o princípio DIP (Dependency Inversion Principle) pode ser garantido formalmente em linguagens estáticas da seguinte forma:
  14. 14. Vemos que uma camada Impl faz referencia às APIs, e a camada Builder conecta as partes compatíveis a nível de API. O fato de as APIs estarem no ralo do grafo (somente arestas incidentes e não de saída) demonstra aderência aos princípios de Stable Abstractions Principle e Stable Dependencies Principle. Sub-arquiteturas: design patterns Com o intuito de evitar reinventar a roda, sempre que padrões de projeto existirem para solucionar determinado problema e sua utilização flexibiliza e simplifica a implementação, esses devem ser adotados. Exemplos como Observer (e sua variação web chamada de WebHook), Model-View-Contoller. O padrão Singleton (global disfarçada) deverá ser evitado ao máximo. Se o seu time não é versado em Design Patterns, recomendamos capacitá-los. No caso de e-learning, recomendamos o curso da Industrial Logic bem como o livro Refactoring to Patterns. Bugfinders No caso de caça-bugs através de análise estática do código, devem haver 0 erros severos e 0 erros críticos ou medianos. Apenas Warnings serão aceitas com ressalvas, de comum acordo. Ferramentas possíveis são Findbugs (Java), PC-Lint (C/C++), jsLint (JavaScript) dentre outros, a serem escolhidas de comum acordo dependendo da plataforma usada (Java, Python, etc). Cobertura A cobertura de código realizada por testes automatizados (sejam as User Stories executáveis no FitNesse ou sejam os testes unitários xUnit ou uma combinação desses) deve ser igual ou superior a 70%. No build, os testes devem ser executados com instrumentação para que essa cobertura seja coletada. Code Cloning Esse é o termo técnico para a “reusabilidade copy/paste”, que produz muito código duplicado. A taxa de duplicação de código do projeto deverá ser inferior a 1%. Há uma diversidade de ferramentas – algumas específicas da linguagem usada – que podem ser usadas para medir tal duplicação de código.
  15. 15. Ferramentas para esse tipo de análise existem tanto como Open Source ou produto comercial: Simian, CPD (Copy/Paste Detector), etc. APIs Documentação de APIs Todas as APIs públicas de serviços e façades devem estar documentadas com Javadoc ou equivalente na linguagem usada. Parâmetros, exceções, valores de retorno devem estar claramente explicados. No caso de APIs REST, essas deverão ser documentadas com Swagger. Padronização de APIs As APIs REST devem ser desenvolvidas de acordo com o padrão de facto da indústria. Recomendamos aderir às guidelines do ebook “Web API Design – Crafting Interfaces that Developers Love” da apigee. No caso de APIs de componentes ou fachadas na linguagem de programação, guidelines de boas APIs como o material de Joshua Bloch devem ser seguinda: How to Design a Good API and Why It Matters [pdf] [palestra]. Evolução de APIs REST Todas as APIs públicas de serviços REST devem evoluir gradativamente, sem quebrar código existente anterior. Todas as APIs oficiais de uma Release N deverão ser suportadas na versão N+1 sem quebras. Algumas podem ser marcadas como Deprecadas mas ainda assim precisam rodar por mais uma Release. Apenas APIs deprecadas na Release X podem ser removidas na Release X+1. Isso oferece uma janela de tempo de adaptação, com uma Release intermediária servindo de warning das APIs a serem removidas. 2.3 - Dashboards Todas essas métricas devem estar consolidadas em dashboards Jenkins e SONAR. Eis um exemplo real:
  16. 16. A análise pode ser instrumentada facilmente em um pipeline de build. Para experimentos, nossa imagem docker pode ser usada: https://github.com/sglebs/mysonar . 2.4 - Deployment Desenvolvimento ágil moderno usualmente engloba rodar testes locais primeiramente e depois testes em um ambiente DEV. Caso passem no DEV, o sistema é “deployado” para um ambiente STAGING (release candidate) e, caso os testes passem no STAGING, ele é promovido (deployado) para o LIVE. Esse processo deve ser todo automatizado. Para tanto, aconselha-se o uso de algumas tecnologias facilitadoras, como por exemplo Vagrant, Docker, Maven, Git etc. Combinadas, algumas dessas ferramentas conseguem implementar uma plataforma altamente ágil e flexível de deploy como o Heroku (basta um git push, todas os parâmetros são definidos por variáveis de ambiente, componentes como banco de dados etc podem ser “attachados”). Maiores detalhes no artigo 12factor. O runtime de deployment deverá ser baseado em docker – seja em tsuru, OpenShift, CoreOS ou outros. 2.5 - Evolução de Database Schema: Migrations Uma fonte comum de dor de cabeça, bugs e custo de implantação ou atualização são os scripts de migração de schema do banco de dados. Felizmente esse problema já foi resolvido na comunidade ágil (por exemplo, Ruby/Rails Active Record Migrations) com o conceito de migrations. No caso de Python/Django, por exemplo, a ferramenta South. Uma solução independente de linguagem/plataforma é o
  17. 17. Liquibase. A Shopify.com, por exemplo, conta com 140 mil lojas online e é capaz de executar 75 deploys diários sem downtime para essas lojas graças ao uso do Large Hadron Migrator. Ou seja, o projeto a ser entregue pela terceirizada não deve fazer uso de scripts ad- hoc ou de intervenção manual para upgrade/downgrade de schema. Todo esse processo de upgrade/downgrade deverá ser feito de forma automática no banco através do uso desse mecanismo de migrations – o específico da plataforma/banco usado. 2.6 – Escalabilidade A programação do sistema não deve impedir a escalabilidade do mesmo na forma de N instâncias. Ou seja, guardar valores-cache em variáveis estáticas/globais d alinguagem de programação é inviável, uma vez que podem haver 2,3,4,5,... máquinas respondendo aos requests de forma round-robin. Storage permanente e/ou volátil deve ser tratado por servidores dedicados pra isso – PostgreSQL, Memcache, Redis etc. O limite do sistema deve ser demonstrável através de ferramentas como tsung ou locust. Um perfil de teste de build deve ser o stress_test, o qual deverá exercitar esses testes de carga do deploy DEV segundo um perfil em escada. Exemplo: • Um request a cada 1000ms por 10 segundos (para warm-up) • Um request a cada 500ms por 10 segundos • Um request a cada 250ms por 10 segundos • Um request a cada 125ms por 10 segundos • Um request a cada 60ms por 10 segundos • Um request a cada 30ms por 10 segundos • Um request a cada 15ms por 10 segundos • Um request a cada 7ms por 10 segundos Os relatórios do build para esse teste de carga permitirão prever o “ponto de quebra” na arquitetura atual para um número N de requests simultâneos. Deve-se observar também os tempos médios de resposta para os percentis 95% e 99%. Fatores como auto-escalabilidade do runtime também serão colocados à prova com essa estratégia. 2.7 – Segurança Sistemas Web são frequentemente alvo de ataques de hackers. Visando uma qualidade e preocupação maior com a segurança, o código entregue será colocado para rodar e será analisado com relação a vulnerabilidades. Mais especificamente, usaremos a ferramenta Zed Attack Proxy: ZAP [home].

×