COMO REDUZIR O NÚMERO DE BUGS E VULNERABILIDADES DE … · 1 INTRODUÇÃO A demanda por qualidade...
Transcript of COMO REDUZIR O NÚMERO DE BUGS E VULNERABILIDADES DE … · 1 INTRODUÇÃO A demanda por qualidade...
UNIVERSIDADE FEDERAL FLUMINENSE
RODRIGO AGAPE VIEIRA MAGINA
COMO REDUZIR O NÚMERO DE BUGS E VULNERABILIDADES DE UMA APLICAÇÃO. TÉCNICAS E FERRAMENTAS PARA DESENVOL-
VER UM SOFTWARE SEGURO E ESTÁVEL
Niterói 2017
RODRIGO AGAPE VIEIRA MAGINA
COMO REDUZIR O NÚMERO DE BUGS E VULNERABILIDADES DE UMA APLICAÇÃO. TÉCNICAS E FERRAMENTAS PARA DESENVOL-
VER UM SOFTWARE SEGURO E ESTÁVEL
Trabalho de Conclusão de Curso subme-
tido ao Curso de Tecnologia em Siste-
mas de Computação da Universidade
Federal Fluminense como requisito par-
cial para obtenção do título de Tecnólogo
em Sistemas de Computação.
Orientadora: HELGA DOLORICO BALBI
NITERÓI
2017
Ficha Catalográfica elaborada pela Biblioteca da Escola de Engenharia e Instituto de Computação da UFF
M194 Magina, Rodrigo Agape Vieira
Como reduzir o número de bugs e vulnerabilidades de uma
aplicação : técnicas e ferramentas para desenvolver um software
seguro e estável / Rodrigo Agape Vieira Magina. – Niterói, RJ :
[s.n.], 2017.
100 f.
Projeto Final (Tecnólogo em Sistemas de Computação) –
Universidade Federal Fluminense, 2017.
Orientador: Helga Dolorico Balbi.
1. Desenvolvimento de software. 2. Engenharia de software. I.
Título.
CDD 005.1
RODRIGO AGAPE VIEIRA MAGINA
COMO REDUZIR O NÚMERO DE BUGS E VULNERABILIDADES DE UMA APLICAÇÃO. TÉCNICAS E FERRAMENTAS PARA DESENVOL-
VER UM SOFTWARE SEGURO E ESTÁVEL
Trabalho de Conclusão de Curso subme-
tido ao Curso de Tecnologia em Siste-
mas de Computação da Universidade
Federal Fluminense como requisito par-
cial para obtenção do título de Tecnólogo
em Sistemas de Computação.
Niterói, 5 de Junho de 2017.
Banca Examinadora:
_________________________________________
Profa Helga Dolorico Balbi, Msc. – Orientadora
UFF – Universidade Federal Fluminense
_________________________________________
Prof. Jean de Oliveira Zahn, Msc. – Avaliador
UFF – Universidade Federal Fluminense
Dedico esse trabalho a minha companheira
Suelen Alves, que mesmo nos momentos di-
fíceis sempre esteve ao meu lado me dando
força para que sempre seguisse em frente.
Sem você eu não teria conseguido. Aos
meus pais, meus verdadeiros mestres, obri-
gado pelo apoio, carinho e amor.
AGRADECIMENTOS
A Deus, que sempre iluminou a minha cami-
nhada.
A minha Orientadora Helga Dolorico Balbi pe-
lo suporte e atenção.
A todos meus familiares pelo apoio, carinho e
paciência.
A todos que acreditaram em mim, o meu mui-
to obrigado.
“Força, foco e fé”.
RESUMO
Cada vez mais é comum usuários reclamarem de instabilidade em aplicações ou a mídia divulgar notícias sobre vulnerabilidades ou vazamentos de dados privados em algum tipo de software. Tendo em vista estas questões, ao longo dos anos medidas foram estudadas para aprimorar o desenvolvimento de software. Este trabalho apre-senta abordagens para melhorar a qualidade da aplicação, através da redução das suas falhas e vulnerabilidades. As abordagens que serão apresentadas foram retira-das de referências bibliográficas e artigos. Verificou-se que, através das técnicas descritas, é possível reduzir os problemas apresentados, deixando o software mais robusto. Porém, apesar de as abordagens contribuírem para o aumento da qualida-de do software, ainda será necessário um aprofundamento em pesquisas para che-gar ao software perfeito.
Palavras-chaves: qualidade de software, desenvolvimento de software, programa-
ção de software, correção de bugs, falha e brecha.
ABSTRACT
More and more users are complaining about instability in applications or the media are releasing news about vulnerabilities or private data leaks in some type of software. Given these issues, solutions have been studied over the years to improve software development. This work presents approaches to improve the quality of applications, through the reduction of its failures and vulnerabilities. The approaches that will be presented have been taken from bibliographical references and articles. It was verified that through the presented techniques it is possible to reduce the target problems, making the software more robust. But, although the approaches contribute to the increase of the quality of the software, it will still be necessary to deepen in researches to arrive at the perfect software.
Key words: software quality, software development, bugs, fix, failure and hack.
LISTA DE ILUSTRAÇÕES
Figura 1: Exemplos de arquiteturas monolítica (à esquerda) e de micro serviços (à
direita) [28]. ............................................................................................................... 31
Figura 2: Exemplo baixo nível de arquitetura micro serviços (à esquerda) e
arquitetura monolítica (à direita) [29] ......................................................................... 32
Figura 3: Exemplo de pirâmides de automação. [45] ................................................ 51
Figura 4: Exemplo de formato de user story no BDD ................................................ 58
Figura 5: Exemplo de formato de critério de aceitação no BDD ............................... 59
Figura 6: Exemplo do painel do SonarQube [70] ...................................................... 69
Figura 7: Exemplo do widget de bugs potencias e regras de código ........................ 70
Figura 8: Exemplo do widget de cobertura e testes .................................................. 71
Figura 9: Exemplo do widget de comentário ............................................................. 72
Figura 10: Exemplo do widget de duplicação de código ........................................... 73
Figura 11: Exemplo do widget de arquitetura e design ............................................. 74
Figura 12: Exemplo do widget de complexibilidade .................................................. 75
Figura 13: Exemplo da pirâmide de Maslow. [81] ..................................................... 84
LISTA DE ABREVIATURAS E SIGLAS
API – Application Programming Interface.
BDD – Behavior-Driven Design
CPU – Central Processing Unit
CVE – Common Vulnerabilities and Exposures.
DDD – Domain Driven Design
DSL – Domain-Specific Language
GUI – Graphical User Interface.
IDE – Integrated Development Environment
IEEE - Institute of Electrical and Electronics Engineers
ROI – Return on Investment.
RUP – Rational Unified Process
TDD – Test Driven Development.
UI – User Interface
XP – Extreme Programming
12
SUMÁRIO
RESUMO ..................................................................................................................... 8
ABSTRACT ................................................................................................................. 9
LISTA DE ILUSTRAÇÕES ......................................................................................... 10
LISTA DE ABREVIATURAS E SIGLAS ...................................................................... 11
1 INTRODUÇÃO ................................................................................................... 13
2 HISTÓRICO ....................................................................................................... 15
3 ARQUITETURA E IMPLEMENTAÇÃO .............................................................. 23
4 REVISÕES DE SOFTWARE .............................................................................. 36
5 TESTES ............................................................................................................. 42
6 MÉTODOS FORMAIS ........................................................................................ 64
7 ABORDAGENS NÃO TÉCNICAS ...................................................................... 81
CONCLUSÕES E TRABALHOS FUTUROS ............................................................. 88
REFERÊNCIAS BIBLIOGRÁFICAS .......................................................................... 90
13 13
1 INTRODUÇÃO
A demanda por qualidade é um fator que está sendo perseguido por todas
as áreas, não apenas pela engenharia de software. Comumente, empreendedores
querem oferecer um produto ou serviço de qualidade ao cliente. A qualidade na en-
genharia de software é obtida pela criação de software estável, fácil de usar, seguro,
íntegro e que atenda bem ao usuário final e aos seus objetivos finais. Um software
de qualidade é capaz de oferecer serviços de comunicação, segurança, localização,
entretenimento, além de oferecer competividade entre empresas.
De forma geral, quando um software deixa de funcionar ou apresenta
qualquer tipo de falha, as consequências geram transtornos sociais e econômicos.
Um problema gerado por uma falha de software pode deixar milhões de pessoas
sem comunicação, podem fazer bancos ficar fora do ar e seus clientes sem dinheiro.
Isso tudo devido a uma falha, também conhecida como bug1. Essas falhas também
podem ser exploradas por pessoas ou empresas mal-intencionadas, utilizando bre-
chas ou vulnerabilidades nos sistemas para obter informações privadas, recursos
monetários ou simplesmente gerar caos.
Diante de todos os fatos, será que é possível construir um software segu-
ro e estável? A engenharia de software busca essas metas há anos através de pes-
quisas, metodologias e conscientização, mas, devido à complexidade, ainda é um
problema nos dias atuais. Até termos coloquiais são utilizados na literatura para dar
ênfase ao tamanho do problema, como a expressão “bala de prata”, que faz analo-
gia a um software sem problemas.
Tendo em vista que eliminar completamente bugs e vulnerabilidades é um
trabalho complexo, o objetivo e foco deste trabalho será apresentar soluções desen-
volvidas para diminuir a incidência de falhas e vulnerabilidades, através de aborda-
gens técnicas e não técnicas na criação de um software. Nesse contexto, a propos-
1 É um erro em um programa de computador ou sistema que produz um resultado incorreto ou ines-perado.
14
ta de trabalho visa apresentar metodologias, ferramentas e conceitos que possibili-
tam a criação de softwares de maior qualidade.
Para o desenvolvimento do trabalho, foram utilizadas referências biblio-
gráficas, artigos e publicações científicas. Devido à complexidade da questão, di-
versas referências de diversos autores foram consultadas. As ferramentas apresen-
tadas, em sua grande maioria, são de código aberto e não possuem custos, deixan-
do o estudo mais fácil de ser aplicado de forma prática.
Este trabalho de conclusão do curso está estruturado em 7 capítulos que
apresentam os seguintes conteúdos: o segundo Capítulo apresenta uma abordagem
histórica de bugs e vulnerabilidades que ficaram famosos devido ao seu prejuízo,
aumentando o entendimento e urgência ao problema. Além disso, mostra um resu-
mo das metodologias e processos de software; no terceiro Capítulo é apresentada
uma arquitetura de software para se construir uma aplicação mais robusta com me-
nor complexidade e maior resistência a falhas, e também são descritas característi-
cas e sugestões de linguagens de programação com recursos para diminuir possí-
veis falhas em uma aplicação; o quarto Capítulo é dedicado às revisões de software,
abordagem importante para aumentar a qualidade da aplicação, tornando-a mais
segura além de aumentar o conhecimento técnico do time de desenvolvedores; no
Capítulo cinco, são mostradas metodologias para aumentar a qualidade dos testes
de software, deixando claro os benefícios da sua automatização. Além disso, des-
creve como realiza-los e que ferramentas utilizar; no sexto Capítulo, é descrito como
utilizar ferramentas de análise estática e dinâmica para detectar possíveis bugs e
vulnerabilidades. Também são descritas ferramentas especializadas em buscar bre-
chas de segurança; no sétimo Capítulo é apresentada outra visão da engenharia de
software. Como o desenvolvimento de software envolve pessoas, o objetivo deste
capítulo é mostrar como alguns fatores pessoais como por exemplo a motivação afe-
ta na qualidade da aplicação. Por fim, são apresentadas as conclusões e ideias para
trabalhos futuros.
15
2 HISTÓRICO
Bugs em computadores existem desde a criação do computadores elétri-
cos, e através da historia ficará claro a necessidade de atenção ao assunto. Nas se-
ções seguintes, serão mostrados a historia da definição do termo bug e vulnerabili-
dade e prejuízos causados. E também uma breve descrição da evolução das meto-
dologias no processo de desenvolvimento de software.
2.1 BUG
Em 09 de setembro de 1947, o computador Mark II na Universidade de
Harvard (EUA) quebrou. Após a inspeção, os engenheiros diagnosticaram a causa -
uma mariposa havia entrado na máquina, talvez atraída pela luz e calor, e causou
um curto circuito nos relés. Os técnicos registraram o incidente em seu caderno com
uma entrada às 15:45 em que eles anexaram o bug na página com fita adesiva e
observaram: "Primeiro caso real de bug encontrado". Hoje a folha é mantida no Mu-
seu Nacional de História Americana da Smithsonian Institution em Washington [1] [2].
Apesar de a história ser muito popular, como qualquer outra história con-
tada, foi distorcida pelos anos. Não foi esse episódio que descreveu a palavra "bug"
para erros de computadores. A verdade é que bem antes desse incidente a palavra
já era utilizada para referenciar mal função em máquinas, como foi evidenciada pe-
las notas do inventor Thomas Edison em 1870. Apesar de fontes do Institute of Elec-
trical and Electronics Engineers (IEEE) terem dado o crédito do termo à Thomas
Edison, este se tornou popular pelos engenheiros de Harvard na era do Mark II [3].
Grace Hopper deveria ter ganho o crédito pela introdução do termo na
linguagem da computação e, em particular, a programação. Ela descreveu os even-
tos que cercam o infame inseto como segue:
When we were debugging Mark II, it was over in another building, and the
windows had no screens on them and we were working on it at night, of
16
course, and all the bugs in the world came in. And, one night she [Mark II]
conked out and we went to look for the bug and found an actual large moth,
about four inches wing span, in one of the relays beaten to death, and we
took it out and put it in the log book and pasted Scotch tap over it. [4]
No entanto uma coisa pode ser afirmada: O incidente serviu para popula-
rizar o uso da expressão "bug" sendo aplicada aos computadores, que hoje em dia é
o principal uso do termo. E também não há dúvidas sobre quem foi o responsável
por isso: Grace Hopper, matemática nascida em Nova York em 1906, soldado da
Marinha dos EUA e pioneira em tecnologia da informação.
Mais de 60 anos depois, os bugs ainda fazem parte da nossa realidade, e
não há previsão para a solução deste tipo de problema. Eles não habitam apenas
nossos sistemas operacionais e aplicativos - hoje eles se escondem dentro de nos-
sos telefones celulares, marca-passos, usinas, equipamentos médicos e até mesmo
em nossos carros. Seguem alguns exemplos de bugs ocorridos que também entra-
ram para a história [5] [6] [7]:
- 1962 - Mariner I, sonda espacial. Um bug no software de voo faz com que o
foguete se desvie do caminho pretendido no lançamento. O controle da mis-
são destruiu o foguete sobre o Oceano Atlântico. A investigação do acidente
descobriu que uma fórmula escrita em papel a lápis foi incorretamente trans-
crita em código de computador, fazendo com que o computador calculasse er-
roneamente a trajetória do foguete.
- 1982 - Gasoduto soviético. Funcionários que trabalharam para a Agência
Central de Inteligência alegaram plantar um bug em um sistema de computa-
dor canadense comprado para controlar o gasoduto transiberiano. Os soviéti-
cos obtiveram o sistema como parte de um amplo esforço para comprar ou
roubar secretamente tecnologia sensível dos EUA. O evento resultante foi a
maior explosão não-nuclear da história do planeta.
- 1985-1987 - Therac-25. Era uma máquina de radioterapia controlada por
computador, muito moderna para sua época, por permitir a utilização do
mesmo equipamento para a aplicação de diversas doses de radiação nos pa-
cientes. Houve uma série de pelo menos 6 acidentes entre 1985 e 1987, nos
quais os pacientes receberam overdose de radiação. Pelo menos cinco mor-
17
tes aconteceram devido aos acidentes, causados por um erro de condição de
corrida2 no software que controlava a máquina.
- 2000 - O bug do Milênio. No século passado, os desenvolvedores de software
nunca tinham pensado que seu código e criações sobreviveriam para o novo
milênio. Por esta razão, muitos assumiram que escrever "19" antes da variá-
vel "ano" era um desperdício desnecessário de memória. A maioria decidiu
omitir esses dois dígitos. Bilhões de dólares foram gastos a fim de atualizar os
sistemas de computador em todo o mundo.
- 2012 - Knight Capital. Uma das maiores corretoras de ações do mercado
americano lutou para permanecer à tona depois que um bug de software pro-
vocou uma perda de US $ 440 milhões em apenas 30 minutos. As ações da
empresa perderam 75% em dois dias depois que uma falha no software inun-
dou o mercado com transações indevidas.
2.2 VULNERABILIDADE
Em 2 de Novembro de 1988, o primeiro worm3 da Internet (o chamado
Morris Worm) infectou entre 2.000 e 6.000 computadores em menos de um dia,
aproveitando-se de uma simples vulnerabilidade chamada buffer overflow4. O código
específico era uma função na rotina de biblioteca de entrada/saída padrão chamada
gets() projetada para obter uma linha de texto sobre a rede. Infelizmente, a instrução
não tinha condição para limitar a sua entrada, e uma entrada excessivamente gran-
de permitia que o worm assumisse qualquer máquina [8] [9].
2 Falha no sistema onde o resultado do processo depende da sequencia ou sincronia de processos simultâneos. 3 São programas de computadores que se replicam de sistema para sistema sem a utilização de ar-quivos existentes. 4 Defeito ocasionado quando o programa de computador recebe mais dados que esteja preparado a receber, causando o corrompimento dos dados, travamento da aplicação, ou violação de segurança. Uma vez explorada pode permitir mudar o comportamento do programa e executar outras instruções permitindo ao atacante controlar o sistema.
18
Há muitas definições diferentes do termo "vulnerabilidade" abrangendo
várias combinações de conceitos, incluindo conhecimento, ataques, exploração, ris-
co, intenção, ameaça, escopo e tempo de introdução. Seguem algumas definições:
1. Em Matt Bishop e Dave Bailey [10]:
A computer system is composed of states describing the current configura-
tion of the entities that make up the computer system. The system computes
through the application of state transitions that change the state of the sys-
tem. All states reachable from a given initial state using a set of state transi-
tions fall into the class of authorized or unauthorized, as defined by a securi-
ty policy. In this paper, the definitions of these classes and transitions is con-
sidered axiomatic.
A vulnerable state is an authorized state from which an unauthorized state
can be reached using authorized state transitions. A compromised state is
the state so reached. An attack is a sequence of authorized state transitions
which end in a compromised state. By definition, an attack begins in a vul-
nerable state.
A vulnerability is a characterization of a vulnerable state which distinguishes
it from all non- vulnerable states. If generic, the vulnerability may character-
ize many vulnerable states; if specific, it may characterize only one...
2. Em Data & Computer Security Dictionary of Standards, Concepts, and Terms
[11]:
1) In computer security, a weakness in automated systems security proce-
dures, administrative controls, Internet controls, etc., that could be exploited
by a threat to gain unauthorized access to information of to disrupt critical
processing. 2) In computer security, a weakness in the physical layout, or-
ganization, procedures, personnel, management, administration, hardware
or software that may be exploited to cause harm to the ADP system activity.
The presence of vulnerability does not itself cause harm. A vulnerability is
merely a condition or set of conditions that may allow the ADP system or ac-
tivity to be harmed by an attack. 3) In computer security, any weakness or
aw existing in a system. The attack or harmful event, or the opportunity
available to a threat agent to mount that attack.
19
Diante dessas definições podemos resumidamente afirmar que uma vul-nerabilidade é uma fraqueza em um produto que pode permitir que um invasor
comprometa a integridade, a disponibilidade ou a confidencialidade de um produto.
Algumas outras vulnerabilidades também entraram para história como [12]:
- OpenSSL Heartbleed Vulnerability (CVE-2014-0160) - O Heartbleed Bug na
biblioteca criptográfica OpenSSL expôs websites e softwares baseados em
SSL a ataques que permitiriam roubo de informações em uma escala sem
precedentes. Cerca de um terço de todos os principais sites foram considera-
dos vulneráveis à questão quando o Heartbleed foi divulgado pela primeira
vez em abril de 2014.
- DNS Cache Poisoning: Kaminsky Bug (CVE-2008-1447) - A descoberta de
2008 de Dan Kaminsky desencadeou a preocupação generalizada com a se-
gurança da Internet, levou a uma campanha de patches simultâneos sem
precedentes, por dezenas de fornecedores de tecnologia em todo o mundo. A
falha fundamental, no nível do protocolo DNS, oferecia aos atacantes uma
maneira relativamente direta de usar dados falsificados para redirecionar o
tráfego da Web para os destinos de sua escolha.
- GNU Bash Remote Code Execution Vulnerability: Shellshock (CVE-2014-6271)
- A vulnerabilidade do Shellshock foi um bug que competiu com o Heartbleed
pela duvidosa distinção de ser o pior de todos, afetou a maioria das versões
do Unix, Linux e Mac OS X e permitiu que os invasores executassem códigos
maliciosos em sistemas vulneráveis. Alguns analistas estimaram que cerca
de meio bilhão de dispositivos conectados à Internet e servidores web esta-
vam vulneráveis ao problema no momento em que o bug foi revelado, incluin-
do servidores web, dispositivos Android, OpenBSD, clientes DHCP, servidores
SSH e dispositivos Mac OSX.
Grandes avanços foram feitos na definição de vulnerabilidades de softwa-
res, catalogando-os e compreendendo-os, e também na educação da comunidade
de software, patches de correção e defeitos. Vulnerabilidades significativas são en-
contradas rotineiramente. Muitas delas foram descobertas há anos, mas as corre-
20
ções não são aplicadas frequentemente. É necessária uma nova abordagem com
uma maior conscientização da comunidade de software.
2.3 METODOLOGIAS DE DESENVOLVIMENTO
Modelos de processos de desenvolvimento foram originalmente propostos
para trazer ordem ao caos no desenvolvimento de software. A história indica que
estes modelos trouxeram uma certa quantidade de estruturas úteis para o trabalho
de engenharia de software e forneceram um roteiro eficaz para equipes de software.
Seguem alguns modelos para exemplificar [13]:
2.3.1 Modelo Cascata
O modelo cascata consiste nas seguintes fases:
- Especificação de requisitos (análise de requisitos).
- Design de software.
- Implementação e Integração.
- Teste (ou Validação).
- Implantação (ou instalação).
- Manutenção.
Tradicionalmente, com esse modelo, só se pode começar a próxima fase
quando a fase anterior for concluída, sendo o paradigma mais antigo para a enge-
nharia de software.
21
2.3.2 Modelo Incremental
O desenvolvimento incremental corta a funcionalidade do sistema em
porções. Em cada porção, uma fatia de funcionalidade é entregue através de traba-
lho interdisciplinar, desde os requisitos até a implantação. As interações são agrupa-
das em fases: início, elaboração, construção e transição. A filosofia incremental é
também usada para todos os processos ágeis (Seção 2.3.3).
2.3.3 Desenvolvimento Ágil
É um grupo de métodos de desenvolvimento de software baseados em
desenvolvimento interativo e incremental.
As características desse tipo de processo de desenvolvimento são:
- Abordagem iterativa.
- Incremental: Software disponível para clientes a cada 2-4 semanas.
- Equipes auto organizadas e multifuncionais.
- Refatoração. Prática que busca aprimorar o design e legibilidade de um códi-
go.
Exemplos de métodos ágeis populares:
- Scrum. Metodologia ágil associada a gestão de projetos onde são divididos
em ciclos chamados de Sprints.
- Programação eXtreme (XP). Metodologia que compartilha as práticas do ma-
nifesto ágil com maior foco nas atividades de desenvolvimento de software.
22
2.4 MELHORIA DO PROCESSO DE SOFTWARE
A existência de um processo de software não garante que o software será
entregue a tempo, que satisfará as necessidades do cliente ou que exibirá as carac-
terísticas técnicas que levarão a características de qualidade de longo prazo. Os
padrões de processos devem ser combinados com uma sólida prática de engenharia
de software. Além disso, o processo em si pode ser avaliado para garantir que ele
atende a um conjunto de critérios de processo básicos que se mostraram essenciais
para uma engenharia de software bem-sucedida.
Várias abordagens diferentes para avaliação e melhoria de processos de
software têm sido propostas nas últimas décadas, conforme abaixo [14]:
- SCAMPI: Prova um modelo de avaliação de processo de cinco etapas que in-
corpora cinco fases: iniciando, diagnosticando, estabelecendo, agindo e
aprendendo. O método SCAMPI utiliza o SEI CMMI como base para avalia-
ção.
- CBA IPI: Fornece uma técnica de diagnóstico para avaliar a maturidade relati-
va de uma organização de software; utiliza o SEI CMM como base para a
avaliação.
- SPICE (ISO / IEC15504): Um padrão que define um conjunto de requisitos
para a avaliação de processos de software. A intenção do padrão é auxiliar as
organizações no desenvolvimento de uma avaliação objetiva da eficácia de
qualquer processo de software definido.
- ISO 9001: 2000 para Software. Um padrão genérico que se aplica a qualquer
organização que queira melhorar a qualidade geral dos produtos, sistemas ou
serviços que ela fornece. Portanto, o padrão é diretamente aplicável a organi-
zações de software e empresas.
As técnicas mostradas nos próximos capítulos são práticas da metodolo-
gia ágil, mas podem ser aplicadas a qualquer metodologia escolhida para o projeto,
visando o aumento de qualidade do software.
23
3 ARQUITETURA E IMPLEMENTAÇÃO
A arquitetura de um software é um processo criativo onde se cria uma or-
ganização que irá satisfazer os requisitos funcionais e não funcionais de um sistema.
Por ser um processo criativo, as atividades dentro do processo dependem do tipo de
sistema que está sendo desenvolvido e dos requisitos específicos para ele. Este
processo é de grande importância porque afeta o desempenho, robustez, distribui-
ção e manutenção de um sistema [15].
Nas seções a seguir serão mostradas propostas de arquitetura e design,
visando manutenção, segurança e proteção do sistema, assim como linguagens de
programação para implementação da arquitetura apresentada.
3.1 DESIGN DE SOFTWARE
Um design de software é uma representação, ou modelo do software a
ser construído. Seu objetivo é permitir aos programadores implementar os requisitos
designando as partes projetadas da implementação. É um conjunto de documentos
contendo texto e diagramas para servir como a base na qual um aplicativo pode ser
totalmente programado. Um projeto de software completo deve ser tão explícito que
um programador poderia codificar o aplicativo dele sem a necessidade de quaisquer
outros documentos. Quando o modelo é construído, ele poderá ser avaliado quanto
à qualidade e melhorado antes que o código seja gerado, sendo o primeiro passo
para a qualidade do software [16].
Uma das abordagens para o design de software é o DDD (Domain-Driven
Design), que será mostrado a seguir.
24
3.1.1 Domain-Driven Design (DDD)
Inicialmente é necessário que se entenda corretamente o que é um domí-
nio de negócio, esse é o coração do DDD. Domínio de negócio refere-se à área de
assunto para qual se está criando um software. Por exemplo, ao escrever um sof-
tware para a indústria de saúde para registrar o tratamento do paciente, não é impor-
tante aprender a se tornar um médico. O que é importante entender é a terminologia
do setor de saúde, como os departamentos diferentes veem os pacientes e os cui-
dados, quais informações os médicos coletam e o que fazem com elas.
A fim de se criar um bom software, é necessário saber para que o softwa-
re servirá. Não se pode criar um sistema de registro de tratamento, a menos que
tenha uma boa compreensão do que é o registro de tratamento para a indústria de
saúde, ou seja, é preciso entender o seu domínio. Quem possui o conhecimento so-
bre o correto registro do tratamento do paciente, não é o desenvolvedor, nem o ar-
quiteto, nem o analista de software, e sim quem vai utilizar o sistema. Ele saberá de
todos os detalhes, regras de negócio e questões possíveis. É aqui que deve come-
çar a modelagem do software, pelo domínio [17].
Há um esforço para produzir um desenvolvimento de qualidade e evitar a
entrega de software com um número fatal de bugs. No entanto, mesmo produzindo
um software completamente livre de bugs, não significa que atenderá ao negócio. O
modelo de software e a forma como o software expressa a solução para a meta de
negócios que está sendo procurada, têm que estar alinhados com a aplicação de-
senvolvida. Sem ter um bom modelo de domínio para servir de guia, a aplicação ge-
rada não atenderá as estratégias de negócio.
Os projetos de software falham quando não tem o entendimento do domí-
nio de negócios em que se está trabalhando. Digitar não é o gargalo para entregar
um produto, a codificação é a parte mais fácil do desenvolvimento. Fora os requisi-
tos não-funcionais, criar e manter um modelo de software para atender aos casos de
uso é a parte difícil.
A abordagem de desenvolvimento de software denominada Domain-
Driven Design - ou DDD - existe para ajudar os desenvolvedores a terem mais su-
cesso na obtenção de modelos de software de alta qualidade. Quando implementa-
25
do corretamente, gerará modelos simples que refletirão exatamente o que tem que
ser desenvolvido sem tornar a aplicação complexa. Uma das principais razões pelas
quais o software se torna complexo e difícil de gerenciar é devido à mistura de com-
plexidades de domínio com complexidades técnicas [18].
DDD pode ser visto como o fortalecimento dos conceitos sobre a Orienta-
ção a Objetos. São boas práticas que já existem há anos. A Orientação a Objetos
não se trata apenas de classes, heranças, polimorfismo, encapsulamento, mas tam-
bém engloba as seguintes características [19]:
- Alinhamento do código com o negócio: Ao implementar o DDD é essencial o
contato dos desenvolvedores com os especialistas do domínio.
- Favorece a reutilização: Os blocos que pertencem a um mesmo conceito de
domínio são reaproveitados em vários lugares.
- Redução do acoplamento: As partes que compõem o sistema interagem com
pouca dependência entre os módulos ou classes de objetos com diferentes
conceitos.
- Agnóstico a tecnologia. O mais importante é o entendimento das regras de
negócio e o reflexo do domínio no código. A tecnologia é importante, mas não
é uma preocupação.
Domain-Driven Design (DDD) é uma filosofia de desenvolvimento definida
por Eric Evans em seu trabalho “Domain-Driven Design: Tackling Complexity in the
Heart of Software” [20]. É uma abordagem de desenvolvimento de software projeta-
da para gerenciar produtos de software complexos e de grande escala. Em seu cen-
tro, ele se concentra na criação de uma linguagem compartilhada conhecida como
Linguagem Ubíqua para descrever eficiente e efetivamente um domínio de problema.
Essa linguagem ubíqua em constante evolução é a ferramenta que permite às equi-
pes de desenvolvimento e ao negócio colaborarem na construção de modelos úteis
do domínio do problema que são usados para resolver casos de uso de negócios. O
modelo é representado em software usando a mesma Linguagem Ubíqua e está no
centro de qualquer implementação técnica.
O DDD apresenta os seguintes benefícios:
26
- Isolar e organizar o código: A capacidade de quebrar grandes e complexos
problemas de domínios e concentrar os esforços da equipe sobre as partes
mais valiosas de um sistema é conhecido como o lado estratégico do DDD.
Este projeto estratégico ajuda a impedir que o software evolua incorretamente
como componentes da solução global são capazes de evoluir dentro de con-
textos de negócios bem definidos, sem ter um impacto negativo em outras
partes do sistema. Ele também arma a equipe com o conhecimento sobre o
que investir mais tempo e esforço entendendo o que é importante para o ne-
gócio [21].
- Manutenção do sistema: Qualquer desenvolvedor trabalhando em um sistema
complexo pode escrever um bom código e mantê-lo por um curto tempo. No
entanto, sem uma sinergia entre a base de código e o domínio do problema, o
desenvolvimento contínuo provavelmente acabará em uma base de código
que é difícil de modificar, resultando em uma aplicação monolítica e confusa.
Domain-Driven Design ajuda com este problema, colocando ênfase na verifi-
cação continua do quão útil é o código para o problema atual e desafiando a
evolução e simplificação dos modelos complexos de domínios [21]. - Aprendizagem através da colaboração: O DDD enfatiza a importância da co-
laboração entre as equipes de desenvolvimento e os especialistas em negó-
cios para produzir modelos úteis para resolver problemas. Sem essa colabo-
ração e o comprometimento dos especialistas em negócios, grande parte do
compartilhamento de conhecimento não será possível e as equipes de de-
senvolvimento não obterão uma visão mais profunda do domínio do problema.
Também é verdade que, através da colaboração e divisão de conhecimento, a
empresa tem a oportunidade de aprender muito mais sobre seu domínio [18].
- Ágil, interativo, modelagem contínua: A palavra design pode provocar pensa-
mentos negativos nas mentes da gestão empresarial. No entanto, o DDD não
propõe um design de muita cerimônia e processo de desenvolvimento. DDD
não se trata de desenhar diagramas, trata-se de refinar cuidadosamente o
modelo mental de especialistas de domínio em um modelo útil para o negócio.
Não se trata de criar um modelo do mundo real, como tentar imitar a realidade.
Os esforços da equipe seguem uma abordagem ágil, que é iterativa e incre-
mental. Qualquer processo ágil que a equipe se sinta confortável pode ser
27
usado com sucesso em um projeto DDD. O modelo que é produzido é o sof-
tware de trabalho que é refinado continuamente até que não seja mais neces-
sário pelo negócio [18].
Utilizando-se um design de sistemas baseado em DDD, haverá uma me-
lhora no entendimento e implementação do domínio de negócio, levando a um me-
lhor design e com isso a diminuição da complexidade do sistema. Com a aplicação
menos complexa, melhor será a manutenção do sistema, fazendo com que o softwa-
re tenha menos defeitos a um longo prazo.
3.2 ARQUITETURA
A arquitetura de software de um programa ou sistema de computação são
as estruturas do sistema que compreendem componentes de software, propriedades
externamente visíveis desses componentes e as relações entre eles. Não é o sof-
tware operacional. Em vez disso, é uma representação que permite analisar a eficá-
cia do projeto no cumprimento de seus requisitos declarados, considerar alternativas
em uma fase em que fazer alterações de projeto ainda é relativamente fácil, e redu-
zir os riscos associados à construção do software [14].
Uma boa escolha de arquitetura poderá salvar uma aplicação da comple-
xidade desnecessária. Assim como na construção de prédios, a criação de softwa-
res também deve ser orquestrada, tendo em vista que a escolha de uma arquitetura
é tida como um dos fatores para o sucesso ou falha do empreendimento.
Para um melhor entendimento da solução serão apresentados 2 tipos de
arquitetura: arquitetura monolítica, utilizada pela grande maioria das aplicações, dei-
xando claro seus problemas; e uma sugestão de arquitetura utilizando micro serviço.
28
3.2.1 Arquitetura Monolítica
Uma arquitetura monolítica, é toda arquitetura de aplicação que possui as
seguintes características [22]:
- Módulo monolítico: Quando todo código do sistema é um único código base
que é compilado junto e produz um simples artefato. O código pode estar até
bem estruturado, mas não há separação de módulos para a compilação.
- Alocação monolítica: Todo código é entregue ao mesmo tempo, tudo está
compilado e pronto para produção então uma única versão é entregue. Todos
os componentes do software possuem a mesma versão.
- Execução monolítica: Uma simples aplicação ou processo que realiza todo o
trabalho para o sistema.
A maioria das aplicações começam a ser desenvolvidas como uma arqui-
tetura monolítica devido a simplicidade no desenvolvimento e nos testes. Mas, com
o seu crescimento, alguns problemas ficam mais evidentes, como os seguintes [23]:
- A base de código monolítica intimida os desenvolvedores, especialmente
aqueles que são novos na equipe. A aplicação será difícil de entender e modi-
ficar, tornando o desenvolvimento mais lento e arriscado.
- IDE sobrecarregada: Quanto maior a base de código, mais lento é o IDE dei-
xando os desenvolvedores menos produtivos.
- Dificuldade na escalabilidade, não permitindo dimensionar cada componente
independentemente.
- Redução de confiança. Erro em qualquer módulo (por exemplo, vazamento de
memória) pode potencialmente derrubar todo o processo. Além disso, como
todas as instâncias do aplicativo são idênticas, esse bug afetará a disponibili-
dade do aplicativo inteiro.
- Qualquer alteração torna necessário republicar a aplicação inteira e como a
sua arquitetura é monolítica, composta de um único processo, a alteração po-
de impactar toda a aplicação.
29
- Requer um compromisso de longo prazo com uma tecnologia específica.
Como as mudanças nos frameworks ou nas linguagens afetarão o aplicativo
inteiro, a mudança é extremamente cara em tempo e custo.
- Utilizando-se uma arquitetura monolítica, códigos relacionados a funções se-
melhantes começam a ficar espalhados por todo projeto, tornando a correção
de erros e implementações cada vez mais difíceis [24].
3.2.2 Arquitetura Micro Serviço
Pode-se definir um micro serviço como: An approach to designing software as a suite of small services, each running
in its own process and communicating with lightweight mechanisms. [25]
Microservices are small, autonomous services that work together. [24]
O conceito básico de um micro serviço é simples: é um pequeno aplicati-
vo que realiza uma única tarefa, e realiza essa tarefa bem. Um micro serviço é um
pequeno componente que é facilmente substituível, desenvolvido de forma indepen-
dente, e publicável de maneira independente.
Dentro de um sistema monolítico, há a tentativa de garantir a coesão no
código muitas vezes através da criação de abstrações ou módulos. A coesão, que
determina que classes/serviços devem ter apenas uma única responsabilidade, é um
conceito importante quando se pensa sobre uma arquitetura micro serviço. Sendo
reforçada pela definição do princípio da responsabilidade única, que afirma Robert C.
Martin, “Reunir classes que mudam pela mesma razão, e separar aquelas que mu-
dam por razões diferentes. “ [26]
Micro serviço toma esta mesma abordagem para serviços independentes.
Os limites do serviço são limites de negócios, tornando-se óbvio que o código vive
para uma determinada funcionalidade. E por manter este serviço focado em um limi-
te explícito, evita a tentação que cresça, com todas as dificuldades associadas que o
crescimento possa introduzir.
30
Os benefícios desta arquitetura são [27]:
- Cada micro serviço é relativamente pequeno, ficando mais fácil para um de-
senvolvedor entender.
- Cada serviço pode ser desenvolvido e implantado de forma independente de
outros serviços.
- Permite escalar melhor o desenvolvimento, dividindo o esforço em torno de
várias equipes. Cada equipe pode desenvolver, implementar e escalar os
seus serviços de forma independente de todas as outras equipes.
- Melhoria no isolamento de falhas. Por exemplo, se houver um vazamento de
memória em um serviço, apenas esse serviço será afetado, os outros serviços
vão continuar trabalhando normalmente. Em comparação, um componente
com mal comportamento em uma arquitetura monolítica, pode derrubar todo o
sistema.
- Elimina qualquer compromisso de longo prazo a tecnologia. Ao desenvolver
um novo serviço poderá escolher uma nova tecnologia, da mesma forma ao
fazer grandes mudanças.
Para deixar mais claro a abordagem, a Figura 1 mostra uma visão de
mais alto nível de como as funcionalidades são implementadas envolvendo uma ar-
quitetura monolítica e uma arquitetura de micro serviços. Na arquitetura monolítica
as funcionalidades pertencem ao mesmo processo e o dimensionamento se dá de
toda a aplicação e não nas partes que requerem maior recurso. Na arquitetura mi-
croserviço a aplicação é construída como serviços, que são independentes e esca-
láveis.
31
Figura 1: Exemplos de arquiteturas monolítica (à esquerda) e de micro serviços (à direita) [28].
Como um sistema opera e o fluxo de controle passa entre microserviços,
há um incentivo natural para encapsular a comunicação intra-serviços para amortizar
o custo das fronteiras de cada serviço. Enquanto essa dosagem pode aumentar as
latências em alguns casos, também pode simplificar dependências entre componen-
tes e, eventualmente, reduzir a probabilidade de falhas de software e, por conse-
guinte, as vulnerabilidades.
A Figura 2 apresenta exemplos baixo nível das arquiteturas monolítica e
de micro serviços. Na direita, é apresentada uma abordagem utilizada por muitas
aplicações monolíticas, uma aplicação única ou dividida em camadas (módulos) com
um simples e centralizado banco de dados realizando a integração do sistema.
Esta é uma abordagem que aparenta ser mais simples e parece permitir o
reuso de entidade em subsistemas diferentes para tornar tudo consistente.
Mas a realidade é que acaba com uma quantidade enorme de tabelas que
servem para muitos subsistemas diferentes e incluem atributos e colunas
que simplesmente não são necessárias na maioria dos casos. [29]
32
Além de que toda aplicação este acoplado a um mesmo processo. Para
corrigir uma parte do programa com problemas, toda aplicação terá que ser republi-
cada, e isto não garantirá que a parte substituída não afetará as demais.
A esquerda da Figura 2 apresentada a arquitetura com a separação da
aplicação em diferentes serviços independentes podendo cada uma ter sua própria
tecnologia de persistência de dados. Como os serviços são independentes, cada
um poderá ter seu ciclo de vida independente dos outros serviços. Além disso é
exemplificado o uso de serviços com estado (stateful) e sem estado (stateless). Um
serviço stateful é útil para reduzir a latência entre a lógica e os dados de domínio,
muito utilizado para processamentos de dados pesados. O serviço stateless é mais
simples de implementar, porém apresenta performance inferior ao serviço stateful
devido a não guardar estado para o acesso mais rápido.
Figura 2: Exemplo baixo nível de arquitetura micro serviços (à esquerda) e arquitetura monolítica (à direita) [29]
3.3 LINGUAGEM
As linguagens de programação estão entre o CPU (Central Processing
Unit) e o programador e impõe regras que impedem alguns tipos de erros. Funções
33
nativas e as características dos seus compiladores são grandes aliados quando se
trata de detecção e prevenção de bugs. Os compiladores muitas vezes adicionam
uma camada extra de segurança, executando análise que pode sinalizar erros no
código muito antes da execução da aplicação.
Compiladores ajudam na prevenção de bugs, mas algumas ambiguidades
podem ser prejudiciais. Roderick Chapman, engenheiro da empresa Praxis, descre-
veu esse tipo de ambiguidade ao escrever uma simples função em C como “i++ *
i++”. Mesmo sabendo que “++” incrementa em uma unidade e “*” sinal de multiplica-
ção, o resultado dependerá do compilador utilizado. Alguns compiladores incremen-
tam antes de multiplicar outros depois, trazendo dificuldade para desenvolvedores
entender o correto funcionamento da linguagem. Such a problem could not happen in Spark, says Praxis’s Chapman, be-
cause all such ambiguous cases were considered—and eliminated—when
the language was created. Coding with Spark thus helps Praxis achieve re-
duced bug rates. In fact, once Spark code has been written, Chapman says,
it has the uncanny tendency to work the first time, just as you wanted. “Our
defect rate with Spark is at least 10 times, sometimes 100 times lower than
those created with other languages." [30]
Nas seções a seguir serão apresentadas características de linguagem
que previnem certos bugs no código, auxiliando na escolha da melhor linguagem
para o projeto.
3.3.1 Tipagem Forte
Uma linguagem é fortemente tipada, quando o compilador não permite
operações como atribuição e comparação entre variáveis de tipos diferentes, sem
usar uma conversão explicita. Por exemplo, não é possível utilizar operações de
ponto flutuante com valores inteiros, ou utilizar valores booleanos como inteiros, sem
utilizar algum tipo de conversão.
A linguagem fortemente tipada impõe restrições sobre operações entre
variáveis de tipos diferentes. Nesses casos, o compilador emite um erro proibindo a
34
operação. Essas diretrizes rigorosas sobre tipos de dados são projetadas para evitar
possíveis erros [31].
3.3.2 Tipagem Estática
A linguagem é estaticamente tipada se o tipo de uma variável é conhecido
em tempo de compilação em vez de em tempo de execução.
A grande vantagem de verificação de tipo estático é que ele permite que
muitos erros sejam descobertos cedo no ciclo de desenvolvimento. Tipagem estáti-
ca geralmente resulta em código compilado que executa mais rapidamente, porque
quando o compilador sabe os tipos de dados exatos que estão em uso, ele pode
produzir código otimizado da máquina (ou seja, mais rápido e / ou usando menos
memória). Verificadores de tipo estático avaliam apenas o tipo de informação que
pode ser determinado em tempo de compilação, mas são capazes de verificar se as
condições verificadas se mantêm por todas as execuções possíveis do programa, o
que elimina a necessidade de repetir verificações cada vez que o programa é execu-
tado. Um verificador estático irá detectar rapidamente erros de tipo em caminhos de
código raramente usados [32].
3.3.3 Automático Gerenciamento de Memória
Gerenciamento de memória envolve o fornecimento de memória necessá-
ria para objetos de um programa e estruturas de dados a partir dos recursos dispo-
níveis, e reciclando a memória para reutilização quando já não é necessário. Aplica-
ções não podem em geral prever com antecedência quanta memória elas vão exigir,
eles precisam de código adicional para lidar com seus requisitos de mudança de
memória.
O gerenciamento automático de memória é um serviço, seja como parte
da linguagem ou como uma extensão, que recicla automaticamente a memória que
35
um programa que não vai utilizar novamente. Eles costumam fazer o seu trabalho
através da reciclagem de blocos que são inacessíveis a partir das variáveis do pro-
grama (isto é, blocos que não podem ser alcançados por ponteiros).
As vantagens do gerenciamento automático são [33]:
- O programador é liberado para trabalhar no problema real;
- Interfaces do módulo são mais limpos;
- Há menos erros de gerenciamento de memória;
- Gerenciamento de memória é muitas vezes mais eficiente.
3.3.4 Estrutura Funcional
A programação funcional é assim chamada porque sua operação funda-
mental é a aplicação de funções a argumentos. Um programa principal em si é es-
crito como uma função que recebe a entrada do programa como seu argumento e
entrega a saída do programa como seu resultado. Normalmente, a função principal é
definida em termos de outras funções, que por sua vez são definidas em termos de
ainda mais funções, até que no nível inferior as funções são primitivas de linguagem.
Todas estas funções são muito semelhantes às funções matemáticas comuns.
Os programas funcionais não contêm instruções de atribuição, portanto as
variáveis, uma vez dadas um valor, nunca mudam. De um modo mais geral, os pro-
gramas funcionais não contêm efeitos colaterais. Uma chamada de função não pode
ter outro efeito senão calcular seu resultado. Isso elimina uma grande fonte de bugs
e também torna irrelevante a ordem de execução - uma vez que nenhum efeito late-
ral pode alterar o valor de uma expressão, ele pode ser avaliado a qualquer momen-
to. Isso alivia o programador da carga de prescrever o fluxo de controle. Uma vez
que as expressões podem ser avaliadas a qualquer momento, é possível substituir
livremente as variáveis por seus valores e vice-versa - isto é, os programas são "re-
ferencialmente transparentes". Essa liberdade ajuda a tornar os programas funcio-
nais mais tratáveis matematicamente do que suas contrapartes convencionais [34].
Para as características apresentadas acima, as seguintes linguagens
apresentam todos esses atributos: F# [35], Haskell [36] e Swift [37].
36
4 REVISÕES DE SOFTWARE
Revisões são atividades de controle de qualidade que verificam a quali-
dade dos entregáveis do projeto. Isso envolve a análise do software, a documenta-
ção e registros do processo para descobrir erros e omissões e ver se os padrões de
qualidade têm sido seguidos.
Revisões de qualidade são baseadas em documentos que foram produzi-
dos durante o processo de desenvolvimento de software. Além de especificações de
software, projetos ou códigos, modelos de processo, planos de teste, procedimentos
de gerenciamento de configuração, padrões de processo e manuais do usuário, to-
dos podem ser revisados. A revisão deve verificar a consistência e integridade dos
documentos ou código sob revisão e certificar-se de que os padrões de qualidade
foram seguidos.
No entanto, as revisões não se limitam a verificar a conformidade com os
padrões. Eles também são usados para ajudar a descobrir problemas e omissões no
software ou na documentação do projeto. As conclusões da revisão devem ser re-
gistradas formalmente como parte do processo de gestão da qualidade. Se os pro-
blemas foram descobertos, os comentários dos revisores devem ser passados para
o autor do software ou quem é responsável pela correção de erros ou omissões [15].
Os benefícios da revisão são:
- Detectar erros na lógica/estrutura do programa ou inconsistências de um arte-
fato para o próximo. Expor programas para outras pessoas ajuda na qualida-
de, tanto pela pressão de pessoas externas para fazer as tarefas bem e tam-
bém detectar problemas que um simples indivíduo não achou.
- Certificar-se de que a intenção do artefato é clara (o mais claro o melhor).
- Verificar se o design e/ou software cumpre seus requisitos.
- Para garantir que o software tenha sido desenvolvido de forma uniforme,
usando padrões acordados.
A revisão de código pode ser especialmente produtiva para identificar vul-
nerabilidades de segurança durante o desenvolvimento de software. Se for devida-
mente conduzida pode fazer mais para a segurança do código do que quase qual-
37
quer outra atividade. Ela permite localizar e corrigir um grande número de problemas
de segurança antes que o código seja testado ou publicado.
Se planejadas e gerenciadas corretamente, as revisões de código são
muito eficientes em termos de custo, especialmente se forem incorporadas ao pro-
cesso de desenvolvimento, evitando o caro processo de manipulação, localização e
correção de vulnerabilidades de segurança durante fases posteriores de desenvol-
vimento ou após lançamento do software. Outra vantagem desta abordagem é que
facilita a experiência e a educação da equipe de desenvolvimento no uso das melho-
res práticas de segurança, o que ajudará a prevenir futuras questões de segurança.
[38]
Além desses objetivos, quando as revisões envolvem a participação de
grupo, as revisões têm efeitos colaterais benéficos. Primeiramente, as revisões são
um meio excelente de aprender sobre o sistema geral e sobre as técnicas dos com-
panheiros de equipe a fim melhorar a comunicação. Em segundo lugar, trabalhando
juntos, várias pessoas da equipe se familiarizam um pouco com os detalhes do arte-
fato sob revisão. Esse conhecimento adicional é útil quando o criador do artefato não
está disponível e/ou participante da revisão deve interagir com o artefato. Finalmente,
há um benefício psicológico para o criador do artefato, quando outras pessoas estão
revisando os documentos ou códigos tem maior incentivo para tornar as coisas cla-
ras e simples. Como resultado, o trabalho é geralmente de maior qualidade.
Muitos tipos diferentes de revisões podem ser conduzidos como parte da
engenharia de software. Cada um tem seu lugar. Uma apresentação formal da ar-
quitetura de software para uma audiência de clientes, gerenciamento e equipe técni-
ca também é uma forma de revisão. Entretanto uma revisão técnica é o filtro mais
eficaz do ponto de vista do controle de qualidade. Conduzido por engenheiros de
software (e outros) para engenheiros de software, é um meio eficaz para descobrir
erros e melhorar a qualidade do software [39].
As revisões de software variam de acordo com a formalidade, sendo des-
critas nas seções abaixo, do método mais formal ao menos informal.
38
4.1 INSPEÇÃO
Uma inspeção é o tipo mais sistemático e rigoroso de revisão pelos pares.
Segue-se um processo bem definido, de vários estágios, com funções específicas
atribuídas a cada participante. O processo de inspeção mais comum inclui sete eta-
pas: planejamento, visão geral, preparação, reunião, retrabalho, acompanhamento e
análise causal. Para máxima eficácia, os inspetores devem ser treinados no proces-
so de inspeção e ser capazes de desempenhar todas as diferentes funções. Inspe-
ções dependem de listas de verificação de defeitos comuns encontrados em diferen-
tes tipos de produtos de trabalho de software, regras para construir produtos de tra-
balho e várias técnicas analíticas para procurar bugs.
Um aspecto fundamental da inspeção é que os participantes, além do au-
tor do produto de trabalho, conduzem a reunião (moderador), apresentam o material
à equipe de inspeção (leitor) e gravam as questões à medida que são levantadas.
Os participantes se preparam para a inspeção examinando o material por conta pró-
pria para compreendê-lo e encontrar problemas. Durante a reunião, o leitor apresen-
ta o material, uma pequena parte de cada vez, para os outros inspetores, que levan-
tam questões e apontam possíveis defeitos. O leitor ajuda a equipe a alcançar a
mesma interpretação de cada parte do produto, porque os inspetores podem compa-
rar sua compreensão com a expressa pelo leitor. No final, a equipe concorda com
uma avaliação do produto de trabalho e decide como verificar as mudanças que o
autor fará durante a retrabalho [40].
4.2 REVISÃO DE EQUIPE
São menos formais e abrangentes e seguem várias etapas encontradas
em uma inspeção. Os participantes recebem os materiais de revisão vários dias an-
tes da reunião, e espera-se que eles estudem os materiais por conta própria. A
equipe coleta dados sobre o esforço de revisão e o número de defeitos encontrados.
No entanto, os estágios de visão geral e de acompanhamento são simplificados ou
39
omitidos, e alguns papéis dos participantes podem ser combinados. O autor pode
liderar a revisão da equipe, e em contraste com a maioria das inspeções, o papel do
leitor é omitido. Em vez de um participante descrever um pequeno pedaço do produ-
to por vez, o líder da revisão pergunta aos participantes se eles têm comentários
sobre uma seção ou página específica.
Revisões de equipe custam mais do que ter uma única pessoa executan-
do uma revisão desk check, mas os diferentes participantes vão encontrar diferentes
conjuntos de erros. A pessoa responsável em gravar ou registrar, captura as ques-
tões à medida que elas são levantadas, usando formulários padrão adotados pela
organização.
Revisões de equipe são adequados para uma equipe ou produto que não
exige o rigor total do processo de inspeção [40].
4.3 WALKTHROUGH
É uma revisão informal em que o autor de um produto de trabalho descre-
ve para um grupo de colegas e solicita seus comentários. Walkthroughs diferem sig-
nificativamente de inspeções porque o autor assume o papel dominante. Não exis-
tem outros papéis específicos do revisor. Considerando-se que uma inspeção é des-
tinada a cumprir os objetivos de qualidade da equipe, um walkthrough serve princi-
palmente as necessidades do autor.
Normalmente não seguem um procedimento definido, não requerem rela-
tórios de gerenciamento e não geram métricas. Eles podem ser uma maneira efici-
ente de rever os produtos modificados durante a manutenção, porque o autor pode
chamar a atenção dos revisores para aquelas partes dos produtos que foram altera-
dos [40].
40
4.4 PROGRAMAÇÃO EM PAR (PAIR PROGRAMMING)
É uma prática popularizada pelo Extreme Programming e desenvolvimen-
to ágil, mas também é um processo de desenvolvimento que incorpora contínua re-
visão. Programação em par são dois desenvolvedores trabalhando no mesmo pro-
duto simultaneamente em uma única estação de trabalho. Isso facilita a comunica-
ção e oferece uma oportunidade para a revisão contínua, incremental e informal das
ideias de cada pessoa, podendo ser usado para criar qualquer produto de trabalho
de software, não apenas o código.
Culturalmente, a programação em pares promove a colaboração, a pro-
priedade coletiva da base de código da equipe e um compromisso compartilhado
com a qualidade de cada componente. Pelo menos dois membros da equipe tornam-
se intimamente familiarizados com cada pedaço de código, aumentando a dissemi-
nação do conhecimento.
Programação em par não é especificamente uma técnica de revisão, mas
sim uma estratégia de desenvolvimento que se baseia na sinergia de duas mentes
focadas para criar produtos superiores em design, execução e qualidade. No entanto,
é uma grande mudança de cultura na maneira como uma equipe de desenvolvimen-
to opera, portanto, não é uma simples substituição para revisões de pares tradicio-
nais na maioria das situações [40].
4.5 DESK CHECK
Em uma revisão desk check, apenas uma pessoa, além do autor, examina
o produto. O autor pode não saber como o revisor abordou a tarefa ou quão abran-
gente foi a revisão. A revisão depende inteiramente do conhecimento, habilidade e
autodisciplina do revisor, por isso espera ampla variabilidade nos resultados. Após a
conclusão, o revisor pode entregar uma lista de defeitos para o autor ou simples-
mente entregar ao autor o seu trabalho com marcações no produto.
41
O desk check é a abordagem de revisão mais barata pois envolve apenas
o tempo do revisor. Este método é adequado para produtos de trabalho de baixo
risco ou projetos com graves restrições de tempo e recursos. Esse tipo de revisão
pode ser mais confortável para o autor do que uma revisão em grupo que pode se
tornar intimidante, fornecendo uma boa maneira de começar a desenvolver uma cul-
tura de revisão [40].
4.6 PASS-AROUND
Nesse tipo de revisão é entregue uma cópia do produto para várias pes-
soas e o autor coleta os comentários de cada revisor. Esse tipo de revisão permite
que revisores com diferentes conhecimentos e experiência contribuam com o produ-
to.
O pass-around mitiga dois riscos principais de uma revisão desk check: o
revisor não fornecer feedback oportuno e/ou revisor não realizar um bom trabalho,
pois apresenta facilidade para envolver outras pessoas no processo, funcionando
bem para equipes geograficamente distribuídas.
Todas as técnicas acima são úteis e resultam em código de melhor quali-
dade. Não importando de qual tipo, a revisão de código é uma ótima maneira de
encontrar bugs, compartilhar informações e prover um aprendizado individual [40].
42
5 TESTES
Os testes destinam-se a mostrar que um programa faz o que se pretende
fazer e para descobrir defeitos do programa antes de ser colocado em uso. Ao testar
software, é executado um programa usando dados artificiais. Verifica-se os resulta-
dos da execução de testes para erros, anomalias ou informações sobre os atributos
não funcionais do programa.
O processo de teste tem dois objetivos distintos:
- Demonstrar ao desenvolvedor e ao cliente que o software atende aos seus
requisitos. Para software, isso significa que deve haver pelo menos um teste
para cada requisito no documento de requisitos. Para produtos genéricos de
software, isso significa que deve haver testes para todos os recursos do sis-
tema, além de combinações desses recursos, que serão incorporados na ver-
são do produto.
- Descobrir situações nas quais o comportamento do software é incorreto, inde-
sejável ou não está de acordo com sua especificação. Estas são uma conse-
quência de defeitos de software. O teste de defeitos está relacionado com a
erradicação do comportamento indesejável do sistema, como falhas do siste-
ma, interações indesejadas com outros sistemas, cálculos incorretos e cor-
rupção de dados.
O objetivo do teste é descobrir o máximo possível de defeitos, no mais
alto nível de seriedade. Não é possível testar um aplicativo com todos os valores de
entrada possíveis devido ao número extraordinariamente grande de combinações de
valores de entrada. Como disse o renomado cientista da computação Edsgar Dijks-
tra, “Testes somente pode mostrar a presença de erros, não a sua ausência”. Em
outras palavras o teste não pode demonstrar que o software está livre de defeitos ou
que se comportará como especificado em todas as circunstâncias, sendo sempre
possível descobrir mais problemas com o sistema.
43
Dois princípios básicos de teste de software são "testar cedo" e "testar
frequentemente". Estes preceitos têm sido respeitados por muitos anos e são uma
característica principal das metodologias ágeis.
"Testar cedo" significa testar as unidades assim que elas são implementa-
das, em vez de aguardar a conclusão das unidades de que fazem parte.
"Testar frequentemente" significa executar testes em todas as oportunida-
des razoáveis, incluindo após pequenas adições ou alterações. Testando muitas
vezes traduz em uma reexecução dos testes anteriores para então testar a nova
funcionalidade. Uma razão para testar muitas vezes é que as alterações às vezes
invalidam o código já implementado [16].
Será apresentada nas próximas seções uma introdução aos testes ágeis
e os seus benefícios comparado com testes tradicionais, deixando claro a necessi-
dade de serem automatizados, mostrando abordagens e ferramentas para alcançar
essa automação.
5.1 TESTE ÁGIL (AGILE TESTING)
É um conjunto de práticas, seguindo o Manifesto Ágil, que incorpora todas
as técnicas de teste comumente utilizadas por profissionais de teste, tendo um gran-
de foco em automação. A principal função é criticar o produto, ou seja, constante-
mente garantir que o que está sendo especificado e desenvolvido realmente atende
às necessidades do cliente e irá entregar valor ao negócio [41].
Na abordagem Ágil, desenvolvedores e testadores são vistos como dois
lados da mesma moeda de produção, duas linhas paralelas que devem sempre se
encontrar e comparar notas diariamente. De uma perspectiva ágil, a produção efici-
ente é severamente prejudicada se seus desenvolvedores estão se esforçando para
refinar seu código para um estado de perfeição antes de passá-lo para uma equipe
de testes separada, que então se esforçam para quebrá-lo de tantas maneiras quan-
to possível antes de enviar seus relatórios de danos para a equipe de desenvolvi-
mento. Este processo de duas etapas requer tempo, dinheiro e frequentemente
leva à divisão interna entre desenvolvedores de uma empresa e testadores. Em vez
44
disso, a perspectiva ágil sugere que essas duas funções essenciais sejam mistura-
das, não necessariamente em termos de pessoas, mas em termos de tempo e pro-
cesso, assim superando a divisão ilusória entre criadores e testadores de código,
reduzindo a necessidade de equipes de testes robustas. Ainda respeitando a ne-
cessidade de ambos os papéis, em ágil os desenvolvedores são encorajados a pen-
sar mais como testadores, continuamente verificando seu próprio código para erros
potenciais, e os testadores são incentivados a pensar mais como desenvolvedores,
temperando suas tendências para se engajar mais plenamente no próprio processo
criativo [42].
Várias práticas essenciais utilizadas pelas equipes ágeis estão relaciona-
das com os testes. Os programadores ágeis usam o test-driven developement
(TDD), um desenvolvimento de software orientado a testes, também chamado de-
sign test-driven, para escrever código de produção de qualidade. Os programadores
também escrevem testes de integração de código para certificar-se de que as pe-
quenas unidades de código trabalham juntas como planejado. Esta prática essenci-
al tem sido adotada por muitas equipes, mesmo aqueles que não se chamam "ágil",
porque é apenas uma maneira inteligente de pensar através de seu projeto de sof-
tware e evitar defeitos.
Teste ágil não significa apenas teste em um projeto ágil. Alguns métodos
de teste, como os testes exploratórios, são inerentemente ágeis, quer se trate de um
projeto ágil ou não.
5.2 TESTES TRADICIONAIS E TESTES ÁGEIS
Ágil é iterativo e incremental. Isso significa que os testadores testam ca-
da unidade de codificação assim que ele é finalizado. Uma iteração pode ser tão
curta como uma semana ou até um mês. A equipe constrói e testa um pouco de có-
digo, certificando-se de que ele funciona corretamente e, em seguida, passa para a
próxima unidade que precisa ser construída. Programadores nunca ficam à frente
dos testadores, porque uma história não é fechada até que tenha sido testada.
45
Ao invés de criar testes a partir de um documento de requisitos criado por
analistas de negócios antes que alguém tenha pensado em escrever uma linha de
código, alguém precisará escrever testes que ilustram os requisitos para cada histó-
ria dias ou horas antes do início da codificação. Isso geralmente é um esforço cola-
borativo entre um especialista em negócios ou domínio e um testador, analista ou
algum outro membro da equipe de desenvolvimento. Os casos de testes funcionais
detalhados, idealmente baseados em exemplos fornecidos por especialistas em ne-
gócios, substituem os requisitos. Os testadores realizarão testes manuais explorató-
rios para encontrar bugs importantes que os casos de teste definidos possam perder.
Os testadores podem se associar a outros desenvolvedores para automatizar e exe-
cutar casos de teste à medida que a codificação de cada história prossegue. Testes
funcionais automatizados são adicionados ao conjunto de testes de regressão.
Quando os testes que demonstram funcionalidade mínima estão completos, a equi-
pe pode considerar a história finalizada.
A diferença mais crítica para os testadores em um projeto ágil é o retorno
rápido do teste. Ele impulsiona o projeto para a frente, e não há nada para bloquear
o progresso do projeto se determinados marcos não forem atendidos.
Uma das maiores diferenças no desenvolvimento ágil para o desenvolvi-
mento tradicional é a abordagem ágil da "equipe inteira". Com ágil, não são só os
testadores ou uma equipe de garantia de qualidade que se sentem responsáveis
pela qualidade. Não há pensamos em "departamentos", apenas pensamentos em
habilidades e recursos necessários para oferecer o melhor produto possível. O foco
do desenvolvimento ágil é a produção de software de alta qualidade em um período
de tempo que maximiza o seu valor para o negócio. Este é o trabalho de toda a
equipe, não apenas testadores ou profissionais de garantia de qualidade. Todos na
equipe ágil ficam orientados a teste.
Uma equipe ágil deve possuir todas as habilidades necessárias para pro-
duzir código de qualidade que ofereça os recursos exigidos pela organização. Em-
bora isso possa significar a inclusão de especialistas na equipe, como testadores
especializados, não há limite de tarefas específicas a membros da equipe em parti-
cular. Qualquer tarefa pode ser concluída por qualquer membro da equipe, ou um
par de membros da equipe. Isso significa que a equipe assume a responsabilidade
por todos os tipos de tarefas de teste, como testes automatizados e testes explorató-
46
rios manuais. Isso também significa que toda a equipe pensa constantemente em
projetar código de testabilidade [43].
5.3 TESTES AUTOMATIZADOS
Cada grupo de desenvolvimento de software testa seus produtos, mas o
software fornecido sempre tem defeitos. Os engenheiros de teste se esforçam para
capturá-los antes que o produto seja lançado, mas eles sempre se aproximam e eles
muitas vezes reaparecem, mesmo com os melhores processos de teste manual.
Teste automatizado é a melhor maneira de aumentar a eficácia, eficiência e cobertu-
ra dos testes.
O teste manual de software é realizado por um ser humano sentado na
frente de um computador passando cuidadosamente por telas de aplicação, tentan-
do várias combinações de uso e entrada, comparando os resultados com o compor-
tamento esperado e registrando suas observações. Testes manuais são repetidos
frequentemente durante ciclos de desenvolvimento para alterações de código fonte e
outras situações como vários ambientes operacionais e configurações de hardware.
Uma ferramenta de teste automatizada é capaz de reproduzir ações pré-gravadas e
predefinidas, comparar os resultados com o comportamento esperado e relatar o
sucesso ou falha desses testes manuais para um engenheiro de teste. Uma vez
criados os testes automatizados, eles podem ser facilmente repetidos e podem ser
estendidos para executar tarefas impossíveis com testes manuais. Devido a isso,
gestores experientes descobriram que o teste de software automatizado é um com-
ponente essencial de projetos de desenvolvimento bem-sucedidos [43].
Principais benefícios:
- O teste manual demora muito tempo.
A razão mais básica para automatizar é que simplesmente leva muito
tempo para concluir todos os testes necessários manualmente. À medida que a
aplicação se torna cada vez maior, o tempo para testar tudo cresce mais e mais, às
vezes exponencialmente, dependendo da complexidade da aplicação em teste.
47
Executar um conjunto completo de testes de regressão pelo menos diari-
amente é uma prática indispensável, e não pode ser feito manualmente. Se execu-
tar o teste de regressão manualmente, é preciso mais e mais tempo testando todos
os dias, cada iteração. A fim de testar para manter o ritmo com codificação, ou os
programadores têm de ter tempo para ajudar com os testes de regressão manual-
mente, ou a equipe tem que contratar mais testadores. Inevitavelmente, tanto a dívi-
da técnica quanto a frustração crescerão.
Se o código não passar por um conjunto de testes automatizados, os tes-
tadores provavelmente gastarão muito tempo pesquisando, tentando reproduzir e
relatar bugs simples e menos tempo encontrando bugs de nível de sistema potenci-
almente sérios.
- Processos manuais são propensos a erros.
Testes manuais são repetitivos, especialmente se estiver seguindo testes
com script, e testes manuais ficam cansativos rapidamente. É muito fácil cometer
erros e ignorar erros simples. Passos e mesmo testes inteiros serão ignorados.
As compilações automáticas, implantação, controle de versão e monito-
ramento automatizados também percorrem um longo caminho no sentido de reduzir
riscos e tornar seu processo de desenvolvimento mais consistente. A automatização
destes testes de script elimina a possibilidade de erros, porque cada teste é feito
exatamente da mesma maneira cada vez.
- Automação libera as pessoas a fazer o seu melhor trabalho.
Escrever o teste de código ajuda aos programadores a compreenderem
os requisitos e ter conformidade com o design do código. Tendo compilações contí-
nuas executando todos os testes de unidade e os testes de regressão, significam
mais tempo para fazer testes exploratórios. Esses dois tipos de testes são muito
importantes. O teste de unidade é a forma de se testar unidades individuais de códi-
go fonte. Será visto com mais detalhes adiante. O teste de regressão significa testar
novamente uma parte de um aplicativo que não foi alterado para verificar se conti-
nua a funcionar corretamente.
Automatizar a configuração para testes exploratórios significa ainda mais
tempo para investigar partes potencialmente fracas do sistema. Como não foi gasto
48
tempo executando scripts manuais tediosos, tem a energia para fazer um bom traba-
lho, pensando em diferentes cenários e aprendendo mais sobre como o aplicativo
funciona.
Se o pensamento está constantemente em como automatizar testes para
uma correção ou nova característica, é mais provável o foco estar na testabilidade e
um design de qualidade, em vez de uma abordagem rápida que possa se mostrar
frágil. Isso significa melhor código e melhores testes.
- Os testes automatizados de regressão fornecem uma rede de segurança.
A maioria dos profissionais conhece a sensação de medo quando se de-
frontam com a correção de um bug ou a implementação de um novo recurso no có-
digo mal projetado que não é coberto por testes automatizados. Qualquer alteração
pode deixar uma parte do sistema instável e não terá o resultado do mal funciona-
mento, na maioria das vezes deixando para o usuário final descobrir o problema.
Conhecer o código com cobertura suficiente por testes de regressão au-
tomáticos dá uma grande sensação de confiança. Uma mudança pode produzir um
efeito inesperado, mas o resultado será dado em questão de minutos ou horas, se
em um nível funcional mais elevado. Fazer o teste de mudança primeiro significa
pensar através do comportamento alterado antes de escrever o código e escrever
um teste para o verificar, o que aumenta essa confiança.
- Os testes automatizados dão feedback de forma precoce e frequente.
Depois de um teste automatizado para uma unidade da funcionalidade
passa, ele deve continuar a passar até que a funcionalidade é alterada. Quando
planejamos mudanças no aplicativo, alteramos os testes para acomodá-los. Quando
um teste automatizado falha inesperadamente, um defeito de regressão pode ter
sido introduzido por uma alteração de código. Rodando um conjunto automatizado
de testes sempre que o novo código é verificado ajuda a garantir que os erros de
regressão serão detectados rapidamente. O feedback rápido ajuda na solução dos
problemas tendo a solução mais rápida do que o bug fosse encontrado semanas de
testes mais tarde.
49
- Os testes fornecem documentação.
É difícil manter a documentação estática atualizada, mas se os testes au-
tomatizados não forem alterados quando o sistema for alterado, os testes falharão.
É necessário corrigi-los para manter o processo de compilação funcionando. Isso
significa que os testes automatizados são sempre uma imagem precisa de como o
código funciona.
- Automação pode ser um bom retorno sobre o investimento.
A automação fornece consistência a um projeto e dá à equipe a oportuni-
dade de testar de forma diferente e empurrar os limites da aplicação. Automação
significa tempo extra para testadores e membros da equipe para se concentrar em
obter o produto certo para o mercado em tempo hábil.
Um componente importante do retorno da automação do teste é a manei-
ra como os defeitos são corrigidos. Equipes que dependem de testes manuais ten-
dem a encontrar bugs muito depois que o código que contém o bug é escrito. Eles
entram no modo de corrigir o "bug do dia", em vez de olhar para a causa raiz do pro-
blema e redesenhar o código de acordo. Quando os programadores executam o
conjunto de testes automatizados, encontram os bugs antes que o código seja verifi-
cado, dando tempo para corrigir o design. Isso é um reembolso muito maior e reduz
a dívida técnica desenvolvendo um código sólido e robusto.
5.4 TEST DRIVEN DEVELOPMENT (TDD)
É a prática de conduzir o design de código com testes. Em contraste com
o fluxo de trabalho tradicional de "escrever código - verificar código", o TDD exige
que a primeira tarefa em qualquer empreendimento de desenvolvimento seja escre-
ver um teste. Somente então o código que fará passar esse teste será escrito. Se
for fielmente aplicado, nenhum código de produção virá a existir, a menos que seja
precedido e acompanhado de pelo menos um teste [44].
O desenvolvimento orientado por teste é realizado em ciclos curtos, cada
uma consistindo no seguinte fluxo: [15]
50
1. Iniciar identificando o incremento da funcionalidade que é necessário. Isso
normalmente deve ser pequeno e implementável em algumas linhas de códi-
go.
2. Um teste para essa funcionalidade é escrita e é implementada como um teste
automatizado. Isso significa que o teste pode ser executado e relatará se ele
foi aprovado ou executado com falha.
3. Em seguida, o teste é executado juntamente com todos os outros testes que
foram implementados. Inicialmente, a funcionalidade não foi implementada
de modo que o novo teste falhará. Isso é deliberado, pois mostra que o teste
adiciona algo ao conjunto de testes.
4. Em seguida, implementar a funcionalidade e voltar a executar o teste. Isso
pode envolver a refatoração do código existente para aprimorá-lo e adicionar
novo código ao que já existe.
5. Uma vez que todos os testes sejam executados com êxito, o próximo pedaço
da funcionalidade poderá ser implementada.
Um ambiente de teste automatizado é essencial para TDD. Como o códi-
go é desenvolvido em passos muito pequenos, tem que ser capaz de executar cada
teste cada vez que adicionar uma funcionalidade ou refatorar o programa. Portanto,
os testes são incorporados em um programa separado que executa os testes e invo-
ca o sistema que está sendo testado. Usando esta abordagem, é possível executar
centenas de testes separados em poucos segundos.
Além da melhor compreensão dos problemas, outros benefícios do de-
senvolvimento orientado a teste são:
- Cobertura de código. Em princípio, cada segmento de código escrito deve ter
pelo menos um teste associado. Portanto, a confiança aumenta de que todo
o código no sistema foi realmente executado. O código é testado conforme
está escrito para que os defeitos sejam descobertos no início do processo de
desenvolvimento.
- Teste de regressão. Um conjunto de testes é desenvolvido gradualmente à
medida que um programa é desenvolvido. Os testes de regressão sempre
podem ser executados para verificar se as alterações no programa não intro-
duziram novos bugs.
51
- Depuração simplificada. Quando um teste falha, deve ser óbvio onde o pro-
blema está. O código recém-escrito precisa ser verificado e modificado. Não
é necessário usar ferramentas de depuração para localizar o problema.
- Documentação do sistema. Os próprios testes funcionam como uma forma
de documentação que descreve o que o código deve fazer. A leitura dos tes-
tes pode facilitar a compreensão do código.
5.5 PIRÂMIDE DE AUTOMAÇÃO
Figura 3: Exemplo de pirâmides de automação. [45]
É um conceito popularizado por Mike Cohn (ver Figura 3), que mostra o
que deve ser automatizado e dará maior retorno de investimento, garantindo os mai-
ores benefícios da automação [46].
A pirâmide de automação de teste ágil (a pirâmide da direita) mostra três
camadas diferentes de testes automatizados. A camada mais baixa é a fundação
que suporta as demais camadas. É composta principalmente de testes de unidade.
52
Esta camada representa a maior parte dos testes automatizados. Eles geralmente
são escritos na mesma linguagem do sistema em teste. Depois de uma equipe ter
dominado a arte de TDD, estes testes são, de longe, os mais rápidos e menos caros
para escrever. Eles fornecem o feedback mais rápido, também os tornando altamen-
te valiosos. Eles têm o maior ROI (retorno do investimento) do que qualquer tipo de
teste.
Em desenvolvimento ágil, é indicada a realização de tantos testes quanto
possível para esta camada. Se existem testes nos quais os clientes não são capa-
zes de ler, eles podem ser codificados muito mais rapidamente como testes de uni-
dade. Outros tipos de testes voltados para a tecnologia, como testes de desempe-
nho, também podem ser possíveis no nível da unidade.
A camada intermediária na pirâmide é a camada que inclui a maioria dos
testes automatizados de negócios voltados para apoiar a equipe. Estes são os tes-
tes funcionais que verificam se a construção está correta. Os testes nesta camada
podem incluir testes de "história", testes de "aceitação" e testes que cobrem conjun-
tos de funcionalidade maiores do que a camada de teste de unidade. Esses testes
operam no nível API ou "atrás da GUI", testando a funcionalidade diretamente sem
passar pela GUI. Como esses testes ignoram a camada de apresentação, eles são
menos caros de escrever e manter do que os testes que usam a interface gráfica.
O nível superior representa o que deve ser o menor esforço de automa-
ção, porque os testes geralmente oferecem o ROI mais baixo. Esses testes são os
feitos através da GUI, os que realmente operam e manipulam a camada de apresen-
tação. Eles são escritos depois que o código é concluído, e por isso são geralmente
escritos para criticar o produto e ir diretamente para o conjunto de regressão.
Estes testes são tradicionalmente mais caros para escrever, embora haja
novas ferramentas que ajudam a reduzir o investimento necessário. Como os com-
ponentes da interface do usuário tendem a ser alterados frequentemente, esses tes-
tes são muito mais frágeis do que os testes que funcionam em nível funcional ou de
unidade.
Não importa quantos testes automatizados se tenha, a maioria dos siste-
mas também precisa de atividades de testes manuais, como testes exploratórios e
testes de aceitação do usuário. Esses testes não podem ser esquecidos e estão
ilustrados na Figura 3 como um círculo na ponta da pirâmide. A maior parte dos tes-
53
tes de regressão deve ser automatizada ou os testes manuais não darão um bom
retorno sobre o investimento.
A abordagem de pirâmide é uma maneira mais forte, mais benéfica e
econômica de implementar a automação de teste do que colocar o foco em testes
GUI automatizados e inadvertidamente seguindo o anti-padrão “Ice Cream Cone5”
(cone de sorvete), sendo representado pela pirâmide da esquerda na Figura 3. A
pirâmide fornece um forte teste de unidade na base de teste para a partir dela cons-
truir testes adicionais nas fases de integração e UI (Interface do Usuário) enquanto a
abordagem é mais pesada e menos estável. É imperativo seguir a pirâmide de tes-
tes de automação para produzir o melhor software de qualidade possível [43].
Sabendo-se o que deve ser automatizado, mostrado pelas camadas da
pirâmide de automação, será detalhado cada camada nos próximos itens.
5.5.1 Camada base: Testes de Unidade
Um teste de unidade é um pedaço de código que testa uma unidade de
trabalho, um método, uma classe ou um conjunto de classes que implementam uma
única operação lógica, que é acessível por meio de uma interface pública [44].
Quando testar classes de objeto, os testes devem ser projetados para
fornecer cobertura de todos os recursos do objeto. Isso significa que deve [15]:
- Testar todas as operações associadas ao objeto.
- Definir e verificar o valor de todos os atributos associados ao objeto.
- Colocar o objeto em todos os estados possíveis. Isso significa que todos os
eventos que causam uma alteração de estado devem ser simulados.
Os testes unitários possuem as seguintes propriedades:
5 Anti-padrão causado com a inversão da pirâmide, se parecendo com um cone de sorvete, onde o foco principal são os testes de UI. Nesse modo são quase inexistentes os testes de unidade. Testes através da interface do usuário são lentos, aumentando os tempos de compilação e o mais importan-te, esses testes são muito frágeis. Um aprimoramento para o sistema pode facilmente acabar que-brando muitos desses testes, que, em seguida, tem que ser reescritos. Resumindo, esse tipo de teste é muito caro e pode minar a confiança do time e dos usuários.
54
- Eles são totalmente automatizados. Os testes de unidade podem ser execu-
tados com o mínimo esforço seja através de um IDE, um script de compilação
ou usando uma ferramenta especializada. Programas de driver que são exe-
cutados manualmente não são testes de unidade.
- Eles são auto verificados. A unidade de teste não executa apenas código.
Ele verifica que o código se comporta como esperado (pelo teste) e comunica
esse resultado.
- Eles são repetitivos e consistentes. Eles fornecem as mesmas entradas e
esperam os mesmos resultados para cada execução e podem ser executados
quantas vezes forem necessárias.
- Eles testam um único conceito lógico. Um teste de unidade deve verificar
apenas uma coisa sobre o código testado.
- Eles devem ser isolados. Um teste não é um teste unitário se fala com o ban-
co de dados, se comunica através da rede, acessa o sistema de arquivos,
não pode ser executado ao mesmo tempo que qualquer um dos seus outros
testes unitários, ou realizar configurações especiais de ambiente (como a edi-
ção de arquivos de configuração) para executá-lo.
- Eles são rápidos. Um teste de unidade leva alguns milissegundos para ser
executado. Um conjunto completo de milhares desses testes leva no máximo
alguns minutos. Juntamente com a exigência de isolamento, isso desqualifica
os testes que exigem o acesso a recursos lentos, como redes e bancos de
dados, e testes algoritmicamente complexos.
5.5.2 Ferramentas para Teste de Unidade
Para a realização dos testes de unidade, existem inúmeras ferramentas
no mercado e a escolha de qual será utilizada, dependerá principalmente da lingua-
gem de programação utilizada para implementar os testes. Seguem algumas opções:
- xUnit.net. É uma ferramenta de teste de unidade de código aberto, centrada
na comunidade para o .NET Framework. Escrito pelo inventor original do
NUnit v2, xUnit.net é a mais recente tecnologia para testar unidades C#, F#,
55
VB.NET e outros idiomas .NET. XUnit.net funciona com ReSharper, Code-
Rush, TestDriven.NET e Xamarin. É parte da Fundação .NET, e opera sob seu
código de conduta e licenciado sob o Apache 2 [47].
- JUnit. É uma ferramenta de código aberto, criado por Erich Gamma e Kent
Beck, com suporte à criação de testes automatizados na linguagem de pro-
gramação Java [48].
5.5.3 Cobertura de Código no teste de unidade
Ferramentas de cobertura de código fornecem uma boa assistência na
escrita, elaboração e manutenção dos testes. Eles mostram graficamente qual códi-
go foi executado nos testes, permitindo saber se o código pretendido foi atingido e
qual código ainda não foi coberto.
Cobertura de código de nenhuma maneira lhe dá uma compreensão exa-
ustiva de como você exercitou seu código. Ela é um guia, não um objetivo. Cober-
tura ajuda a escrever os testes certos para exercer os caminhos de execução sintá-
tica do código. Da mesma forma, a qualidade dos testes escrita depende da habili-
dade e atenção aplicada à tarefa de escrevê-los. A cobertura tem pouco poder para
detectar testes acidentalmente ou deliberadamente de má qualidade.
Use a métrica da cobertura de código como um indicador para identificar
as classes que estão com baixa cobertura. Essas classes podem indicar futuros
problemas, pois não foi alcançado um bom nível de teste.
Não há um número mágico para identificar o quanto de cobertura de teste
é necessário. Conforme dito pelo autor Stephen Vance a partir de 50% de cobertura
já possui um retorno de qualidade, tornado mais significativo quando chega as 80%
[49].
56
5.6 CAMADA INTERMEDIÁRIA: ACEITAÇÃO, INTEGRAÇÃO, TESTE DE COM-
PONENTE E API
A abordagem do BDD (Behavior-Driven Development) é muito comum
para testes de aceitação e integração, fornecendo uma linguagem não técnica para
escrever testes. Será abordada com detalhes a seguir.
5.6.1 Behavior-Driven Development (BDD)
São um conjunto de práticas de engenharia de software projetado para
ajudar as equipes a construir e entregar software valiosos e de maior qualidade mais
rápido. Baseia-se em práticas ágeis e enxutas, incluindo, em particular, Test-Driven
Development (TDD) e Domain-Driven (DDD). Mas o mais importante é que o BDD
fornece uma linguagem comum baseada em frases simples e estruturadas expres-
sas em inglês (ou na língua nativa das partes interessadas) que facilitam a comuni-
cação entre os membros da equipe do projeto e as partes interessadas do negócio.
O BDD ajuda as equipes a concentrar seus esforços na identificação,
compreensão e construção de recursos valiosos que importam para as empresas, e
garante que esses recursos sejam bem projetados e bem implementados.
Os praticantes do BDD usam conversas em torno de exemplos concretos
de comportamento do sistema para ajudar a entender como os recursos fornecerão
valor ao negócio. O BDD encoraja os analistas de negócios, desenvolvedores de
software e testadores a colaborar mais estreitamente, permitindo que eles expres-
sem os requisitos de uma forma mais testável, de uma forma que tanto a equipe de
desenvolvimento quanto os responsáveis pelo negócio possam entender facilmente.
As ferramentas do BDD podem ajudar a transformar esses requisitos em testes au-
tomatizados que ajudam a orientar o desenvolvedor, verificar o recurso e documen-
tar o que o aplicativo faz.
57
O BDD não é uma metodologia de desenvolvimento de software por si só.
Não é uma substituição para Scrum [50], XP [51] e RUP [52], ou qualquer metodolo-
gia usada atualmente, incorporando, desenvolvendo e aprimorando ideias de muitas
dessas metodologias.
Apresenta os seguintes benefícios [53]:
- Reduzir Desperdício: BDD é sobre como concentrar o esforço de desenvol-
vimento na descoberta e entrega dos recursos que fornecerão valor de negó-
cios e evitar aqueles que não. Quando uma equipe cria um recurso que não
está alinhado com os objetivos de negócios subjacentes ao projeto, o esforço
é desperdiçado para o negócio. Da mesma forma, quando uma equipe es-
creve um recurso que o negócio precisa, mas de uma forma que não é útil pa-
ra o negócio, a equipe terá que retrabalhar o recurso para caber na conta, re-
sultando em mais desperdícios. O BDD ajuda a evitar esse tipo de esforço
desperdiçado, ajudando as equipes a se concentrar em recursos que estão
alinhados com os objetivos de negócios.
- Reduzir custos: A consequência direta desse desperdício reduzido é a redu-
ção de custos. Ao concentrar-se na construção de recursos com valor de ne-
gócio demonstrável (construção do software certo) e não desperdiçar esforços
em recursos de pouco valor, reduzirá o custo de fornecer um produto viável
para os usuários. E, melhorando a qualidade do código do aplicativo, reduzi-
rá o número de bugs e, portanto, o custo de corrigir esses bugs, bem como o
custo associado aos atrasos que esses bugs causariam.
- O BDD torna consideravelmente mais fácil alterar e estender os aplicativos. A
documentação viva é gerada a partir das especificações executáveis usando
termos com os quais as partes interessadas estão familiarizadas. Isso torna
muito mais fácil para as partes interessadas entender o que a aplicação real-
mente faz. As especificações executáveis de baixo nível também atuam co-
mo documentação técnica para os desenvolvedores, facilitando a compreen-
são da base de código existente e a realização de suas próprias alterações.
- Liberação rápida: As práticas do BDD produzem um conjunto abrangente de
testes de unidade e aceitação automatizados, o que reduz o risco de regres-
sões causadas por quaisquer novas alterações ao aplicativo. Estes abran-
58
gentes testes automatizados também aceleram o ciclo de lançamento consi-
deravelmente. Testadores não são mais necessários para realizar longas
sessões de testes manuais antes de cada nova versão. Em vez disso, eles
podem usar os testes de aceitação automatizados como ponto de partida e
gastar seu tempo de forma mais produtivo e eficiente em testes exploratórios
e outros testes manuais não triviais.
Exemplo:
Conceitualmente falando, Critérios de Aceite são requisitos necessários
para que uma funcionalidade possa ser considerada completa. Não existem limites
de critérios de aceite nem regras de como eles devem ser descritos, o fundamental é
que todos eles representem um comportamento testável que agregue valor para o
cliente.
Para o bom entendimento do BDD, é fundamental entender como são es-
truturados os critérios de aceite. Para ilustrar esta estrutura, um exemplo de User
Story (História de Usuário) será apresentado conforme a Figura 4. Nesse exemplo,
o papel, o objetivo e a razão de uma funcionalidade serão descritos.
Feature: <description of the feature>
As a <user/actor>
I want <goal to be achieved>
so that <the reason you want to achieve the goal>
Figura 4: Exemplo de formato de user story no BDD
Cada story (funcionalidade) recebe um ou mais Acceptance Criterias (Cri-
térios de Aceite), o que são um substituto dos casos de teste tradicionais que muitos
testadores estão acostumados. São descritos na Figura 5: Exemplo de formato de
critério de aceitação no BDD:
Scenario: <description of the test>
Given <a known state>
When <an event occurs>
59
Then <then this should happen>
Figura 5: Exemplo de formato de critério de aceitação no BDD
O modelo acima traduzido para o português seria algo como:
Funcionalidade: <descrição da funcionalidade>
Como um <usuário/ator>
Eu quero <meta a ser alcançada>
De modo que <a razão para alcançar a meta>
Cenário: <descrição do teste>
Dado <um estado conhecido>
Quando <um determinado evento ocorre>
Então <isso deve ocorrer> [54]
Um exemplo bem simples de história de uma aplicação de vendas utilizando os for-
matos da Figura 4 e Figura 5 poderia ser: “Venda de balas na doceria”. Demonstrado
pelo exemplo (1).
#language: pt-br
Funcionalidade: Vender doces
Para quando um doce for vendido
Eu, como vendedor
Desejo decrementar um item no estoque
Cenário: Baixa 1 bala do estoque
Dado que cliente pede 1 bala
E tenho 10 balas em estoque
Quando ele realiza a compra
Então eu fico com 9 balas em estoque [55]
(1)
60
5.6.2 Ferramentas para escrever BDD
- SpecFlow. Ferramenta de código aberto e fornecido sob uma licença BSD,
usando o parser oficial Gherkin e suporta a estrutura .NET, Xamarin e Mono.
O SpecFlow se integra com o Visual Studio, mas também pode ser usado a
partir da linha de comando (por exemplo, em um servidor de compilação). O
SpecFlow suporta outros populares frameworks de teste: MSTest, NUnit (2 e
3), xUnit 2 e MbUnit. Use SpecFlow para definir, gerenciar e executar auto-
maticamente testes de aceitação legível em projetos .NET [56].
- RSpec. É uma ferramenta de teste para Ruby, criada para desenvolvimento
orientado por comportamento (BDD). É a biblioteca de testes mais utilizada
para Ruby em aplicações de produção. Mesmo que tenha um rico e poderoso
DSL (Domínio de Linguagem Específica), o seu núcleo é uma ferramenta
simples com uma pequena curva de aprendizado [57].
5.6.3 Teste de API e componentes
Uma API (Interface de Programação de Aplicativo) é uma coleção de fun-
ções que podem ser executadas por outros aplicativos de software ou componentes.
O usuário final geralmente nunca sabe que existe uma API, interagindo apenas com
a interface final.
Cada chamada de API tem uma função específica com um número de
parâmetros que aceitam entradas diferentes. Cada variação retornará um resultado
diferente. Cada chamada pode ser desenvolvida cedo no ciclo de vida de uma apli-
cação, o que significa que o teste também pode ocorrer cedo junto com o desenvol-
vimento. Testar através de uma API pode trazer confiança ao sistema antes de tes-
tes complicados da camada de UI.
61
5.6.4 Ferramentas de Teste para API e componentes
- Fit (Framework for Integrated Tests): É um framework de teste de código
aberto que promove a colaboração, o que o torna uma boa ferramenta para
ajudar a refinar os requisitos. O Fit permite que os clientes, testadores e pro-
gramadores usem exemplos para especificar o que esperam que o sistema
faça. Quando os testes são executados, o Fit compara automaticamente as
expectativas dos clientes com os resultados reais. Com o Fit, os clientes po-
dem fornecer orientação usando os seus conhecimentos especializados para
definir os exemplos que os programadores podem codificar contra. Os pro-
gramadores participam escrevendo os textos que fazem os cheques reais
contra os exemplos. Esses arquivos usam os dados especificados nos exem-
plos para serem executados com o programa real [58].
- FitNesse: É um servidor web, uma wiki e uma ferramenta de teste de software
baseada no Fit. Originalmente desenvolvido por Robert C. "Tio Bob" Martin e
Micah Martin, é uma ferramenta de código aberto com uma comunidade de
desenvolvedores ativos. A principal diferença entre FitNesse e Fit é que os
testes FitNesse são escritos em marcação wiki em vez de tabelas HTML, o
que alguns usuários acham mais fácil. Ele também suporta a criação de tes-
tes em planilhas e importá-las para os testes [59].
Outro benefício de um tipo de ferramenta Fit ou FitNesse é que ele pro-
move a colaboração entre diferentes membros da equipe, a fim de chegar aos testes
certos para orientar o desenvolvimento. Clientes, programadores, testadores e ou-
tros trabalham juntos para especificar e automatizar os testes.
62
5.6.5 Web Services (Serviços Web)
Os serviços da Web são uma arquitetura baseada em serviços que forne-
cem uma interface externa para que outros possam acessar o sistema. São seme-
lhantes à API, mas são acessíveis através de protocolos HTTP.
5.6.6 Ferramentas de Teste WebService
As mesmas ferramentas de teste de API poderiam ser utilizadas. Porém,
existem outras ferramentas especializadas em serviços Web que poupam bastante
trabalho no momento de escrever o teste.
- SOAPSonar: É um exemplo de uma ferramenta para testar serviços da web.
Basta fornecer o WSDL (Web Services Description Language); SOAPSonar
compila a página e, em seguida, apresenta-lhe um menu com guias que con-
tém caixas de texto para preencher. Possui um modo de execução que per-
mite adicionar os testes a um conjunto e, em seguida, executa-los [60].
- SoapUI: Tem uma boa curva de aprendizado, mas pode ser usado para testes
de desempenho, carga e segurança. Como ele pode executar um loop de li-
nhas em uma planilha do Excel ou em um arquivo de texto, ele pode ser usa-
do para testes de dados [61].
5.7 CAMADA SUPERIOR: TESTE DE UI
Nos últimos anos, a comunidade ágil veio com padrões e práticas mais
eficazes para projetar testes automatizados que entregam um grande ROI. Isso é
63
verdadeiro para todos os níveis de automação, incluindo os testes de regressão que
são executados através da interface do usuário.
Automatizar os testes que exercem a interface do usuário da aplicação
continua a apresentar os desafios mais difíceis. Muitas equipes têm investido tempo
e dinheiro para automatizar testes de interface do usuário, apenas para encontrar o
um bom custo de manutenção a longo prazo [62].
5.7.1 Ferramentas para Teste de UI
Estas ferramentas fornecem uma variedade de maneiras de verificar a
navegação correta e o conteúdo das páginas, como etapas específicas de verifica-
ção de ferramenta ou XPath. Algumas dessas ferramentas têm uma curva de
aprendizado mais alta do que as ferramentas simples de gravação/reprodução, mas
o investimento extra de tempo geralmente compensa em scripts com um baixo custo
total de propriedade.
Implementar uma nova ferramenta de automação de teste geralmente
requer alguma experimentação para obter um bom equilíbrio de código testável e
scripts de teste bem projetados. Envolver toda a equipe torna isso muito mais fácil.
- Watir: É uma biblioteca de Ruby de código aberto simples para automatizar
navegadores da Web que funciona com o Internet Explorer no Windows. Exis-
tem diferentes plug-ins para outros navegadores, incluindo FireWatir para
Firefox e SafariWatir para Safari [63].
- Selenium. É um conjunto de ferramentas de código aberto para testes de
aplicações Web. Os testes podem ser escritos como tabelas HTML ou codifi-
cados em várias linguagens de programação e scripts populares, incluindo
Java, C # e Ruby, e podem ser executados diretamente na maioria dos nave-
gadores modernos. Um plug-in do Firefox chamado "Selenium IDE" fornece
uma maneira de aprender a ferramenta rapidamente. Um gravador é forneci-
do para ajudar a criar os testes, incluindo asserções de escrita [64].
64
6 MÉTODOS FORMAIS
Métodos formais incluem todas as abordagens de análise de software ba-
seadas em matemática e lógica, incluindo análise, verificação de tipo, desenvolvi-
mento baseado em modelo e correção por construção. Métodos formais podem aju-
dar desenvolvedores de software a obterem maior garantia de que classes inteiras
não possuem vulnerabilidades, e também podem ajudar a reduzir ciclos imprevisí-
veis de testes caros e correção de erros [65].
Existem vários tipos de métodos formais. Aqueles que serão apresenta-
dos a seguir possuem o maior impacto na detecção e correção de defeitos e vulne-
rabilidades.
6.1 ANÁLISE ESTÁTICA
O termo análise estática refere-se a qualquer processo para avaliar o có-
digo sem executá-lo. A análise estática é poderosa porque permite a rápida conside-
ração de muitas possibilidades. Uma ferramenta de análise estática pode explorar
um grande número de cenários sem ter que passar por todos os cálculos necessá-
rios para executar o código para todos os cenários. A análise estática é particular-
mente adequada à segurança porque muitos problemas de segurança ocorrem em
casos e estados diferenciados, que podem ser difíceis de serem alcançados pela
execução do código. Boas ferramentas de análise estática fornecem uma maneira
rápida de obter uma avaliação consistente e detalhada do código [66].
As métricas estáticas de análise de código são métricas técnicas. É pos-
sível detectar problemas ou potenciais problemas no software analisando o código
fonte, sem compilar ou executar. As informações disponibilizadas por esses tipos de
ferramenta podem fornecer uma riqueza de informações sobre a qualidade do códi-
go automaticamente. Com base nesse feedback, a equipe pode tomar medidas pa-
ra melhorar a qualidade.
65
A razão para se preocupar com a qualidade do código é que o código
complicado e confuso é difícil de modificar com segurança. Isso faz com que cada
aprimoramento de recursos ou manutenção demore mais tempo e custe mais dinhei-
ro do que o necessário, além dos defeitos que a alteração possa causar. O acúmulo
de problemas técnicos ao longo do tempo é chamado de dívida técnica ou dívida de
design, e é uma das principais causas de desaceleração no desenvolvimento e de
morte prematura dos sistemas de produção.
As ferramentas estáticas de análise de código geralmente se concentram
em sete áreas-chave, conhecidas como os sete eixos de qualidade do código ou os
sete pecados mortais dos programadores [67]:
- Complexidade. As ferramentas de análise de código estáticas procuram um
par de tipos de complexidade. A primeira é a complexidade ciclomática, que
verifica a profundidade das instruções condicionais aninhadas no código. Um
valor alto para a complexidade ciclomática pode significar que o código é difí-
cil de entender e, portanto, difícil de modificar com segurança. Em alguns ca-
sos, isso só pode significar que a seção de código em questão é complicada.
O segundo tipo de complexidade é conhecido como “Resposta para a Classe”
(RFC), e se aplica principalmente a linguagens de programação orientada a
objetos. O algoritmo é baseado na contagem do número total de chamadas
de método e o número de chamadas de método exclusivo em uma classe. A
complexidade excessiva de qualquer tipo pode tornar o código difícil de en-
tender, bem como demorado e arriscado para mudar.
- Duplicação. Fragmentos de código idêntico em vários locais na base de có-
digo. Também pode haver formas menos óbvias de duplicação, como fun-
ções ou métodos que executam quase o mesmo processamento e que dife-
rem apenas de maneira superficial, ou utilitários em diferentes pacotes de ter-
ceiros que executam as mesmas funções ou classes em pacotes diferentes
que tem responsabilidades semelhantes ou sobrepostas.
- Cobertura de teste. Os testes unitários são o conjunto de testes automatiza-
dos mais finos aplicados a uma base de código. As ferramentas de análise
de código estático podem facilmente verificar a cobertura de código no nível
da unidade. A cobertura adequada depende do nível de validação incorpora-
do na própria linguagem de programação e da quantidade de código que po-
66
de ser gerada automaticamente por ferramentas de desenvolvimento. Cem
por cento de cobertura de teste não é geralmente necessário ou desejável,
mas em geral uma cobertura mais elevada é melhor do que uma cobertura
mais baixa.
- Padrões de codificação. Uma base de código que geralmente segue as
mesmas convenções para nomes e estrutura tenderá a ser menos propensa a
erros e mais fácil de manter do que uma base de código que exibe um misto
de diferentes convenções e estilos.
- Comentários. Código que inclui comentários de fonte excessiva pode ser
confuso, tanto por causa da desorganização geral criada pelos comentários,
quanto porque os comentários tendem a ficar rapidamente fora de sincronia
com o código que descrevem. Por outro lado, há ocasiões em que comentá-
rios explicativos ajudam as pessoas a entender a intenção do código ou aler-
tam sobre possíveis efeitos colaterais quando o código é modificado. Ferra-
mentas de análise de código estático podem aplicar heurísticas para avisá-lo
quando parece que há muitos ou muito poucos comentários no código-fonte.
As linhas de origem que são comentadas e deixadas no local também são
questionáveis.
- Bugs potenciais. Algumas ferramentas estáticas de análise de código procu-
ram padrões estruturais no código que podem levar a problemas previsíveis
em termos de manutenção, segurança, testabilidade eficiência, confiabilidade,
portabilidade e fatores semelhantes. Estes podem fornecer alertas úteis de
problemas potenciais.
- Problemas de arquitetura. Padrões estruturais no código podem apontar para
problemas de arquitetura. A análise estática de código pode identificar os ar-
quivos de código-fonte que têm os problemas estruturais mais graves. Isso
ajuda a equipe a entender quais arquivos são dignos de atenção para refato-
ração para reduzir a dívida técnica. Em aplicações grandes e complicadas
que compreendem múltiplos componentes implementáveis separadamente,
as dependências circulares ou cíclicas entre componentes podem levar a sé-
rios problemas na construção e implantação da aplicação. Isso significa que
um módulo no componente A tem uma dependência de um módulo no com-
ponente B e um módulo diferente no componente B tem uma dependência em
67
algum outro módulo no componente A. Um programador trabalhando em um
pequeno subconjunto da base de código pode ignorar esse tipo de problema,
mas as ferramentas de análise estáticas de código podem indicar o problema.
Algumas ferramentas podem validar a estrutura da base de código contra um
conjunto de restrições de arquitetura definidas pela equipe. Isso pode prote-
ger contra o acesso inadequado em diferentes camadas ou níveis da arquite-
tura no aplicativo. As dependências entre os componentes do aplicativo e bi-
bliotecas de terceiros podem ser detectadas e relatadas por ferramentas está-
ticas de análise de código. Essas informações podem ajudar as equipes a
identificar possíveis oportunidades de melhoria da qualidade do código, bem
como a exposição a bugs relatados em bibliotecas.
Uma diretriz geral para o bom design de software é lutar pela alta coesão
e baixo acoplamento. Os dois variam geralmente inversamente um em relação
aos outros. Acoplamento refere-se ao grau em que diferentes módulos depen-
dem uns dos outros, ou devem saber uns sobre os outros. A regra de ouro sobre
alta coesão e baixo acoplamento se aplica a qualquer código-fonte e não é espe-
cífico para qualquer paradigma de programação. Ferramentas estáticas de análi-
se de código podem procurar padrões estruturais que sugerem alto acoplamento
[67].
6.1.1 Ferramentas para análise estática
Existem bastantes ferramentas no mercado para realizar análise estática,
mas o mais popular é o SonarQube [68].
Sonar é uma plataforma de qualidade de código livre e aberto. Ele coleta
e analisa o código fonte, medindo a qualidade e fornecendo relatórios para os proje-
tos. Ele combina ferramentas de análise estática e dinâmica e permite que a quali-
dade seja medida continuamente ao longo do tempo. Mais de 600 regras de código
são incorporadas na plataforma, verificando o código de diferentes perspectivas.
68
As regras são separadas em grupos lógicos diferentes e cada uma contri-
bui em um nível diferente para a qualidade geral do projeto no caso. Os resultados
da análise, as violações de código e os dados históricos estão disponíveis e acessí-
veis através de uma interface de usuário, composta por diferentes componentes,
cada um servindo e atendendo a diferentes necessidades e escopos.
A plataforma analisa o código fonte de diferentes aspectos. Para conse-
guir isso, o Sonar detalha o código, camada por camada, passando do nível do mó-
dulo para o nível da classe. Em cada nível, a Sonar realiza análise estática e dinâ-
mica produzindo valores métricos e estatísticas, revelando áreas problemáticas na
fonte que requerem inspeção ou melhoria. A análise não é um procedimento monolí-
tico, mas examina o código de diferentes perspectivas, introduzindo o conceito de
eixos de qualidade. Os resultados são então interpretados e consolidados em um
painel informativo, exemplificado pela Figura 6, permitindo formar uma opinião sobre
o código defeituoso e testes de qualidade sobre os projetos [69].
69
Figura 6: Exemplo do painel do SonarQube [70]
O Sonar trabalha visando os “Sete Eixos de Qualidade” exibindo em cada
widget. Os mais importantes para este trabalho serão mostrados nas seções a se-
guir.
6.1.2 Bugs potenciais e regras de código
Como parte da análise, o SonarQube compara seu código a um conjunto
de regras. Quando uma regra é quebrada, um problema é marcado com a linha on-
de ocorreu. Os problemas são relatados em uma das cinco severidades: Blocker,
Critical, Major, Minor e Info, exemplificados pela Figura 7. O nível de seriedade está
indicado em ordem decrescente.
70
Figura 7: Exemplo do widget de bugs potencias e regras de código
Basicamente o SonarQube quebra os problemas em seis categorias ge-
rais que são categorizados nas severidades. Segue a lista pela ordem de importân-
cia:
- Bugs: Problemas na categoria de bugs são garantidos por serem um proble-
ma.
- Bugs potenciais: como acontece com bugs, representam problemas reais no
código. Muitas vezes, porém, eles são problemas condicionais, aqueles que
só acontecerão em algum estado ou condição específica. Problemas de se-
gurança também estão nesta categoria.
- Indicações de erro (potencial) do programador: ao contrário das duas primei-
ras categorias, não é garantido que irão causar problemas.
- Ineficiências: Os problemas nesta categoria não farão com que o programa
deixe de executar corretamente e nem levará a problemas futuros. Porém, fa-
rão com que o programa rode com pior desempenho.
- Incoerências no estilo: O programa listará os problemas de estilo de codifica-
ção, baseando-se nas regras configuradas. Por serem deixados para o final,
não quer dizer que não sejam importantes. Estes problemas podem tornar o
código difícil de manter e favorecer a introdução de bugs (devido à baixa legi-
bilidade).
71
6.1.3 Cobertura e testes
Neste widget, exemplificado pela Figura 8, são exibidos a cobertura dos
testes unitários e métricas dos testes, mostrando a quantidade de testes na aplica-
ção, quantos estão com erro, quantos não foram executados e o tempo total de exe-
cução dos testes.
Figura 8: Exemplo do widget de cobertura e testes
Conforme mencionado anteriormente, a cobertura de código serve como
um guia para a qualidade do software. Baixa cobertura poderá indicar instabilidade
com o programa, devido a cenários que não foram testados.
6.1.4 Comentário
Existem dois tipos principais de comentários de código: na linha em qual-
quer método (público ou privado) que se destinam dar alguns detalhes da lógica do
código, e os fora de um método público que se destinam a comunicar como e por
que usá-los (os comentários da API). O primeiro tipo é muitas vezes referido como
um “code smell6”. Esse tipo de comentário tende a ficar obsoleto, a lógica muda,
mas os comentários não ou os comentários ficam separados do que eles se referem.
6 É um termo usado para evidenciar que algo no código não faz sentido (falta de coesão).
72
O segundo tipo de comentário (a documentação da API) é o que é medido pelo So-
nar.
Esta é uma métrica de manutenção. Ela olha para quantas vezes o cha-
mador do método lerá o código para entender o que está implementado, em compa-
ração com a documentação intencional que (idealmente) explica o que deve ser
passado, o que será devolvido e talvez até mesmo o que acontecer entre isso.
Comentários são medidos porque eles são parte do que torna um sistema
fácil (ou não) para trabalhar. A sua falta pode ocasionar um mal-uso da API, deixan-
do a aplicação em um estado inesperado. Eles são medidos com a ideia de que os
programadores devem gastar seu tempo escrevendo os sistemas que os seus usuá-
rios precisam, não tentando descobrir o que a última pessoa pensou que estava fa-
zendo quando codificou o método.
Figura 9: Exemplo do widget de comentário
O Sonar, apresentado na Figura 9, mostra a quantidade absoluta de li-
nhas comentadas, percentual da API que foi documentada, quantidade de linhas da
API pública que não foram comentadas e o percentual de linhas comentadas basea-
do na quantidade de comentários e linhas de código.
6.1.5 Duplicação
Do not Repeat Yourself (DRY) é um princípio de engenharia de software
que deve ser aplicado a todos os aspectos de um projeto de software. Concentra-se
em minimizar ou eliminar duplicações entre os recursos de um sistema, especial-
mente no código.
73
Um dos conceitos básicos do DRY é que é mais eficiente e produtivo
manter uma única cópia de cada recurso do que manter várias cópias. Isso é por-
que ter várias cópias de qualquer coisa, quer dados ou algoritmos, não só significa
mais trabalho quando há mudanças, mas também pode significar que pode acabar
com algumas cópias desatualizadas, que é o efeito colateral mais perigoso da dupli-
cação.
Código duplicado é responsável por muitos bugs, e pode ter grandes im-
pactos especialmente em sistemas que estão em constante evolução para refletir as
necessidades do mercado.
Figura 10: Exemplo do widget de duplicação de código
O Sonar, conforme mostra a Figura 10, mostra a quantidade absoluta de
linhas envolvida em duplicação, quantidade absoluta de blocos de código duplicados,
o número absoluto de arquivos fontes que contém linhas duplicadas e o percentual
de linhas duplicadas de acordo com o número total de linhas do projeto.
6.1.6 Arquitetura e design
O widget de design do pacote, exemplificado pela Figura 11, mostra o
quão limpa está implementação de design, dando-lhe números de alto nível que in-
dicam quanto trabalho precisa ser feito para tornar a implementação mais limpa com
um bom design.
74
Figura 11: Exemplo do widget de arquitetura e design
Para entender melhor, imagine uma pequena aplicação dividida em três
bibliotecas. Ao longo do tempo, cada biblioteca evoluiu para depender das demais,
criando, assim, ciclos. Em certo ponto não se pode atualizar uma biblioteca sem
alterar também as outras. Devido aos ciclos de dependência, mudanças simples
que devem ser fáceis levam três vezes mais tempo para serem concluídas.
Detectar esse tipo de dependência circular no nível da biblioteca é o foco
desse widget, mostrado na Figura 11,. Ele é dividido em duas partes. No lado es-
querdo, apresenta uma métrica denominada Package Tangle Index e o número de
ciclos de pacotes detectados durante a análise. À direita, o widget relata dependên-
cias indesejadas entre pacotes e arquivos. Estas são as dependências que devem
ser cortadas para remover os ciclos de bibliotecas. Para a imagem exibida, seria
necessário corrigir 35 dependências de bibliotecas envolvendo 51 arquivos para lim-
par seus ciclos de dependência.
Essa métrica indica o quanto será difícil entender e manter o código no
futuro. Os ciclos de dependência podem diminuir a produtividade, a manutenção e a
compatibilidade do projeto.
6.1.7 Complexibilidade
O Sonar reporta a complexidade ciclomática que essencialmente trata de
quantos pares de chaves (reais ou implícitas) o método tem. A premissa deste indi-
cador principal é que quanto mais pares de chaves existirem, mais complexa será a
lógica. E quanto mais complexa é a lógica, mais difícil é entender e manter.
75
Figura 12: Exemplo do widget de complexibilidade
Os números no widget, exemplificado pela Figura 12, são médias: com-
plexidade média por método, classe e arquivo. O número na parte inferior é a com-
plexidade geral do aplicativo, que é a soma das partes. O gráfico no lado direito do
widget mostra a distribuição de complexidade entre métodos ou arquivos, com base
na opção selecionada.
Um alto nível de complexidade diminui a capacidade de manutenção de
um arquivo e aumenta o tempo necessário para entender o código. Além disso, a
alta complexidade torna difícil testar um método de forma adequada [71].
6.2 SEGURANÇA
A análise estática é bem adequada para identificar problemas de segu-
rança por uma série de razões:
- As ferramentas de análise estática aplicam as verificações cuidadosamente e
consistentemente, sem qualquer preconceito que um programador possa ter
sobre quais pedaços de código são "interessantes" sob a perspectiva de se-
gurança ou quais pedaços de código são fáceis de exercer através de testes
dinâmicos.
- Ao examinar o código em si, as ferramentas de análise estática muitas vezes
podem apontar para a causa raiz de um problema, e não apenas um de seus
sintomas. Isto é particularmente importante para garantir que as vulnerabili-
dades sejam corrigidas corretamente.
76
- A análise estática pode encontrar erros no início do desenvolvimento, mesmo
antes de o programa ser executado pela primeira vez. Encontrar um erro ce-
do não só reduz o custo de corrigir o erro, mas o ciclo de feedback rápido po-
de ajudar a orientar o trabalho de um programador. Um programador tem a
oportunidade de corrigir erros que não estava ciente anteriormente que pode-
rá acontecer. Os cenários de ataque e as informações sobre as construções
de código usadas por uma ferramenta de análise estática agem como um
meio de transferência de conhecimento.
- Quando um pesquisador de segurança descobre uma nova variedade de ata-
ques, as ferramentas de análise estática facilitam a verificação de um grande
corpo de código para ver onde o novo ataque pode ser bem-sucedido. Alguns
defeitos de segurança existem no software por anos antes de serem desco-
bertos, o que torna a capacidade de rever o código herdado para os tipos de
defeitos recentemente descobertos de valor inestimável.
A reclamação mais comum contra as ferramentas de análise estática que
visam a segurança é que produzem muitos falsos positivos, também conhecidos
como falsos alarmes. Neste contexto, um falso positivo é um problema relatado em
um programa quando nenhum problema realmente existe. Um grande número de
falsos positivos pode causar uma grande dificuldade. Não só caminhar através de
uma longa lista de falsos positivos é cansativo, faz com que um programador possa
ignorar resultados importantes que estão nela.
Falso positivos são certamente indesejáveis, mas a partir de uma pers-
pectiva de segurança, falsos negativos são muito piores. Com um falso negativo,
existe um problema no programa, mas a ferramenta não o denúncia. A penalidade
para um falso positivo é a quantidade de tempo desperdiçado ao rever o resultado.
A penalidade para um falso negativo é muito maior. Não só porque pagará o preço
associado por ter uma vulnerabilidade no código, mas viverá com uma falsa sensa-
ção de segurança decorrente do fato de que a ferramenta fez parecer que tudo esta-
va bem.
Todas as ferramentas de análise estática são garantidas para produzir
alguns falsos positivos ou alguns falsos negativos. O equilíbrio que uma ferramenta
atinge entre falsos positivos e falsos negativos é muitas vezes indicativo do propósi-
77
to da ferramenta. O equilíbrio certo é bastante diferente para as ferramentas de
análise estática que se destinam a detectar bugs diversos e ferramentas de análise
estática que visam especificamente defeitos relevantes para a segurança. O custo
de não detectar um defeito é pequeno, porque várias técnicas e processos podem
ser aplicados para se certificar de que os erros mais importantes são capturados.
Por esta razão, as ferramentas de qualidade de código geralmente tentam produzir
um baixo número de falsos positivos e estão mais dispostas a aceitar falsos negati-
vos. Segurança é uma história diferente. A penalidade para bugs de segurança ig-
norados é alta, então as ferramentas de segurança geralmente produzem mais fal-
sos positivos para minimizar os falsos negativos.
Na prática, o importante é que as ferramentas de análise estática forne-
cem resultados úteis. O fato de serem imperfeitos não os impede de ter um valor
significativo. Na verdade, a natureza da análise estática não é realmente o principal
fator limitante para as ferramentas de análise estática. Os principais fatores práticos
que determinam a utilidade de uma ferramenta de análise estática são:
- Fazer sentido ao programa, construindo um modelo preciso
- Fazer bons compromissos entre precisão, profundidade e escalabilidade
- Procurar o conjunto certo de defeitos.
- Apresentar resultados e erros fáceis de entender.
- Integração fácil com o sistema de compilação e ambientes de desenvolvimen-
to integrados [66].
6.2.1 Ferramenta
O SonarQube possui alguns plug-ins com algumas métricas de segurança
como o FindBugs [72], mas somente está disponível para a linguagem Java.
Existem outras ferramentas de análise estática focadas em segurança.
As mais populares são o Checkmarx [73], Coverity [74] e Fortify [75].
78
6.3 ANÁLISE DINÂMICA
A análise dinâmica é a análise de software de computador que é executa-
da enquanto programas são executados em um processador real ou virtual em tem-
po real. O objetivo é encontrar erros de segurança em um programa enquanto ele
está em execução, em vez de examinar repetidamente o código off-line. Ao depurar
um programa em todos os cenários para os quais foi concebido, a análise dinâmica
elimina a necessidade de criar artificialmente situações susceptíveis de produzir er-
ros. Ele tem as vantagens distintas de ter a capacidade de identificar vulnerabilida-
des que poderiam ter sido falsos negativos e validar resultados da análise de código
estático.
A análise dinâmica também é conhecida como Teste Dinâmico de Segu-
rança de Aplicativos (DAST), identificando vulnerabilidades dentro de um aplicativo
de produção. As ferramentas DAST são usadas para avaliar rapidamente a segu-
rança geral de um sistema.
A análise dinâmica possui os seguintes benefícios:
- Não há acesso a instruções reais sendo executadas.
- Requer apenas um sistema em execução para executar um teste.
- Nenhuma exigência para ter acesso ao código fonte ou código binário.
- Não há necessidade de entender como escrever software ou executar compi-
lações.
- Testa uma implantação operacional específica.
- Identifica vulnerabilidades em um ambiente de tempo de execução.
- As ferramentas automatizadas fornecem flexibilidade sobre o que procurar.
- Permite a análise de aplicações sem acesso ao código real, podendo ser rea-
lizado em qualquer aplicação.
- Identifica vulnerabilidades que podem ter sido falsas negativas na análise de
código estático e permite a validação dos resultados obtidos pela análise es-
tática [38].
79
6.3.1 Ferramenta para análise dinâmica
A Open Web Application Security Project (OWASP) é uma entidade sem
fins lucrativos e de reconhecimento internacional, que contribui para a melhoria da
segurança de softwares aplicativos reunindo informações importantes que permitem
avaliar riscos de segurança e combater formas de ataques através da internet.
Os estudos e documentos da OWASP são disponibilizadas para toda a
comunidade internacional, e adotados como referência por entidades como U.S. De-
fense Information Systems Agency (DISA), U.S. Federal Trade Commission, várias
empresas e organizações mundiais das áreas de Tecnologia, Auditoria e Segurança,
e também pelo PCI Council [76].
A OWASP possui uma grande lista de recomendações de ferramentas e
uma delas é o ZAP [77]. O WASP Zed Attack Proxy (ZAP) é uma das ferramentas
de segurança gratuitas e de código fonte aberto mais populares do mundo e é man-
tido ativamente por centenas de voluntários internacionais. Ele ajuda a encontrar
automaticamente vulnerabilidades de segurança em aplicativos da Web enquanto
estiver desenvolvendo e testando as aplicações. É também uma ótima ferramenta
para profissionais experientes utilizarem para testes manuais de segurança.
As principais funcionalidades do Zap são:
- Proxy de interceptação. Ele permite ver todos os pedidos realizados para um
aplicativo da Web e todas as respostas que recebe dele.
- Tradicional and AJAX spiders. O Spider é uma ferramenta que é usada para
descobrir automaticamente novos recursos (URLs) em um determinado site.
Ele começa com uma lista de URLs para visitar, chamado de sementes. O
Spider então visita esses URLs, identificando todos os hiperlinks na página e
os adiciona à lista de URLs a visitar e o processo continua recursivamente
enquanto novos recursos forem encontrados.
- Scanner automático. A verificação ativa tenta encontrar possíveis vulnerabili-
dades usando ataques conhecidos contra os destinos selecionados. Vulnera-
bilidades lógicas, como controle de acesso interrompido, não serão encontra-
das por qualquer verificação de vulnerabilidade ativa ou automatizada. Testes
80
de penetrações manuais devem sempre ser realizados além de verificação
ativa para encontrar todos os tipos de vulnerabilidades.
- Scanner Passivo. Por padrão, o ZAP verifica passivamente todas as mensa-
gens HTTP (solicitações e respostas) enviadas para o aplicativo da Web que
está sendo testado. A varredura passiva não altera os pedidos nem as res-
postas de forma alguma e, portanto, é seguro de usar. A varredura é realiza-
da em um segmento de plano de fundo para garantir que não atrasará a ex-
ploração de um aplicativo [78].
81
7 ABORDAGENS NÃO TÉCNICAS
A Engenharia de Software está principalmente voltada para o desenvolvi-
mento de software que satisfaça requisitos funcionais e não funcionais. No en-
tanto, desenvolvedores não são influenciados apenas por determinados requisi-
tos e restrições. A qualidade, estrutura e outras características dos sistemas de
softwares desenvolvidos também dependem da formação dos programadores,
sua experiência de trabalho, estratégias de resolução de problemas, estrutura
organizacional e relações sociais.
A importância dos fatores não técnicos no desenvolvimento dos sistemas
de informação é reforçada por uma pesquisa, que afirma que 90% das falhas do
projeto podem ser atribuídas a falhas não técnicas (sociais, organizacionais, etc.).
Portanto, um processo de design de software não é meramente uma tarefa técni-
ca, mas também há um processo social incorporado nas estruturas organizacio-
nais e culturais. Estas estruturas influenciam e governam o comportamento de
trabalho dos programadores e seus produtos finais, como o código e documenta-
ção [79].
O cultivo de pessoas motivadas e altamente qualificadas de software tem
sido discutido desde a década de 1960. De fato, o "fator pessoas" é tão importan-
te que o Instituto de Engenharia de Software desenvolveu um Modelo de Maturi-
dade de Capacidade de Pessoas (CMM), em reconhecimento ao fato de que "to-
da organização precisa melhorar continuamente sua capacidade de atrair, de-
senvolver, motivar, organizar e reter a força de trabalho necessária para atingir
seus objetivos estratégicos de negócios.
O modelo de maturidade de capacidade de pessoas define as seguintes
áreas de prática chave para pessoas envolvidas com software: pessoal, comuni-
cação e coordenação, ambiente de trabalho, gerenciamento de desempenho,
treinamento, compensação, análise e desenvolvimento de competências, desen-
volvimento de carreira, desenvolvimento de grupo de trabalho, desenvolvimento
de equipe/cultura e outras. As organizações que alcançam altos níveis de matu-
ridade de Pessoas-CMM têm maior probabilidade de implementar práticas efica-
zes de gerenciamento de projetos de software [14].
82
Diversos fatores influenciam a qualidade de um produto, veremos alguns
nas seções seguintes.
7.1 EDUCAÇÃO E TREINAMENTO
O treinamento visa possibilitar mecanismos de capacitação e aperfeiçoa-
mento profissional, como também oferecer instrumentos de desenvolvimento pesso-
al. Treinar significa o preparo da pessoa para uma tarefa ou cargo, enquanto o pro-
pósito da educação é o de preparar a pessoa para o ambiente dentro ou fora do seu
trabalho.
O treinamento é uma atividade que visa fornecer novos conhecimentos,
desenvolver comportamentos necessários para o bom andamento do trabalho e re-
duzir a dívida técnica e na época de hoje. Sua maior missão é de conscientizar os
funcionários da importância de autodesenvolver-se e de buscar o aperfeiçoamento
contínuo. Tem como principais benefícios, o aumento da produtividade, redução de
custos, qualificação da mão de obra e menor rotatividade dos funcionários [80].
Educação não trata apenas do ensino a desenvolvedores sobre como es-
crever melhor software. Ela também inclui educar os usuários sobre como se especi-
ficar melhor o software, e gerentes sobre como configurar ambientes que resultam
em software de qualidade superior. Além disso, a educação e a formação são o
principal mecanismo para a transição das abordagens técnicas discutidas neste tra-
balho.
O treinamento é um elemento crítico no desenvolvimento seguro de sof-
tware que requer o elemento humano. O treinamento ajuda a reduzir o custo da se-
gurança e um programa de treinamento eficaz motivará a equipe de desenvolvimen-
to a produzir softwares mais seguros com menos problemas, mais eficiência e eco-
nomia. Deve-se enfatizar que nenhuma solução pontual fornecerá uma solução úni-
ca para a segurança do software. Em vez disso, é necessária uma abordagem pro-
funda incluindo uma mistura de pessoas, processos e tecnologia. Embora as ferra-
mentas possam analisar através de grandes quantidades de código rapidamente,
elas não são substitutas para os seres humanos. No futuro previsível, a segurança
83
do software ainda será considerada uma arte, mas a arte pode ser aumentada atra-
vés do processo e da tecnologia e, contrariamente aos mitos, a arte pode ser ensi-
nada através de mentores adequados.
Ao longo dos últimos anos, tem havido uma mudança de foco no ensino
superior para incluir uma maior ênfase na concepção de software com segurança. A
educação também tem visto crescimento nos esforços de segurança tanto do usuá-
rio quanto do produtor de software. Entender os princípios de segurança é essencial
para garantir que o software seja seguro e utilizável. Os gerentes e executivos tam-
bém devem ser educados nas implicações de gerenciamento de risco de vulnerabili-
dades de softwares, para entender a importância de investir em softwares de segu-
rança e de baixa vulnerabilidade [38].
7.2 MOTIVACIONAL
Motivação significa organizar o trabalho e o ambiente de trabalho para
incentivar as pessoas a trabalhar o mais eficazmente possível. Se as pessoas não
estão motivadas, não estarão interessadas no trabalho que estão fazendo. Eles tra-
balharão lentamente, terão maior probabilidade de cometer erros e não contribuirão
para os objetivos mais amplos da equipe ou da organização.
Para dar esse incentivo, será necessário entender um pouco sobre o que
motiva as pessoas. O psicólogo americano Abraham H. Maslow sugere que as pes-
soas são motivadas por satisfazer suas necessidades. Essas necessidades são or-
ganizadas em uma série de níveis, como mostrado na Figura 13. Os níveis mais
baixos dessa hierarquia, que são as necessidades fisiológicas, representam neces-
sidades fundamentais de alimentação e sono. Seguindo em diante, encontra-se a
necessidade de segurança que é a necessidade de se sentir seguro em um ambien-
te. As necessidades sociais dizem respeito à necessidade de se sentirem parte de
um agrupamento social. As necessidades de autoestima representam a necessida-
de de se sentirem respeitadas pelos outros, e as necessidades de auto realização
preocupam-se com o desenvolvimento pessoal. As pessoas precisam satisfazer ne-
84
cessidades de nível inferior antes das necessidades mais abstratas e de nível mais
alto.
Figura 13: Exemplo da pirâmide de Maslow. [81]
Pessoas que trabalham em organizações de desenvolvimento de software
não estão geralmente com fome ou sede ou fisicamente ameaçadas por seu ambi-
ente. Portanto, o ponto mais importante é que as necessidades sociais das pessoas,
estima e auto realização sejam satisfeitas.
Para satisfazer necessidades sociais, é necessário dar tempo às pessoas
para conhecer seus colegas de trabalho e fornecer lugares para eles se encontrarem.
Isso é relativamente fácil quando todos os membros de uma equipe de desenvolvi-
mento trabalham no mesmo lugar, mas, cada vez mais, os membros da equipe não
estão localizados no mesmo prédio ou mesmo na mesma cidade ou estado. Eles
podem trabalhar para diferentes organizações ou de casa a maior parte do tempo.
Sistemas de redes sociais e teleconferência podem ser utilizados para facilitar as
comunicações, mas sistemas eletrônicos são mais eficazes uma vez que as pessoas
85
se conhecem. Portanto, é interessante que sejam organizadas algumas reuniões
cara a cara no início do projeto para que as pessoas possam interagir diretamente
com outros membros da equipe. Através dessa interação direta, as pessoas se tor-
nam parte de um grupo social e aceitam as metas e prioridades desse grupo.
Para satisfazer as necessidades de estima, é necessário mostrar às pes-
soas que elas são valorizadas pela organização. O reconhecimento público das
conquistas é uma maneira simples, mas eficaz, de se fazer isso. Obviamente, as
pessoas também devem sentir que são pagas ao nível que reflete suas habilidades
e experiência.
Finalmente, para satisfazer as necessidades de auto realização, será pre-
ciso dar às pessoas a responsabilidade pelo seu trabalho, atribuir tarefas exigentes,
mas não impossíveis e fornecer um programa de treinamento onde as pessoas pos-
sam desenvolver suas habilidades. O treinamento é uma importante influência moti-
vadora, pois as pessoas gostam de adquirir novos conhecimentos e aprender novas
habilidades [15].
7.3 EXCESSO DE HORAS TRABALHADAS
Conforme relatou o economista Sidney J. Chapman que publicou o traba-
lho “Hours of Labour” [82], após a realização de um estudo com trabalhadores indus-
triais que mantinham uma produtividade em mais ou menos 40 horas por semana de
trabalho em cinco dias, concluiu-se que, quando os trabalhadores foram expostos a
longas horas de trabalho, a produtividade começou a diminuir. O estudo realizado
constatou que, no período entre quatro dias e dois meses, os ganhos de horas adi-
cionais de trabalho são anulados pelo declínio da produtividade. Em casos extre-
mos, com a privação do sono o resultado foi pior.
A habilidade de fazer tarefas mentais complexas se degrada mais rapi-
damente do que o desempenho físico. Entre os trabalhadores do conhecimento,
uma perda de produtividade devida a horas excessivas pode começar mais cedo e
ser maior do que os trabalhadores industriais, porque o trabalho é afetado pela fadi-
ga mental [83].
86
Embora não exista uma pesquisa aprofundada específica para descobrir a
relação entre produtividade e horas trabalhadas no desenvolvimento de software, há
diversos fatores a favor da extinção do trabalho excessivo [84]:
- A programação requer concentração intensa. Em primeiro lugar, a principal
atividade de desenvolvimento de software requer concentração intensa e es-
forço mental. Assim, a programação é muito vulnerável aos custos da priva-
ção de sono e o estresse que assumem os trabalhadores.
- Os erros são caros. Erros na programação podem levar muito tempo para se-
rem corrigidos, muito mais tempo do que costumam tomar para fazer. Código
que é escrito durante a fadiga e estresse extremo é quase sempre de quali-
dade muito inferior ao código escrito em condições mais relaxadas. Tal códi-
go muitas vezes falta de documentação e cheio de defeitos. Erros muitas ve-
zes não são descobertos até muito mais tarde em um projeto quando este
código passa a ser testado de novas maneiras. Tais erros envolvem frequen-
temente complexas interações de um grande número de componentes indivi-
duais e podem levar dias, se não semanas, para encontrar e corrigir. De fato,
o código escrito durante a fadiga é geralmente muito mais suscetível a pro-
blemas.
- O desenvolvimento de software é altamente colaborativo. A programação é
um esforço colaborativo, e os erros cometidos por um indivíduo muitas vezes
trarão um efeito cascata no trabalho de todos os envolvidos em um projeto.
Assim, é comum que um desenvolvedor de software gaste uma grande parte
de seu tempo lidando com bugs produzidos por outros desenvolvedores. O
excesso de trabalho de um único funcionário pode, portanto, afetar negativa-
mente a produtividade de toda a equipe. Quando cada programador está tra-
balhando longas e cansativas horas, esse problema é aumentado exponenci-
almente. Todos começam a gastar mais e mais do seu tempo corrigindo erros
ao invés de realizar novas tarefas.
- A reutilização é muito importante. Finalmente, é muito comum que o software
seja reutilizado por anos, se não décadas, e tal software invariavelmente re-
quer atualizações e outras modificações. Embora o código escrito em situa-
ções extremas possa ser concluído no tempo, para atender a um prazo mui-
tas vezes mal concebido, geralmente apresenta qualidade muito inferior ao
87
código escrito em condições mais relaxadas. Quando é necessária a realiza-
ção de atualizações e modificações, esse código é muitas vezes muito mais
frágil e difícil de entender, o que significa que essas mudanças geralmente le-
vam muito mais tempo para serem implementadas. É muito mais produtivo
fazer o trabalho certo na primeira vez, em vez de fazer um trabalho com me-
nos qualidade e depois pagar por ele mais tarde.
88
CONCLUSÕES E TRABALHOS FUTUROS
Este trabalho reuniu conhecimentos, sob diferentes aspectos, para melho-
ria da qualidade do software, apresentando conteúdo técnico e não técnico, buscan-
do centralizar abordagens para redução de falhas de software e vulnerabilidades.
Como o assunto abordado é muito complexo e amplo, este estudo serviu também
como um guia para esclarecer como o bug foi introduzido e as abordagens para que
a correção de falhas seja realmente efetiva e outros defeitos não venham ocorrer.
Para reduzir os bugs e vulnerabilidades de uma aplicação este estudo se
baseou em estratégias técnicas que envolvem a diminuição e controle da complexi-
dade, aumento do índice de manutenção do código fonte, revisão dos artefatos pro-
duzidos e aumento da qualidade dos testes. O estudo abordou estratégias, além das
técnicas, mostrando fatores como educação, motivação e o excesso de horas traba-
lhadas.
As estratégias apresentadas nos capítulos propostos permitem que o obje-
tivo seja alcançado através da diminuição da quantidade de bugs e vulnerabilidades
inseridos na aplicação final, e o aumento da quantidade de bugs e vulnerabilidades
encontrados na fase de desenvolvimento. Além disso, a qualidade do software e di-
minuição do impacto das falhas é possibilitada através de uma arquitetura mais ro-
busta.
O trabalho mostrou que propostas de design e arquitetura de software po-
dem ser aplicadas para diminuir e controlar a complexidade do software. Além disso,
linguagens de programação com uso de seus compiladores fornecem uma camada
extra de proteção para possíveis problemas.
Revisões de software também podem ser utilizadas com o objetivo de au-
mentar a descoberta de bugs, além de melhorar a qualidade do software escrito e o
favorecimento da formação dos profissionais. Também são práticas muito efetivas
para descobertas de vulnerabilidades, permitindo uma análise prévia, antes que o
software entre em produção.
O trabalho também introduziu técnicas para melhorar a qualidade dos tes-
tes em todas as camadas da aplicação com foco na automação, melhorando a qua-
lidade do software. Os métodos formais englobam técnicas baseadas em matemáti-
89
ca e lógica para descobrir possíveis problemas, desde a construção do software até
a manutenção, oferecendo uma boa opção para a construção de um software com
qualidade e efetivo suporte aos testes.
As abordagens não técnicas representam o outro lado da engenharia de
software que é o elemento pessoal. O objetivo dessas abordagens é reduzir a quan-
tidade de falhas no código usando elementos como motivação, treinamento e uma
melhor qualidade de vida, através do controle das horas trabalhadas.
Dada a importância e abrangência do tema será necessário o aprofunda-
mento dos estudos, principalmente com foco em segurança da aplicação, que é um
dos problemas em ascensão, aperfeiçoando o conhecimento e a educação nessa
área.
Cada vez mais as aplicações crescem em complexidade, sendo necessá-
rio construir um software seguro e com menos bugs. Com as abordagens descritas
nesse trabalho, desenvolvedores serão capazes de projetar e construir uma aplica-
ção com essas características. Mas como a criação e manutenção do software é
uma tarefa realizada por humanos, erros e problemas, por mínimos que sejam, sem-
pre existirão.
90
REFERÊNCIAS BIBLIOGRÁFICAS
1. COMPUTERWORLD. Moth in the machine: Debugging the origins of 'bug'. ComputerWorld, 3 set. 2011. Disponivel em: <http://www.computerworld.com/article/2515435/app-development/moth-in-the-machine--debugging-the-origins-of--bug-.html>.
2. THE NATIONAL MUSEUM OF AMERICAN HISTORY. Log Book with Computer Bug. The National Museum of American History. Disponivel em: <http://americanhistory.si.edu/collections/search/object/nmah_334663>.
3. MAGOUN, A. B.; ISRAEL, P. Did You Know? Edison Coined the Term “Bug”. the institute, 23 ago. 2013. Disponivel em: <http://theinstitute.ieee.org/tech-history/technology-history/did-you-know-edison-coined-the-term-bug>.
4. BEYER, K. W. Grace Hopper and the Invention of the Information Age. [S.l.]: The MIT Press, 2012. 64 p.
5. GARFINKEL, S. History's Worst Software Bugs. Wired, 11 ago. 2015. Disponivel em: <http://archive.wired.com/software/coolapps/news/2005/11/69355?currentPage=all>.
6. HARLEY, N. 10 of the most costly software errors in history. Raygun, 29 maio 2014. Disponivel em: <https://raygun.com/blog/2014/05/10-costly-software-errors-history/>.
7. JONES, A. 10 Seriously Epic Computer Software Bugs. ListVerse, 24 dez. 2012. Disponivel em: <http://listverse.com/2012/12/24/10-seriously-epic-computer-software-bugs/>.
8. SELTZER, L. The Morris Worm: Internet malware turns 25. Zdnet, 2 nov. 2013. Disponivel em: <http://www.zdnet.com/article/the-morris-worm-internet-malware-turns-25/>.
9. SEELEY, D. A Tour of the Worm. University of Utah. [S.l.], p. 8-9. 1989. 10. BISHOP, M.; BAILEY, D. A Critical Analysis of Vulnerability Taxonomies. University
of California. [S.l.]. 1996. (CSE-96-11). 11. LONGLEY, D.; SHAIN, M. The Data and Computer Security Dictionary of
Standards,concepts and terms. [S.l.]: [s.n.], 1990. 12. VIJAYAN, J. The 10 Worst Vulnerabilities of The Last 10 Years. DarkReading, 06 maio
2016. Disponivel em: <http://www.darkreading.com/vulnerabilities---threats/the-10-worst-vulnerabilities-of-the-last-10-years/d/d-id/1325425>.
13. HALVORSEN, H.-P. Software Development A Practical Approach! University College of Southeast Norway. [S.l.], p. 50-53. 2017.
14. S.PRESSMAN, R. SOFTWARE ENGINEERING: A PRACTITIONER’S APPROACH. Seventh Edition. ed. [S.l.]: [s.n.]. ISBN 978–0–07–337597–7.
15. SOMMERVILLE, I. SOFTWARE ENGINEERING. Ninth Edition. ed. [S.l.]: Addison-Wesley. ISBN 978-0-13-703515-1.
91
16. BRAUDE, E. J.; BERNSTEIN, M. E. Software Engineering Modern Approaches. Second Edition. ed. [S.l.]: [s.n.].
17. MILLETT, S.; TUNE, N. Patterns, Principles, and Practices of Domain-Driven Design. [S.l.]: Wrox.
18. VERNON, V. Implemeting Domain-Driven Design. [S.l.]: Addison-Wesley. 19. CUKIER, D. DDD - Introdução a Domain Driven Design. Agile And Art, 16 jul. 2010.
Disponivel em: <http://www.agileandart.com/2010/07/16/ddd-introducao-a-domain-driven-design/>.
20. EVANS, E. Domain-Driven Design: Tackling Complexity in the Heart of Software. [S.l.]: Addison-Wesley Professional, v. 1, 2003.
21. MILLETT, S. Practicing Domain-Driven Design Practical advice for teams implementing the development philosophy of Domain-Driven Design. [S.l.]: [s.n.].
22. ANNETT, R. What is a Monolith? coding the architecture, 10 dez. 2014. Disponivel em: <http://www.codingthearchitecture.com/2014/11/19/what_is_a_monolith.html>.
23. RICHARDSON, C. Microservice Patterns. Version 1. ed. [S.l.]: Manning. 24. NEWMAN, S. Building Microservices. [S.l.]: Mike Loukides, 2015. 25. LEWIS, J.; FOWLER, M. Microservices Resource Guide. MartinFowler. Disponivel em:
<https://martinfowler.com/microservices/>. 26. MARTIN, R. C. Clean Code A Handbook of Agile Software Craftsmanship. [S.l.]:
Prentice Hall. 27. RICHARDSON, C. Pattern: Microservice Architecture. Microservice Architecture.
Disponivel em: <http://microservices.io/patterns/microservices.html>. 28. FOWLER, M.; LEWIS, J. Microservices. MartinFowler, 10 mar. 2014. Disponivel em:
<https://martinfowler.com/articles/microservices.html>. 29. TORRE, C. D. L.; SINGH, K. D.; TURECEK, V. Microsoft Azure - Azure Service Fabric
and the Microservices Architecture. MSDN, dez. 2015. Disponivel em: <https://msdn.microsoft.com/en-us/magazine/mt595752.aspx>.
30. ROSS, P. E. The Exterminators. IEEE Spectrum, 1 set. 2005. Disponivel em: <http://spectrum.ieee.org/computing/software/the-exterminators>.
31. MICROSOFT. Strong Typing. MSDN. Disponivel em: <https://msdn.microsoft.com/en-us/library/windows/desktop/aa378693(v=vs.85).aspx>.
32. KRAUSS, A. Programming Concepts: Static vs. Dynamic Type Checking. thesocietea.org, 20 nov. 2015. Disponivel em: <https://thesocietea.org/2015/11/programming-concepts-static-vs-dynamic-type-checking/>.
33. RAVENBROOK. Memory Management Reference. Memory Management Reference. Disponivel em: <http://www.memorymanagement.org/mmref/begin.html>.
34. HUGHES, J. Why Functional Programming Matters. The University, Glasgow. [S.l.], p. 1.
35. F# SOFTWARE FOUNDATION. FSharp. FSharp. Disponivel em: <http://fsharp.org/>. 36. OPEN SOURCE FOUNDATION. Haskell. Haskell. Disponivel em:
<https://www.haskell.org/>. 37. OPEN SOURCE FOUNDATION. Swift. Swift. Disponivel em: <https://swift.org/>. 38. RANSOME, J.; MISRA, A. Core Software Security Security at the Source. [S.l.]: CRC
92
Press. 39. WILLIAMS, D. L. A (Partial) Introduction to Software Engineering Practices and
Methods. North Carolina State University. [S.l.]. 2010. 40. WIEGERS, K. When Two Eyes Aren't Enough. Dr.Dobb's The World of Software
Development, 01 out. 2001. Disponivel em: <http://www.drdobbs.com/when-two-eyes-arent-enough/184414780>.
41. NOGUEIRA, E. O que é Agile Testing? iMasters, 04 jul. 2016. Disponivel em: <https://imasters.com.br/desenvolvimento/o-que-e-agile-testing/?trace=1519021197&source=single>.
42. SMARTBEAR. What is Agile Testing. SmartBear. Disponivel em: <https://smartbear.com/learn/software-testing/what-is-agile-testing/>.
43. CRISPIN, L.; GREGORY, J. Agile Testing A Practical Guide for Testers and Agile Teams. [S.l.]: [s.n.].
44. TARLINDER, A. Developer Testing Building Quality into Software. [S.l.]: [s.n.]. 45. AMARAL, L. Estratégias para implantar o processo de automação de testes. Base2, 20
dez. 2016. Disponivel em: <http://www.base2.com.br/2016/12/20/estrategias-para-implantar-o-processo-de-automacao-de-testes/>.
46. COHN, M. Succeeding with Agile Software Development using Scrum. [S.l.]: [s.n.]. 47. NET FOUNDATION. xUnit.net. xUnit.net. Disponivel em: <https://xunit.github.io/>. 48. OPEN SOURCE FOUNDATION. JUnit. JUnit. Disponivel em: <http://junit.org/junit4/>. 49. VANCE, S. Quality Code Software Testing Principles, Practices, and Patterns. [S.l.]:
Addison-Wesley. 50. RUBIN, K. S. Essential Scrum A Practical Guide to the Most Popular Agile Process.
[S.l.]: Addison-Wesley. 51. BECK, K.; ANDRES, C. Extreme Programming Explained: Embrace Change. 2nd
Edition. ed. [S.l.]: Addison-Wesley. 52. KROLL, P.; KRUCHTEN, P. The Rational Unified Process Made Easy: A Practitioner's
Guide to the RUP. [S.l.]: Addison-Wesley. 53. SMART, J. F. BDD in Action Behavior-Driven Development for the whole software
lifecycle. [S.l.]: Manning. 54. RIBEIRO, C. Entendendo BDD com Cucumber. The Bug Bang Theory Teste de
Software e Continuos Delivery, 19 fev. 2012. Disponivel em: <http://www.bugbang.com.br/entendendo-bdd-com-cucumber-parte-i/>.
55. PIRES, E. Eduardo Pires Treinamentos e Consultoria. DDD, TDD, BDD, Afinal o que são essas siglas?, 06 jun. 2012. Disponivel em: <http://www.eduardopires.net.br/2012/06/ddd-tdd-bdd/>.
56. OPEN SOURCE FOUNDATION. specflow. specflow. Disponivel em: <http://specflow.org/>.
57. RSPEC TEAM. RSpec. RSpec. Disponivel em: <http://rspec.info/>. 58. OPEN SOURCE FOUNDATION. Fit: Framework for Integrated Test. Fit. Disponivel em:
<http://fit.c2.com/>. 59. OPEN SOURCE FOUNDATION. FitNesse. FitNesse. Disponivel em:
<http://www.fitnesse.org/>. 60. CROSSXCHECK. SOAPSonar. SOAPSonar. Disponivel em:
93
<http://www.crosschecknet.com/products/soapsonar.php>. 61. SMARTBEAR. SoapUI. SoapUI. Disponivel em: <https://www.soapui.org/>. 62. GREGORY, J.; CRISPIN, L. More Agile Testing Learning Journeys for the Whole
Team. [S.l.]: [s.n.]. 63. OPEN SOURCE FOUNDATION. watir. watir. Disponivel em: <http://watir.github.io/>. 64. OPEN SOURCE FOUNDATION. SeleniumHQ. SeleniumHQ. Disponivel em:
<http://www.seleniumhq.org/>. 65. AL., P. E. B. E. Dramatically Reducing Software Vulnerabilities. Report to the White
House Office of Science and Technology Policy, November 2016. 66. CHESS, B.; WEST, J. Secure Programming with Static Analysis. [S.l.]: [s.n.]. 67. NICOLETTE, D. Software Development Metrics. [S.l.]: Manning. 68. SONARSOURCE. SonarQube. SonarQube. Disponivel em:
<https://sonarqube.com/about>. 69. ARAPIDIS, C. S. Sonar Code Quality Testing Essentials. [S.l.]: Packt. 70. UAIJUG. Automação e Devops. uaijug. Disponivel em: <http://uaijug.github.io/hermes-
workshop/sessao4.html#/>. 71. CAMPBELL, G. A.; PAPAPETROU, P. P. SonarQube in Action. [S.l.]: Manning. 72. OPEN SOURCE FOUNDATION. FindBugs. FindBugs. Disponivel em:
<http://findbugs.sourceforge.net/>. 73. CHECKMARX. Static Code Analysis (SAST). CheckMarx. Disponivel em:
<https://www.checkmarx.com/>. 74. SYNOPSYS. Static Application Security Testing (SAST). Synopsys. Disponivel em:
<https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html>. 75. HEWLETT PACKARD ENTERPRISE. Fortify Static Code Analyzer. Hewlett Packard
Enterprise. Disponivel em: <https://saas.hpe.com/en-us/software/sca>. 76. OWASP. About The Open Web Application Security Project. OWASP. Disponivel em:
<https://www.owasp.org/index.php/Main_Page>. 77. OPEN SOURCE FOUNDATION. OWASP Zed Attack Proxy Project. Owasp. Disponivel
em: <https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project>. 78. OWASP ZED. OWASP Zed Attack Proxy Project. OWASP Zed Attack Proxy Project.
Disponivel em: <https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project>.
79. DAMASEVICIUS, R. Information Systems Development. [S.l.]: Springer, 2010. 80. CALIL, S. O treinamento visa possibilitar mecanismos de capacitação e
aperfeiçoamento profissional. UFSM. [S.l.]. 81. ABRANTES, L. Pirâmide de Maslow: Entenda os níveis de necessidade dos seus clientes.
saia do lugar. Disponivel em: <http://saiadolugar.com.br/piramide-de-maslow/>. 82. CHAPMAN, S. J. Hours of Labour. Economic Journal, set. 1909. 83. ROBINSON, E. Why Crunch Modes Doesn't Work: Six Lessons. igda. Disponivel em:
<http://www.igda.org/?page=crunchsixlessons>. 84. ROBERTS, E. How is Software Development Different? Crunch mode: programming to
the extreme. Disponivel em: <https://cs.stanford.edu/people/eroberts/courses/cs181/projects/crunchmode/econ-
94
software-differences.html>. 85. TIMBERG, C. A FLAW IN THE DESIGN. The Washington Post, 30 May 2015.
Disponivel em: <http://www.washingtonpost.com/sf/business/2015/05/30/net-of-insecurity-part-1/?utm_term=.11c1f1ebb71e>.
86. ALEXANDER B. MAGOUN; ISRAEL, P. Did You Know? Edison Coined the Term “Bug”. the institute The IEEE news source, 23 August 2013. Disponivel em: <http://theinstitute.ieee.org/tech-history/technology-history/did-you-know-edison-coined-the-term-bug>.
1. COMPUTERWORLD. Moth in the machine: Debugging the origins of 'bug'.
ComputerWorld, 3 set. 2011. Disponivel em: <http://www.computerworld.com/article/2515435/app-development/moth-in-the-machine--debugging-the-origins-of--bug-.html>.
2. THE NATIONAL MUSEUM OF AMERICAN HISTORY. Log Book with Computer Bug. The National Museum of American History. Disponivel em: <http://americanhistory.si.edu/collections/search/object/nmah_334663>.
3. MAGOUN, A. B.; ISRAEL, P. Did You Know? Edison Coined the Term “Bug”. the institute, 23 ago. 2013. Disponivel em: <http://theinstitute.ieee.org/tech-history/technology-history/did-you-know-edison-coined-the-term-bug>.
4. BEYER, K. W. Grace Hopper and the Invention of the Information Age. [S.l.]: The MIT Press, 2012. 64 p.
5. GARFINKEL, S. History's Worst Software Bugs. Wired, 11 ago. 2015. Disponivel em: <http://archive.wired.com/software/coolapps/news/2005/11/69355?currentPage=all>.
6. HARLEY, N. 10 of the most costly software errors in history. Raygun, 29 maio 2014. Disponivel em: <https://raygun.com/blog/2014/05/10-costly-software-errors-history/>.
7. JONES, A. 10 Seriously Epic Computer Software Bugs. ListVerse, 24 dez. 2012. Disponivel em: <http://listverse.com/2012/12/24/10-seriously-epic-computer-software-bugs/>.
8. SELTZER, L. The Morris Worm: Internet malware turns 25. Zdnet, 2 nov. 2013. Disponivel em: <http://www.zdnet.com/article/the-morris-worm-internet-malware-turns-25/>.
9. SEELEY, D. A Tour of the Worm. University of Utah. [S.l.], p. 8-9. 1989. 10. BISHOP, M.; BAILEY, D. A Critical Analysis of Vulnerability Taxonomies. University
of California. [S.l.]. 1996. (CSE-96-11). 11. LONGLEY, D.; SHAIN, M. The Data and Computer Security Dictionary of
Standards,concepts and terms. [S.l.]: [s.n.], 1990. 12. VIJAYAN, J. The 10 Worst Vulnerabilities of The Last 10 Years. DarkReading, 06 maio
2016. Disponivel em: <http://www.darkreading.com/vulnerabilities---threats/the-10-worst-vulnerabilities-of-the-last-10-years/d/d-id/1325425>.
13. HALVORSEN, H.-P. Software Development A Practical Approach! University College of Southeast Norway. [S.l.], p. 50-53. 2017.
14. S.PRESSMAN, R. SOFTWARE ENGINEERING: A PRACTITIONER’S APPROACH. Seventh Edition. ed. [S.l.]: [s.n.]. ISBN 978–0–07–337597–7.
95
15. SOMMERVILLE, I. SOFTWARE ENGINEERING. Ninth Edition. ed. [S.l.]: Addison-Wesley. ISBN 978-0-13-703515-1.
16. BRAUDE, E. J.; BERNSTEIN, M. E. Software Engineering Modern Approaches. Second Edition. ed. [S.l.]: [s.n.].
17. MILLETT, S.; TUNE, N. Patterns, Principles, and Practices of Domain-Driven Design. [S.l.]: Wrox.
18. VERNON, V. Implemeting Domain-Driven Design. [S.l.]: Addison-Wesley. 19. CUKIER, D. DDD - Introdução a Domain Driven Design. Agile And Art, 16 jul. 2010.
Disponivel em: <http://www.agileandart.com/2010/07/16/ddd-introducao-a-domain-driven-design/>.
20. EVANS, E. Domain-Driven Design: Tackling Complexity in the Heart of Software. [S.l.]: Addison-Wesley Professional, v. 1, 2003.
21. MILLETT, S. Practicing Domain-Driven Design Practical advice for teams implementing the development philosophy of Domain-Driven Design. [S.l.]: [s.n.].
22. ANNETT, R. What is a Monolith? coding the architecture, 10 dez. 2014. Disponivel em: <http://www.codingthearchitecture.com/2014/11/19/what_is_a_monolith.html>.
23. RICHARDSON, C. Microservice Patterns. Version 1. ed. [S.l.]: Manning. 24. NEWMAN, S. Building Microservices. [S.l.]: Mike Loukides, 2015. 25. LEWIS, J.; FOWLER, M. Microservices Resource Guide. MartinFowler. Disponivel em:
<https://martinfowler.com/microservices/>. 26. MARTIN, R. C. Clean Code A Handbook of Agile Software Craftsmanship. [S.l.]:
Prentice Hall. 27. RICHARDSON, C. Pattern: Microservice Architecture. Microservice Architecture.
Disponivel em: <http://microservices.io/patterns/microservices.html>. 28. FOWLER, M.; LEWIS, J. Microservices. MartinFowler, 10 mar. 2014. Disponivel em:
<https://martinfowler.com/articles/microservices.html>. 29. TORRE, C. D. L.; SINGH, K. D.; TURECEK, V. Microsoft Azure - Azure Service Fabric
and the Microservices Architecture. MSDN, dez. 2015. Disponivel em: <https://msdn.microsoft.com/en-us/magazine/mt595752.aspx>.
30. ROSS, P. E. The Exterminators. IEEE Spectrum, 1 set. 2005. Disponivel em: <http://spectrum.ieee.org/computing/software/the-exterminators>.
31. MICROSOFT. Strong Typing. MSDN. Disponivel em: <https://msdn.microsoft.com/en-us/library/windows/desktop/aa378693(v=vs.85).aspx>.
32. KRAUSS, A. Programming Concepts: Static vs. Dynamic Type Checking. thesocietea.org, 20 nov. 2015. Disponivel em: <https://thesocietea.org/2015/11/programming-concepts-static-vs-dynamic-type-checking/>.
33. RAVENBROOK. Memory Management Reference. Memory Management Reference. Disponivel em: <http://www.memorymanagement.org/mmref/begin.html>.
34. HUGHES, J. Why Functional Programming Matters. The University, Glasgow. [S.l.], p. 1.
35. F# SOFTWARE FOUNDATION. FSharp. FSharp. Disponivel em: <http://fsharp.org/>. 36. OPEN SOURCE FOUNDATION. Haskell. Haskell. Disponivel em:
<https://www.haskell.org/>.
96
37. OPEN SOURCE FOUNDATION. Swift. Swift. Disponivel em: <https://swift.org/>. 38. RANSOME, J.; MISRA, A. Core Software Security Security at the Source. [S.l.]: CRC
Press. 39. WILLIAMS, D. L. A (Partial) Introduction to Software Engineering Practices and
Methods. North Carolina State University. [S.l.]. 2010. 40. WIEGERS, K. When Two Eyes Aren't Enough. Dr.Dobb's The World of Software
Development, 01 out. 2001. Disponivel em: <http://www.drdobbs.com/when-two-eyes-arent-enough/184414780>.
41. NOGUEIRA, E. O que é Agile Testing? iMasters, 04 jul. 2016. Disponivel em: <https://imasters.com.br/desenvolvimento/o-que-e-agile-testing/?trace=1519021197&source=single>.
42. SMARTBEAR. What is Agile Testing. SmartBear. Disponivel em: <https://smartbear.com/learn/software-testing/what-is-agile-testing/>.
43. CRISPIN, L.; GREGORY, J. Agile Testing A Practical Guide for Testers and Agile Teams. [S.l.]: [s.n.].
44. TARLINDER, A. Developer Testing Building Quality into Software. [S.l.]: [s.n.]. 45. AMARAL, L. Estratégias para implantar o processo de automação de testes. Base2, 20
dez. 2016. Disponivel em: <http://www.base2.com.br/2016/12/20/estrategias-para-implantar-o-processo-de-automacao-de-testes/>.
46. COHN, M. Succeeding with Agile Software Development using Scrum. [S.l.]: [s.n.]. 47. NET FOUNDATION. xUnit.net. xUnit.net. Disponivel em: <https://xunit.github.io/>. 48. OPEN SOURCE FOUNDATION. JUnit. JUnit. Disponivel em: <http://junit.org/junit4/>. 49. VANCE, S. Quality Code Software Testing Principles, Practices, and Patterns. [S.l.]:
Addison-Wesley. 50. RUBIN, K. S. Essential Scrum A Practical Guide to the Most Popular Agile Process.
[S.l.]: Addison-Wesley. 51. BECK, K.; ANDRES, C. Extreme Programming Explained: Embrace Change. 2nd
Edition. ed. [S.l.]: Addison-Wesley. 52. KROLL, P.; KRUCHTEN, P. The Rational Unified Process Made Easy: A Practitioner's
Guide to the RUP. [S.l.]: Addison-Wesley. 53. SMART, J. F. BDD in Action Behavior-Driven Development for the whole software
lifecycle. [S.l.]: Manning. 54. RIBEIRO, C. Entendendo BDD com Cucumber. The Bug Bang Theory Teste de
Software e Continuos Delivery, 19 fev. 2012. Disponivel em: <http://www.bugbang.com.br/entendendo-bdd-com-cucumber-parte-i/>.
55. PIRES, E. Eduardo Pires Treinamentos e Consultoria. DDD, TDD, BDD, Afinal o que são essas siglas?, 06 jun. 2012. Disponivel em: <http://www.eduardopires.net.br/2012/06/ddd-tdd-bdd/>.
56. OPEN SOURCE FOUNDATION. specflow. specflow. Disponivel em: <http://specflow.org/>.
57. RSPEC TEAM. RSpec. RSpec. Disponivel em: <http://rspec.info/>. 58. OPEN SOURCE FOUNDATION. Fit: Framework for Integrated Test. Fit. Disponivel em:
<http://fit.c2.com/>. 59. OPEN SOURCE FOUNDATION. FitNesse. FitNesse. Disponivel em:
97
<http://www.fitnesse.org/>. 60. CROSSXCHECK. SOAPSonar. SOAPSonar. Disponivel em:
<http://www.crosschecknet.com/products/soapsonar.php>. 61. SMARTBEAR. SoapUI. SoapUI. Disponivel em: <https://www.soapui.org/>. 62. GREGORY, J.; CRISPIN, L. More Agile Testing Learning Journeys for the Whole
Team. [S.l.]: [s.n.]. 63. OPEN SOURCE FOUNDATION. watir. watir. Disponivel em: <http://watir.github.io/>. 64. OPEN SOURCE FOUNDATION. SeleniumHQ. SeleniumHQ. Disponivel em:
<http://www.seleniumhq.org/>. 65. AL., P. E. B. E. Dramatically Reducing Software Vulnerabilities. Report to the White
House Office of Science and Technology Policy, November 2016. 66. CHESS, B.; WEST, J. Secure Programming with Static Analysis. [S.l.]: [s.n.]. 67. NICOLETTE, D. Software Development Metrics. [S.l.]: Manning. 68. SONARSOURCE. SonarQube. SonarQube. Disponivel em:
<https://sonarqube.com/about>. 69. ARAPIDIS, C. S. Sonar Code Quality Testing Essentials. [S.l.]: Packt. 70. UAIJUG. Automação e Devops. uaijug. Disponivel em: <http://uaijug.github.io/hermes-
workshop/sessao4.html#/>. 71. CAMPBELL, G. A.; PAPAPETROU, P. P. SonarQube in Action. [S.l.]: Manning. 72. OPEN SOURCE FOUNDATION. FindBugs. FindBugs. Disponivel em:
<http://findbugs.sourceforge.net/>. 73. CHECKMARX. Static Code Analysis (SAST). CheckMarx. Disponivel em:
<https://www.checkmarx.com/>. 74. SYNOPSYS. Static Application Security Testing (SAST). Synopsys. Disponivel em:
<https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html>. 75. HEWLETT PACKARD ENTERPRISE. Fortify Static Code Analyzer. Hewlett Packard
Enterprise. Disponivel em: <https://saas.hpe.com/en-us/software/sca>. 76. OWASP. About The Open Web Application Security Project. OWASP. Disponivel em:
<https://www.owasp.org/index.php/Main_Page>. 77. OPEN SOURCE FOUNDATION. OWASP Zed Attack Proxy Project. Owasp. Disponivel
em: <https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project>. 78. OWASP ZED. OWASP Zed Attack Proxy Project. OWASP Zed Attack Proxy Project.
Disponivel em: <https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project>.
79. DAMASEVICIUS, R. Information Systems Development. [S.l.]: Springer, 2010. 80. CALIL, S. O treinamento visa possibilitar mecanismos de capacitação e
aperfeiçoamento profissional. UFSM. [S.l.]. 81. ABRANTES, L. Pirâmide de Maslow: Entenda os níveis de necessidade dos seus clientes.
saia do lugar. Disponivel em: <http://saiadolugar.com.br/piramide-de-maslow/>. 82. CHAPMAN, S. J. Hours of Labour. Economic Journal, set. 1909. 83. ROBINSON, E. Why Crunch Modes Doesn't Work: Six Lessons. igda. Disponivel em:
<http://www.igda.org/?page=crunchsixlessons>. 84. ROBERTS, E. How is Software Development Different? Crunch mode: programming to
98
the extreme. Disponivel em: <https://cs.stanford.edu/people/eroberts/courses/cs181/projects/crunchmode/econ-software-differences.html>.
85. TIMBERG, C. A FLAW IN THE DESIGN. The Washington Post, 30 May 2015. Disponivel em: <http://www.washingtonpost.com/sf/business/2015/05/30/net-of-insecurity-part-1/?utm_term=.11c1f1ebb71e>.
86. ALEXANDER B. MAGOUN; ISRAEL, P. Did You Know? Edison Coined the Term “Bug”. the institute The IEEE news source, 23 August 2013. Disponivel em: <http://theinstitute.ieee.org/tech-history/technology-history/did-you-know-edison-coined-the-term-bug>.
99
ANEXOS
A função das duas definições, Anexo e Apêndice, é semelhante, mas com uma
grande diferença entre elas: a autoria. O ANEXO de um trabalho acadêmico deve
ser aquele texto ou documento que não foi elaborado por você, tendo como objeti-
vo servir de legitimação. Já o APÊNDICE se configura como texto ou documento
elaborado por você, tendo como objetivo complementar a sua argumentação.
100
ANEXO A – TÍTULO DO ANEXO A
101
ANEXO B – TÍTULO DO ANEXO B