Pular para o conteúdo
Quiz
← Início

Engenharia de software

150 questões

Certas
0
Erradas
0
Não respondidas
150
Dificuldade
Número de questões

150 questões neste filtro

Resumo teórico

Imagine construir uma casa na árvore com seus amigos. Se ela for pequena, você pega umas tábuas e começa. Mas agora imagine construir um arranha-céu, com centenas de operários e anos de trabalho. Você precisaria de plantas, desenhos, testes, um orçamento e um jeito de manter todo mundo trabalhando junto. Engenharia de software é a mesma ideia para programas de computador: é como as pessoas planejam, constroem, verificam e consertam software, principalmente quando o programa é grande e muita gente trabalha nele.

Por que isso importa? Software está em todo lugar: em celulares, carros, hospitais, bancos e jogos. Muitos projetos de software terminam atrasados, custam caro demais ou quebram quando as pessoas usam. A engenharia de software reúne as lições que as pessoas aprenderam do jeito difícil, para não repetirmos os mesmos erros.

Neste resumo você vai conhecer as ideias principais da área: como um projeto é organizado em etapas (processos), como descobrimos o que o cliente quer (requisitos), como desenhamos e projetamos o sistema, como escrevemos código limpo, como testamos, estimamos e gerenciamos um projeto, por que colocar mais gente em um projeto atrasado pode atrasá-lo ainda mais, como fazer software que não machuca ninguém e como as equipes trabalham de forma ágil. Leia as seções em ordem, ou pule para a que precisar, e depois faça o quiz.

O panorama geral e o vocabulário

Fazer um sanduíche é fácil. Administrar um restaurante que serve mil sanduíches por dia é difícil. Do mesmo jeito, escrever um programa pequeno é fácil, mas construir um software que muita gente usa por muitos anos é difícil. A engenharia de software é a disciplina (um conjunto de métodos cuidadosos) que torna o caso difícil administrável.

Todo projeto de software, grande ou pequeno, precisa de quatro atividades básicas. Pense em assar um bolo de aniversário: você decide qual bolo o convidado quer, planeja a receita, assa e prova, e depois conserta ou muda para a próxima festa.

As quatro atividades fundamentais de qualquer processo de software
AtividadePergunta que respondeExemplo do bolo
EspecificaçãoO que o sistema deve fazer?O convidado quer um bolo de chocolate sem nozes
Projeto e implementaçãoComo vamos construir?Escolher a receita e assar o bolo
ValidaçãoÉ mesmo o que se queria?Provar e conferir se não tem nozes
EvoluçãoComo mudamos depois?No ano seguinte o convidado quer um bolo de baunilha

Um [[stakeholder|qualquer pessoa com interesse no sistema: usuários, clientes, gerentes, testadores, vizinhos que são afetados]] é qualquer pessoa que se importa com o resultado. Um requisito é algo que o sistema deve fazer ou um limite que ele deve respeitar. Um processo é o conjunto ordenado de atividades usado para construir software.

↑ Voltar ao sumário

Modelos de processo: cascata, incremental, ágil

Um modelo de processo é uma receita de como arranjar as quatro atividades. Não existe uma receita melhor para tudo. Uma boa equipe escolhe a que combina com o projeto, como escolher entre bicicleta, carro ou avião conforme a viagem.

Cascata: um passo depois do outro

No modelo em cascata (waterfall) as fases rodam em sequência, como água caindo em degraus. Cada fase deve ser terminada e aprovada antes de a próxima começar. Ele é um .

Requisitos
     |
     v
  Projeto
     |
     v
Implementação
     |
     v
  Testes
     |
     v
Manutenção
Cascata: cada fase desce para a próxima

A cascata combina quando os requisitos são conhecidos e estáveis, e quando erros são muito caros. Por exemplo, o software de controle de um dispositivo médico construído junto com seu hardware, sob regras de segurança, precisa de planejamento cuidadoso e muitos documentos. Sua fraqueza é que mudar de ideia tarde é caro.

O modelo em V

O modelo em V é um processo dirigido por plano desenhado como um V. Cada plano de teste é preparado a partir de uma fase do lado esquerdo, para que o teste correspondente do lado direito possa conferi-la. Por exemplo, o plano de teste de aceitação vem da especificação de requisitos: as necessidades do cliente são conferidas contra o sistema final.

Requisitos ---------------> Teste de aceitação
  Projeto  ---------------> Teste de sistema
     Código --------------> Teste de unidade
Cada fase da esquerda tem um teste correspondente na direita

Desenvolvimento incremental

No desenvolvimento incremental você constrói o sistema em pedaços pequenos chamados incrementos, e entrega cada pedaço ao cliente para experimentar. É como montar um castelo de LEGO uma torre por vez e mostrar cada torre ao seu amigo. O custo de acomodar mudanças nos requisitos do cliente é menor, e o cliente recebe software útil cedo.

Engenharia orientada ao reuso

Por que assar o pão se você pode comprá-lo? Na engenharia de software orientada ao reuso o sistema é construído principalmente com componentes existentes, como um pacote comercial pronto (COTS). A etapa que não tem equivalente direto nos outros processos é modificar os requisitos para combinar com os componentes disponíveis. Um risco é perder o controle sobre como o sistema evolui, porque as novas versões do pacote são decididas pelo fornecedor.

O modelo espiral e o RUP

O modelo espiral de Boehm anda em voltas. O que o torna especial é que ele faz o reconhecimento explícito e a redução de risco parte de cada volta. Se a equipe não sabe se os usuários vão aceitar uma nova interface de voz, a espiral gasta aquela volta em avaliação e redução de risco, por exemplo construindo um pequeno teste.

O Rational Unified Process (RUP) tem quatro fases, em ordem: concepção (inception), elaboração, construção e transição. No fim da elaboração a equipe deve entender o domínio do problema, ter uma arquitetura e um plano, e ter identificado os principais riscos. Sommerville descreve o RUP por três perspectivas: dinâmica (as fases ao longo do tempo), estática (as atividades do processo, como os fluxos de modelagem de negócio e requisitos) e prática (boas práticas).

Comparação rápida dos modelos de processo genéricos
ModeloIdeia principalMelhor quando
CascataFases em sequência, aprovadasRequisitos estáveis, segurança, hardware envolvido
IncrementalEntregar em pedaços pequenosOs requisitos vão mudar, o cliente quer valor cedo
Orientado ao reusoMontar componentes existentesExistem bons componentes
EspiralVoltas guiadas pelo riscoProjetos de alto risco
RUPQuatro fases, iterações dentroProjetos grandes com várias visões

Protótipos e lidar com mudanças

Um protótipo é uma versão inicial e rústica do sistema usada para testar ideias. Um protótipo descartável rápido não deve virar o produto final, porque foi feito para ser rápido: ignora requisitos não funcionais e é mal estruturado e difícil de manter. Sommerville separa duas formas de reduzir o custo do retrabalho: evitar mudanças (por exemplo, um protótipo que mostra o sistema ao cliente cedo, antes do trabalho real) e tolerar mudanças (por exemplo, a entrega incremental, em que uma mudança afeta só um pequeno pedaço). Alguns clientes, como uma agência do governo cujas regras de compra exigem uma especificação completa antes do contrato, não conseguem usar a entrega incremental com facilidade, pois ela desenvolve a especificação junto com o software.

↑ Voltar ao sumário

Engenharia de requisitos

Antes de construir uma casa você precisa perguntar à família o que ela quer: quantos quartos, quanto dinheiro, perto de qual escola? Engenharia de requisitos é o trabalho de descobrir, escrever e conferir o que um sistema deve fazer e os limites que ele deve respeitar.

Requisitos de usuário e de sistema

Requisitos de usuário são afirmações de alto nível, escritas para os clientes, sobre o que o sistema deve fazer. Requisitos de sistema descrevem as mesmas coisas com mais detalhe, para os desenvolvedores. Pense no cardápio (o que você pode pedir) e na receita da cozinha (exatamente como é feito).

Funcionais e não funcionais

Requisitos funcionais contra não funcionais
TipoSignificadoExemplo
FuncionalUm serviço ou comportamento que o sistema ofereceUm usuário pode pesquisar o catálogo por título
Não funcionalUma qualidade ou restrição sobre o sistemaA busca no catálogo deve devolver resultados em até 2 segundos para 95% das consultas

Requisitos não funcionais costumam ser mais críticos do que os funcionais individuais, porque falhar em um deles pode tornar o sistema inteiro inutilizável. Um avião rápido e bonito que não é seguro não serve. Sommerville os classifica como requisitos de produto (como o produto se comporta: velocidade, confiabilidade), organizacionais (regras da empresa, como um processo de desenvolvimento obrigatório) e externos (fatores fora do sistema e do seu desenvolvimento, como uma lei ou a regra de um órgão regulador).

Torne os objetivos testáveis

Um cliente que diz 'o sistema deve ser fácil de usar' dá um objetivo vago. Uma boa reescrita o torna mensurável: 'Depois de quatro horas de treinamento, os atendentes devem cometer em média no máximo dois erros por hora de uso.' Um requisito que você não consegue verificar é um requisito que você não tem como saber se cumpriu.

Descobrindo requisitos: elicitação

  • Entrevistas com os stakeholders.
  • [[Etnografia|um analista passa tempo no local de trabalho e observa como as pessoas realmente trabalham]]: ótima para a prática real, mas fraca para achar requisitos de funcionalidades novas e inovadoras que mudam o jeito de trabalhar.
  • Cenários e histórias: uma descrição de como um usuário faz uma tarefa. Um cenário de saque em um caixa eletrônico inclui o fluxo normal e também o que pode dar errado, como uma senha errada ou saldo insuficiente, e como isso é tratado.

Quando dois stakeholders querem coisas opostas (a chefe de enfermagem quer toda mudança de medicação confirmada duas vezes, os médicos querem rapidez), o engenheiro faz a priorização e negociação de requisitos para resolver o conflito.

Escrevendo e conferindo requisitos

Um documento de requisitos deve descrever o que o sistema deve fazer, e não como fazer. Use a linguagem de forma consistente: shall (deve) marca um requisito obrigatório, should (convém) um desejável. A linguagem natural é fácil de ler, mas pode ser imprecisa: 'lembrar o paciente antes de cada consulta' não diz como (SMS ou e-mail) nem quando, então defina o canal e o momento com precisão, de preferência em uma forma estruturada.

A validação confere se os requisitos são o que o cliente realmente quer. Verificações úteis: a verificação de realismo (dá para construir com a tecnologia e o orçamento disponíveis? exigir renderização 3D em tempo real em um dispositivo minúsculo não passa) e a geração de casos de teste (se você não consegue escrever um teste para um requisito, ele provavelmente é vago ou ambíguo).

Requisitos mudam. Um bom processo de mudança tem três passos em ordem: análise do problema e especificação da mudança, depois análise da mudança e cálculo de custo, depois implementação da mudança. A rastreabilidade registra, para cada requisito, quem o pediu e quais outros requisitos e partes do projeto dependem dele, para que você avalie o impacto de uma mudança proposta antes de decidir.

↑ Voltar ao sumário

Modelagem de sistemas com UML

Antes de construir uma cidade de brinquedo, você desenha um mapa. Um modelo é uma imagem simplificada de um sistema que mostra o que importa e esconde o resto. A Unified Modeling Language (UML) é um conjunto padrão de diagramas para desenhar sistemas de software, para que todos leiam as figuras do mesmo jeito.

Sommerville descreve quatro perspectivas para modelar: externa (o contexto e o ambiente), de interação (como as coisas conversam entre si), estrutural (a organização do sistema e de seus dados) e comportamental (o que o sistema faz enquanto roda).

Modelo de contexto

Um modelo de contexto é desenhado nas etapas iniciais para definir as fronteiras do sistema: quais outros sistemas estão no seu ambiente. Ele decide o que está dentro da caixa e o que está fora.

Diagrama de casos de uso

Um diagrama de casos de uso mostra os atores (pessoas ou outros sistemas) e as interações que eles têm com o sistema, mas não a ordem delas. Ele responde 'quem faz o quê com o sistema?'

Diagrama de sequência

Um diagrama de sequência mostra a ordem das mensagens entre objetos, com o tempo correndo para baixo. Neste, o caixa eletrônico pede ao Banco que verifique a senha antes de perguntar ao cliente o valor.

Cliente        Caixa        Banco
   | insere cartão|             |
   |------------->| verificar   |
   |              |  senha      |
   |              |------------>|
   |              |<------------|
   |  pede valor  |  senha ok   |
   |<-------------|             |
O tempo corre para baixo; as setas são mensagens

Diagrama de classes

Um diagrama de classes mostra as classes (tipos de coisas) e as relações entre elas. Os números sobre uma linha são a multiplicidade. Ler Departamento 1 --- 1..* Funcionário significa que cada departamento tem pelo menos um funcionário, e cada funcionário pertence a exatamente um departamento.

Relações comuns entre classes
RelaçãoSignificadoExemplo
Generalização'é um tipo de' (herança)Um caminhão é um tipo de veículo
Agregação'tem partes' (um todo e suas partes)Um carro tem rodas e um motor
AssociaçãoDuas classes estão ligadasUm cliente faz pedidos

Diagramas de estados e de atividades

Um diagrama de estados mostra os estados de uma coisa e os eventos que a levam de um para outro. Uma máquina de venda no estado Pronta, quando o estoque é zero e alguém aperta o botão, continua em Pronta e mostra 'esgotado': o diagrama diz o que acontece para cada evento em cada estado.

Um diagrama de atividades mostra o fluxo de trabalho. Uma barra grossa com uma entrada e várias saídas é um fork (bifurcação): os caminhos rodam em paralelo. Uma barra grossa com várias entradas e uma saída é um join (junção): ela espera todos os caminhos de entrada terminarem. Assim, 'Enviar pedido' depois de uma junção só começa depois que 'Cobrar cartão' e 'Embalar itens' terminarem.

Engenharia dirigida por modelos

A arquitetura dirigida por modelos (MDA) usa três tipos de modelo: o modelo independente de computação (o domínio), o modelo independente de plataforma (PIM), que descreve o sistema sem referência a uma plataforma, e o modelo específico de plataforma (PSM). A promessa é gerar código para várias plataformas a partir dos modelos. Na prática é difícil de adotar porque abstrações boas para discutir um projeto nem sempre servem para a implementação, e as ferramentas e o esforço de manter modelos e código em sincronia podem custar mais do que economizam.

↑ Voltar ao sumário

Visão geral do projeto de arquitetura

Antes de construir uma casa, o arquiteto decide quantos andares ela tem e por onde passam as paredes e os canos. Em software, o projeto de arquitetura se preocupa em identificar os principais componentes estruturais de um sistema e as relações entre eles. É a ligação entre os requisitos e o projeto detalhado.

Visões: o modelo 4+1

Um desenho só não mostra tudo sobre uma casa. O modelo de visões 4+1 usa várias visões: a visão lógica (as abstrações-chave), a visão de processo (como o sistema roda, quais partes são concorrentes), a visão de desenvolvimento (como o software é dividido em pedaços para os desenvolvedores) e a visão física (hardware e onde as coisas são implantadas). O '+1' são os casos de uso, que amarram as visões.

Padrões arquiteturais

Quatro padrões arquiteturais comuns
PadrãoIdeiaBom para
MVC (Model-View-Controller)O modelo guarda os dados, a visão os mostra, o controlador recebe interações do usuário como teclas e cliques do mouseApps interativos com várias visões dos mesmos dados
Em camadasCada camada usa só os serviços da camada de baixo; uma camada pode ser trocada por outra com a mesma interfaceSistemas construídos em níveis, troca fácil
RepositórioTodas as ferramentas trabalham em um armazenamento central compartilhadoUma IDE com editor, verificador de tipos, ferramenta de refatoração
Pipe e filtroOs dados fluem por filtros que os transformamProcessamento em lote e fluxos de dados
+--------------------+
|  Interface usuário |
+--------------------+
|  Lógica de negócio |
+--------------------+
|  Armazenamento     |
+--------------------+
Arquitetura em camadas: cada camada fala com a de baixo

Um projeto pipe e filtro funciona bem para um processamento em lote noturno que lê pagamentos, confere e escreve relatórios, porque é feito para fluxos de dados. Ele serve mal para sistemas interativos, pois estes reagem a eventos imprevisíveis do usuário.

Arquitetura e requisitos não funcionais

As escolhas da arquitetura decidem as qualidades do sistema. Se o requisito mais importante é a manutenibilidade, use componentes pequenos e autocontidos que mudam com facilidade, com os produtores de dados separados dos consumidores. Se é o desempenho, use menos componentes, maiores, para reduzir a comunicação. Sommerville diz que ter os dois juntos é difícil porque o desempenho favorece poucos componentes grandes, enquanto a manutenibilidade favorece muitos pequenos.

Assista ao vídeo: Visualizando a arquitetura de software com o modelo C4 (Simon Brown)↑ Voltar ao sumário

Projeto, implementação e código limpo

Escrever código é como escrever uma carta que outras pessoas precisam ler por anos. Até você, um mês depois, será um 'estranho' para o seu próprio código. Esta seção fala de projetar com ideias comprovadas e de escrever código fácil de ler.

Padrões de projeto

Um padrão de projeto (design pattern) é a descrição de um problema recorrente e da essência da sua solução, abstrata o bastante para ser reutilizada em contextos diferentes. O padrão Observer é um exemplo: quando uma célula da planilha muda, a tabela, o gráfico de barras e o gráfico de pizza se atualizam, porque são observadores avisados da mudança pelo sujeito.

Reuso e código aberto

O reuso acontece em vários níveis. Quando uma equipe não copia código algum, mas aplica o conhecimento de um projeto bem-sucedido, esse é o nível de abstração. As licenças de código aberto diferem. A GNU General Public Licence (GPL) é recíproca (copyleft): se você distribui software que inclui código GPL, deve liberar o seu software também sob a GPL. Licenças como BSD e MIT são permissivas: uma empresa pode incluir uma biblioteca BSD em um produto de código fechado e modificá-la, mantendo o próprio código privado, se mantiver os avisos.

Sommerville lista quatro atividades fundamentais de gerência de configuração durante a implementação: gerência de versões, construção do sistema, gerência de mudanças e rastreamento de problemas (problem tracking, também chamado de issue tracking), entre elas.

Código limpo: nomes

No Clean Code, um nome deve revelar sua intenção. Uma declaração como int d; // tempo decorrido em dias deve ser renomeada para elapsedTimeInDays, e o comentário removido. O nome agora diz o que o comentário dizia.

// Ruim: o que é d? por que 86400000?
let d = (end - start) / 86400000;

// Bom: o nome conta a história
const msPerDay = 86_400_000;
const elapsedTimeInDays = (end - start) / msPerDay;
Nomes que revelam a intenção

Código limpo: funções

  • Uma função deve fazer uma coisa, em um nível de abstração.
  • Um parâmetro booleano é um mau sinal: mostra que a função faz duas coisas. Divida exportReport(true) em exportReportAsPdf e exportReportAsCsv.
  • Não devolva null no lugar de uma lista; devolva uma lista vazia, para os chamadores percorrerem sem uma checagem especial (uma checagem de null esquecida pode derrubar o programa).
  • Siga a Lei de Demeter: um método deve falar só com seus colaboradores imediatos, sem se enfiar fundo nos objetos (a.getB().getC().doIt()).

Código limpo: comentários, formatação, classes, testes

A visão de Martin sobre comentários: um comentário muitas vezes compensa um código que não se expressa bem, então prefira código claro e guarde os comentários para o que o código não consegue dizer (a razão de uma decisão, um aviso). A formatação segue a metáfora do jornal: ponha os conceitos de alto nível no topo e os detalhes mais abaixo, para o leitor poder parar quando já tiver o bastante.

O Princípio da Responsabilidade Única diz que uma classe deve ter um único motivo para mudar. Uma classe que muda quando a equipe de folha de pagamento muda as regras de pagamento, quando a equipe de banco de dados muda o esquema e quando o financeiro muda os relatórios tem vários motivos para mudar; divida-a. Testes limpos seguem a ideia do F.I.R.S.T.: rápidos (fast), independentes, repetíveis, autovalidáveis e oportunos (timely). Um teste que falha quando roda sozinho, mas passa na ordem, não é independente.

Por fim, as fronteiras: se o código chama uma biblioteca HTTP de terceiros em 40 arquivos, uma nova versão maior força 40 edições. Embrulhe a biblioteca atrás de uma pequena interface sua e escreva testes de aprendizado que verificam como a biblioteca se comporta, para que uma atualização mude um lugar só.

↑ Voltar ao sumário

Testes, evolução e manutenção

Uma ponte é testada antes de os carros passarem por ela. Com software é igual. Testar é executar um programa para procurar defeitos (bugs) e mostrar que ele faz o que deveria. A frase famosa de Dijkstra, que Sommerville repete, diz tudo: testes podem mostrar a presença de erros, nunca a ausência. Se 2.000 testes passam, você só sabe que aquelas 2.000 situações funcionam. Outras entradas e caminhos ainda podem esconder um bug. Por isso usamos também revisões e outras verificações, e escolhemos os casos de teste com cuidado em vez de apenas acumular muitos.

Etapas de teste

Teste de desenvolvimento, de release e de usuário
EtapaO que é testadoQuem
Teste de unidadeUma função, método ou classe, sozinhaDesenvolvedores
Teste de componenteVárias unidades trabalhando juntas, pela sua interface (bug típico: uma suposição errada entre unidades)Desenvolvedores
Teste de sistemaOs componentes juntos, e como interagemA equipe de desenvolvimento
Teste de releaseUma versão feita para uso fora da equipe, para checar se faz o que foi especificadoUma equipe separada, caixa-preta
Teste de usuárioAlfa (usuários trabalham com os desenvolvedores), beta (usuários escolhidos testam no próprio ambiente), aceitação (o cliente decide aceitar)Usuários e clientes

As três primeiras etapas formam o teste de desenvolvimento: são feitas pela equipe que constrói o sistema e caçam bugs (teste de defeitos). O teste de release é diferente. Uma equipe que não construiu o sistema o testa como caixa-preta (sem olhar o código), usando os requisitos, para mostrar que ele é bom o bastante para ser liberado. Isso é teste de validação. Só a última etapa, o teste de aceitação, é feita pelo cliente.

Escolhendo casos de teste

Não dá para testar todas as entradas possíveis. O particionamento em classes de equivalência divide as entradas em grupos (partições) que o programa deve tratar do mesmo jeito, e você testa um valor de cada grupo. Os bugs se juntam nas bordas dos grupos, onde um < é facilmente confundido com um <=. Então teste também os valores de fronteira: o valor em cada borda e os valores logo fora dela.

  regra: ingresso grátis só para idades de 0 a 12

  partição:    abaixo de 0 |   0 .. 12   | acima de 12
  fronteiras:        -1 | 0         12 | 13
  testes:            -1      0   6   12    13
Três partições, testes dos dois lados de cada borda
function isAdultWorker(age: number): boolean {
	return age >= 18 && age <= 65;
}
// testes de fronteira: 17 -> false, 18 -> true, 65 -> true, 66 -> false
Teste as bordas de um intervalo

Quanto do código os testes visitaram?

A cobertura diz quanto do código os testes executam. Existem vários critérios, e eles não são iguais. A cobertura de comandos exige que cada linha rode ao menos uma vez. A cobertura de ramos exige que cada decisão (um if) seja vista indo para os dois lados, verdadeiro e falso. A cobertura de caminhos exige todo trajeto possível pela função, e só ela roda todas as combinações de decisões.

function price(base: number, student: boolean, voucher: boolean): number {
	let total = base;
	if (student) total -= 5;
	if (voucher) total -= 3;
	return total;
}
Duas decisões independentes, uma depois da outra
A mesma função, três critérios
CritérioO que exigeTestes necessários aqui
ComandosCada comando roda uma vez1: (true, true)
RamosCada decisão vai para verdadeiro e falso2: (true, true) e (false, false)
CaminhosTodo trajeto pela função4: todas as combinações dos dois flags

Com n decisões independentes em sequência existem 2^n caminhos: 10 decisões dão 1.024 caminhos e 20 dão 1.048.576. Por isso a cobertura total de caminhos raramente é prática. E 100% de cobertura de comandos ainda pode deixar passar um bug: o caso (true, false) acima nunca rodou no único teste de comandos.

Desenvolvimento guiado por testes e regressão

No desenvolvimento guiado por testes (TDD) o teste vem antes do código. Você trabalha em rodadas bem curtas, um pedacinho de funcionalidade por vez:

  1. Escreva um teste automatizado para o próximo pedacinho.
  2. Rode e veja o teste falhar. Isso prova que o teste realmente consegue detectar algo, e que o código ainda não existe.
  3. Escreva só o código suficiente para ele passar.
  4. Rode todos os testes de novo e limpe o código (refatore) enquanto eles continuam verdes.

Uma regressão é um bug em algo que antes funcionava. O teste de regressão roda os testes antigos de novo após cada mudança, porque uma mudança pode quebrar código que parece não ter relação. Feito à mão é lento, então as pessoas pulam. Com testes automatizados a suíte inteira pode rodar a cada mudança, e é por isso que o TDD e a integração contínua adoram automação.

Evolução e manutenção

O software não termina quando é entregue: ele continua vivo e muda. Isso é a evolução do software, também chamada de manutenção. Sommerville cita três tipos. Repare que consertar bugs é a menor parte. A maior parte da manutenção é evolução, não correção de bugs.

Três tipos de manutenção
TipoTambém chamado deExemplo
Reparo de falhasCorretivaConsertar um total errado em uma nota fiscal
Adaptação ao ambienteAdaptativaUm sistema de folha de pagamento que funciona bem precisa mudar porque o sistema operacional será substituído (nada está errado, o entorno mudou)
Adição de funcionalidadePerfectivaOs usuários pedem um relatório novo
Parcela do esforço de manutenção por tipo (Sommerville)
Reparo de falhas
17 %
Adaptação ao ambiente
18 %
Adição de funcionalidade
65 %

Lehman e Belady estudaram como os sistemas grandes mudam ao longo dos anos e escreveram leis. As duas mais conhecidas explicam por que gastamos tempo limpando:

  • Mudança contínua: um sistema usado no mundo real precisa continuar mudando, ou fica cada vez menos útil.
  • Complexidade crescente: ao mudar, sua estrutura piora e se embaralha, a menos que alguém trabalhe para mantê-la simples.
  • Crescimento contínuo: ele precisa ganhar funcionalidades para manter os usuários satisfeitos.
  • Conservação da familiaridade: a quantidade de mudança em cada release fica mais ou menos igual ao longo do tempo, porque as pessoas precisam continuar entendendo o sistema.
  • Estabilidade organizacional: o ritmo de desenvolvimento fica mais ou menos constante, não importa quantas pessoas você acrescente.

Por que mudar depois da entrega custa mais

A mesma mudança custa mais depois da entrega do que durante o desenvolvimento. Sommerville dá razões:

  • Estabilidade da equipe: os desenvolvedores saem, e os mantenedores precisam reconstruir o entendimento. O código não registra por que foi projetado daquele jeito.
  • Responsabilidade contratual: quando uma empresa constrói e outra mantém, quem constrói não ganha nada por deixar o sistema fácil de mudar. Pular esse trabalho economiza dinheiro de quem construiu, e a próxima empresa paga a conta.
  • Habilidade da equipe: a manutenção costuma ir para pessoas menos experientes.
  • Idade e estrutura: programas velhos, mudados muitas vezes, são mais difíceis de ler.

Sistemas antigos: manter, consertar ou trocar?

Um sistema legado é um sistema antigo que ainda está em uso e é difícil de mudar. Para decidir o que fazer, Sommerville olha duas coisas: o valor para o negócio (o quanto as pessoas precisam dele) e a qualidade (quanto custa mudar e operar).

Os quatro grupos de sistemas legados
Valor para o negócioQualidadeO que fazer
BaixoBaixaDescartar
AltoBaixaReengenharia, ou trocar por um sistema adequado
BaixoAltaManter com manutenção normal enquanto for barato, ou descartar
AltoAltaContinuar com a manutenção normal

Refatoração e reengenharia

As duas melhoram a estrutura do código sem mudar o que ele faz, mas em momentos diferentes.

Duas formas de combater a deterioração
AspectoRefatoraçãoReengenharia
IdeiaPrevençãoCura
QuandoO tempo todo, em passos pequenos, durante o desenvolvimento e a evoluçãoUm projeto separado, depois de anos de manutenção, quando os custos já subiram
O quêRenomear, dividir um método longo, remover código duplicadoReestruturar, redocumentar, traduzir o código-fonte para uma linguagem mais nova, muitas vezes com ferramentas
SegurançaProtegida por testes automatizadosExige plano e orçamento
Assista ao vídeo: TDD, Where Did It All Go Wrong (Ian Cooper)↑ Voltar ao sumário

Gerência de projetos e estimativas

Planejar um passeio da escola é escolher uma data, um ônibus, um orçamento e quem vai. A gerência de projetos faz isso para software: garante que o software seja entregue no prazo, dentro do orçamento e atenda às necessidades do cliente.

Gerência de riscos

Um risco é algo que pode dar errado. Se a única desenvolvedora que conhece a integração de pagamentos anuncia que vai sair, isso é um risco de projeto, porque ameaça o cronograma e os recursos. O processo de gerência de riscos, em ordem, é: identificação, análise, planejamento, monitoramento. Na análise você estima duas coisas para cada risco: sua probabilidade e a gravidade dos seus efeitos. Um plano de contingência é o que você faz se o risco acontecer (por exemplo, se o banco de dados pode não aguentar o pico de carga, você prepara um plano para adicionar capacidade ou cache, sem mudar o projeto agora).

Pessoas e equipes

Os desenvolvedores se motivam de formas diferentes: pessoas orientadas à tarefa gostam do trabalho em si, pessoas orientadas a si mesmas são movidas pelo sucesso pessoal, pelo reconhecimento e pelo dinheiro, e pessoas orientadas à interação gostam da companhia dos colegas. Uma equipe muito coesa pode cair no pensamento de grupo (groupthink): ela continua aprovando as próprias decisões sem crítica séria. A comunicação tem custo: com n pessoas, o número de canais entre duas pessoas é n(n-1)/2.

Canais de comunicação possíveis entre n pessoas
n = 2
1 canais
n = 4
6 canais
n = 8
28 canais
n = 10
45 canais
n = 20
190 canais

Planejamento e cronograma

Um entregável (deliverable) é um produto de trabalho entregue ao cliente. Um marco (milestone) é um ponto de verificação em que o progresso pode ser avaliado (pode não ser entregue a ninguém). As atividades têm durações e dependências. O caminho crítico é a cadeia mais longa de atividades dependentes; ele decide o menor tempo em que o projeto pode terminar.

Um pequeno plano
AtividadeDuração (dias)Depende de
A: Projeto3nenhuma
B: Codificar módulo 15A
C: Codificar módulo 27A
D: Integrar2B, C

Neste plano, A leva 3 dias. B e C começam depois de A e rodam em paralelo. D só começa quando B e C terminam, isto é, depois do dia 3 + 7 = 10. D termina no dia 12. O caminho crítico é A, C, D = 3 + 7 + 2 = 12 dias. B tem folga, pode atrasar 2 dias sem efeito.

Estimando

Em equipes ágeis, a velocidade (velocity) é o número de story points concluídos por sprint. Se uma equipe fez 30, 34 e 26 pontos, a média é 30, então 150 pontos restantes levam cerca de 5 sprints. Um modelo algorítmico de custo tem a forma Esforço = A × Tamanho^B × M. Se B é maior que 1 (por exemplo 1,1), o esforço cresce mais rápido que o tamanho: dobrar o tamanho multiplica o esforço por 2^1,1, cerca de 2,14, e não 2. M é o produto dos multiplicadores dos direcionadores de custo (como no COCOMO II). Direcionadores avaliados longe do nominal são multiplicados entre si. Por exemplo, uma estimativa nominal de 100 pessoas-mês com multiplicadores 1,1, 1,05 e 0,98 dá cerca de 113 pessoas-mês.

Quando um cliente quer orçamento fixo e data de entrega fixa com um processo ágil, fixe prazo e custo, entregue primeiro as funcionalidades de maior prioridade e negocie quanto do escopo é entregue.

↑ Voltar ao sumário

Brooks: The Mythical Man-Month e No Silver Bullet

Fred Brooks liderou a construção do OS/360 da IBM e escreveu sobre isso em The Mythical Man-Month (1975). Suas lições continuam verdadeiras hoje.

O homem-mês mítico e a lei de Brooks

Um homem-mês é o trabalho de uma pessoa em um mês. O mito é achar que pessoas e meses podem ser trocados: que 12 pessoas-mês podem ser 12 pessoas por 1 mês. Isso só funciona quando a tarefa se divide entre trabalhadores que não precisam se comunicar, como colher maçãs. Quando as tarefas dependem umas das outras, mais gente significa mais conversa. A lei de Brooks: acrescentar pessoas a um projeto de software atrasado o atrasa ainda mais. Os novatos precisam ser treinados por quem já faz o trabalho, o trabalho precisa ser dividido de novo, e cada pessoa extra acrescenta caminhos de comunicação.

A lei é uma simplificação deliberada. Acrescentar pessoas tem menos chance de prejudicar quando elas entram cedo, a tarefa se divide em partes independentes e elas já conhecem o domínio.

A regra de cronograma de Brooks

A regra prática de Brooks para dividir um cronograma
FaseParte do cronograma
Planejamentoum terço
Codificaçãoum sexto
Teste de componentes e início do teste de sistemaum quarto
Teste de sistema, com todos os componentes prontosum quarto

Repare que codificar é só um sexto; a maior parte do tempo vai para planejamento e testes.

A equipe cirúrgica

Brooks descreveu, a partir de uma proposta de Harlan Mills, uma equipe cirúrgica: um programador-chefe projeta e codifica, e os outros o apoiam em papéis especializados. A razão é que a produtividade dos programadores varia cerca de dez vezes, e poucas mentes dão um projeto mais coerente.

Integridade conceitual e o arquiteto

A integridade conceitual é a consideração mais importante no projeto de sistemas: o sistema deve refletir um conjunto coerente de ideias de projeto, mesmo que isso signifique deixar de fora algumas boas funcionalidades. Brooks separa o arquiteto dos implementadores. O arquiteto decide a especificação completa do que o usuário vê: os comandos, seus parâmetros e seu comportamento. Como é construído por dentro pertence aos implementadores.

O efeito do segundo sistema e 'planeje jogar um fora'

O efeito do segundo sistema: o segundo sistema de um projetista tende a ser superprojetado, carregado das ideias que ficaram guardadas do primeiro. Em 1975 Brooks aconselhou: 'planeje jogar um fora; você vai jogar de qualquer jeito.' Mais tarde ele julgou o conselho simples demais, porque supunha o modelo em cascata; ele passou a favorecer o crescimento incremental do sistema.

No Silver Bullet (1986)

Uma bala de prata mata monstros; as pessoas desejavam uma ferramenta que matasse a crise do software. A afirmação central de Brooks: nenhuma técnica ou método de gestão isolado vai, dentro de uma década, dar nem mesmo uma melhoria de dez vezes em produtividade, confiabilidade ou simplicidade. Ele separa as dificuldades essenciais, inerentes à estrutura conceitual do software (complexidade, conformidade, mutabilidade, invisibilidade), das dificuldades acidentais, que vêm das nossas ferramentas (como gerenciar registradores e endereços de memória à mão em assembly).

  • Complexidade: o software tem estados e partes demais.
  • Conformidade: ele precisa seguir muitas regras arbitrárias do mundo externo, como leis de impostos, acordos sindicais e formatos de bancos.
  • Mutabilidade: pedem a ele o tempo todo que mude.
  • Invisibilidade: ele não tem uma forma natural para desenhar.

Exemplo de conta: suponha que 60% do esforço seja acidental e 40% essencial. Mesmo se você remover toda a parte acidental, sobram 40%, então o tempo cai para 0,4 e o ganho é 1 / 0,4 = 2,5 vezes, e não 10.

Ganho máximo se só o trabalho acidental for removido
Acidental 90%
10 x mais rápido
Acidental 60%
2.5 x mais rápido
Acidental 50%
2 x mais rápido
↑ Voltar ao sumário

Qualidade e gerência de configuração

Uma fábrica de chocolate confere cada lote e guarda o registro de cada versão da receita. As equipes de software fazem o mesmo com a gerência de qualidade (conferir se o software é bom) e a gerência de configuração (acompanhar cada versão e cada mudança).

Padrões, revisões e inspeções

Padrões de produto valem para o produto (por exemplo, uma convenção de nomes). Padrões de processo definem como o trabalho é feito, como um procedimento que toda solicitação de mudança deve seguir antes de ser aprovada.

Uma inspeção de código é uma leitura do código em grupo para achar defeitos. Sua vantagem real sobre o teste é que ela acha muitos defeitos em uma sessão, porque um defeito não esconde os outros (em um teste, uma queda pode esconder os problemas seguintes). Numa inspeção formal, o moderador planeja a reunião e mantém a discussão focada em achar defeitos, e não em consertá-los.

Métricas

Uma métrica é algo que medimos. Uma métrica de controle mede o processo (por exemplo, o tempo médio para consertar um defeito reportado), enquanto uma métrica de previsão mede o produto (por exemplo, a complexidade ciclomática). Cuidado: a complexidade é um atributo interno; ela diz algo sobre um atributo externo como a manutenibilidade só se a relação foi validada. A densidade de defeitos é defeitos por mil linhas de código (KLOC): 45 defeitos em 15.000 linhas dá 45 / 15 = 3 defeitos por KLOC.

Gerência de configuração

  • Controle de versão: registra cada mudança nos arquivos. Uma baseline é uma definição de uma versão específica do sistema que nomeia a versão exata de cada componente.
  • Controle de versão distribuído (como o Git): cada desenvolvedor tem uma cópia completa do repositório, com todo o histórico. Em um sistema centralizado há uma cópia no servidor.
  • Gerência de mudanças: uma solicitação de mudança é analisada quanto a custo, impacto e valor estratégico, e um comitê de controle de mudanças decide se a aprova.
  • Construção do sistema: uma ferramenta de build como o make reconstrói um alvo só quando uma de suas entradas diretas é mais nova, e os alvos reconstruídos se propagam em cascata. Se util.c muda, util.o é reconstruído, e depois o app final que depende dele.
  • Gerência de releases: para reproduzir um bug em uma versão antiga (como a 2.3 entregue há dois anos), você precisa das versões exatas do compilador, das bibliotecas, dos scripts de build e da configuração usados.
main.c --> main.o --\
                      +--> app
util.c --> util.o --/
Se só util.c muda: util.o e app são reconstruídos, main.o não

Integração contínua significa integrar com frequência na linha principal compartilhada. Antes de enviar, baixe a linha principal mais recente, construa o sistema com a sua mudança e rode os testes localmente, para que o build compartilhado continue saudável.

↑ Voltar ao sumário

Confiança e segurança (dependability e safety)

Quando você aperta o botão do elevador, você confia nele. Dependability (confiança no sistema) é o quanto os usuários podem confiar em um sistema. Para alguns sistemas, como um controlador de trem ou uma bomba médica, uma falha pode machucar pessoas.

As propriedades

Propriedades de dependability
PropriedadeSignificadoExemplo
DisponibilidadeA probabilidade de o sistema estar no ar e poder prestar os serviços quando pedidosUm site quase sempre acessível
ConfiabilidadeA probabilidade de operação sem falhas durante um tempo dadoUm programa que não trava enquanto você usa
Segurança (safety)O sistema não causa dano a pessoas nem ao ambienteUma bomba nunca dá uma dose mortal
Segurança (security)O sistema consegue se proteger de ataquesFechaduras e senhas
ResiliênciaO sistema continua prestando os serviços essenciais durante problemasUm sistema de hospital continua rodando em um backup

Disponibilidade e confiabilidade são diferentes. Um serviço web que cai uma vez por hora, mas reinicia sozinho em menos de um segundo sem os usuários perceberem, tem alta disponibilidade, mas baixa confiabilidade.

Disponibilidade como tempo parado: com 99,9% de disponibilidade em um ano de 8.760 horas, o tempo parado é 0,1% de 8.760, cerca de 8,8 horas.

Tempo parado por ano para cada nível de disponibilidade
99%
87.6 horas
99,9%
8.76 horas
99,99%
0.876 horas

Termos de safety e correção

Um perigo (hazard) é uma condição que pode levar a um acidente, por exemplo a possibilidade de uma bomba de insulina dar uma overdose. Um acidente é um evento não planejado que causa dano. Software correto nem sempre é seguro: se o software nunca se desviou da especificação, mas a própria especificação pode estar incompleta ou errada sobre os perigos, então o comportamento correto ainda pode ser inseguro.

Cadeia de eventos: uma falta (fault, um defeito no código) pode produzir um erro (um estado interno errado), que pode produzir uma falha (failure, o usuário vê um comportamento errado).

Redundância e diversidade

Redundância significa cópias extras, como ter um estepe. Diversidade significa que as cópias são diferentes, para não falharem pelo mesmo motivo: por exemplo, dois sensores de velocidade de fabricantes diferentes, construídos com princípios diferentes. Na programação N-versões, três equipes independentes implementam a mesma especificação e um votador toma a saída da maioria. A fraqueza: as equipes tendem a cometer erros parecidos, principalmente onde a especificação compartilhada está errada ou é difícil.

Security e safety podem entrar em conflito

Um sistema de hospital que bloqueia um usuário por 30 minutos depois de três logins errados é seguro contra invasão, mas numa emergência um médico que erra a senha não consegue acessar os dados do paciente. Uma medida de segurança pode reduzir a disponibilidade e, por meio dela, a safety.

↑ Voltar ao sumário

Métodos ágeis e lean startup

Imagine desenhar um quadro com um amigo, mostrando depois de algumas linhas e mudando conforme ele diz o que gosta. Esse é o espírito dos métodos ágeis: entregar pequenos pedaços funcionando, ouvir e se adaptar.

O Manifesto Ágil (2001)

  1. Indivíduos e interações mais que processos e ferramentas
  2. Software funcionando mais que documentação abrangente
  3. Colaboração com o cliente mais que negociação de contratos
  4. Responder a mudanças mais que seguir um plano

'Mais que' não quer dizer 'nunca'. O lado direito ainda tem valor; ele só fica em segundo lugar. Uma equipe que diz 'somos ágeis, então não escrevemos documentação nenhuma' leu o manifesto errado.

Extreme Programming (XP)

As práticas do XP incluem programação em par, desenvolvimento test-first, integração contínua, releases pequenos e propriedade coletiva: qualquer dupla de desenvolvedores pode mudar qualquer parte do código, e ninguém é dono de um módulo. O XP também avisa sobre o ritmo sustentável: muitas horas extras (como semanas de sessenta horas) reduzem a qualidade do código e a produtividade, então o ganho líquido é pequeno ou negativo.

Scrum

Papéis e eventos do Scrum
ElementoO que é
Product ownerOrdena o product backlog por valor
Scrum masterAjuda a equipe a seguir o Scrum e remove obstáculos
Equipe de desenvolvimentoConstrói o incremento
SprintUm período curto e fixo (geralmente de 1 a 4 semanas) que termina com um incremento funcionando
Retrospectiva da sprintA equipe se reúne sozinha no fim da sprint para discutir como trabalha e combinar melhorias

O ágil é mais difícil de aplicar, sem adaptação, a um sistema crítico para a segurança construído por várias equipes distribuídas, em que um órgão regulador precisa aprovar uma especificação e uma documentação detalhadas.

A Lean Startup

Eric Ries aplica o mesmo espírito de aprendizado às startups, que vendem sob incerteza extrema. O ciclo é construir, medir, aprender. Um produto mínimo viável (MVP) é a versão de um produto que permite à equipe completar um ciclo com o menor esforço.

   Ideias
   /    \
Aprender  Construir
   \    /
   Medir
O ciclo construir-medir-aprender

Para uma startup, o progresso é o aprendizado validado sobre os clientes. Entregar todas as funcionalidades no prazo sem evidência de que os clientes as querem não é progresso. As métricas de vaidade, como o total de cadastros, podem crescer sem que nada melhore. Use a análise de coortes, que acompanha grupos de clientes que entraram no mesmo período. Se 2% de cada coorte semanal paga, em toda coorte, então os clientes pagantes cresceram porque mais gente se cadastrou, e não porque o produto melhorou.

A contabilidade da inovação tem três passos, em ordem: estabelecer uma linha de base com um MVP, ajustar o motor em direção ao ideal e depois decidir se faz um pivô ou persevera. Um pivô é uma correção de rumo estruturada. Um pivô de zoom-in faz de uma única funcionalidade o produto inteiro (os clientes usam só a parte de controle de tempo de uma suíte grande de projetos, então construa só essa). Ries nomeia duas hipóteses de salto de fé: a hipótese de valor (o produto entrega valor?) e a hipótese de crescimento (como os novos clientes vão descobrir o produto?).

Assista ao vídeo: Agile is Dead (Dave Thomas, GOTO)↑ Voltar ao sumário

Simplicidade e dívida técnica

Um quarto bagunçado fica mais difícil de arrumar quanto mais você espera. Com código é igual. O livro Code Simplicity, de Max Kanat-Alexander, diz que todo software tem um propósito, ajudar pessoas, e que projetar é a arte de manter as coisas simples para que o software continue ajudando.

Dívida técnica

A metáfora de Ward Cunningham compara um atalho rápido e bagunçado a um empréstimo. Você ganha algo agora (velocidade) e paga depois. A metáfora tem três partes.

O empréstimo, o principal e os juros
No dinheiroNo código
O empréstimoO tempo que você ganha ao entregar a solução rápida
O principalO trabalho para trocar o atalho pela solução correta (pago uma vez, e a dívida acaba)
Os jurosO esforço extra de cada mudança feita em cima do atalho, pago de novo e de novo até o principal ser pago

Dívida nem sempre é errada: assumi-la para cumprir um prazo pode ser um bom negócio, se você planeja pagá-la. Mas os juros são cobrados por mudança, então dependem de quantas vezes o código é mexido e por quanto tempo ele vai viver. Dívida em código mudado toda semana é a dívida a pagar primeiro. Dívida em código que raramente muda, ou que será jogado fora logo, custa pouco.

Dias extras de esforço após mais 10 mudanças no módulo
Manter o atalho para sempre
10 dias
Projeto correto agora
4 dias
Trocar o atalho de imediato
6 dias
Trocar depois de 3 mudanças
9 dias

Os números vêm de um exemplo pequeno, contados em relação ao plano que pega o atalho. O atalho economiza 4 dias agora, cada mudança em cima dele custa 1 dia a mais, e trocá-lo depois custa 6 dias. Após 10 mudanças, manter o atalho custa 10 dias de juros, e fazer direito custa os 4 dias que o atalho economizou. Trocar de imediato custa 6, e esperar 3 mudanças custa 3 + 6 = 9. Mas se o módulo receber só 3 mudanças e depois for aposentado, manter o atalho custa apenas 3 e ganha. Com 4 mudanças, manter e fazer direito empatam (4 e 4).

Martin Fowler acrescenta que a dívida pode ser escolhida ou não, e sensata ou tola:

O quadrante da dívida técnica de Fowler
IntençãoPrudenteImprudente
DeliberadaPrecisamos entregar agora e lidaremos com as consequências depois do releaseNão temos tempo para projetar
InadvertidaAgora sabemos como deveríamos ter feitoO que é separação em camadas?

O custo da bagunça

Robert C. Martin abre o Clean Code com esta história. Uma equipe anda rápido no começo fazendo bagunça. Depois cada mudança demora mais, e a produtividade da equipe vai em direção a zero. Todos prometem limpar depois. Mas o depois traz novos prazos, então depois é igual a nunca (lei de LeBlanc). O único jeito de ir rápido é manter o código limpo o tempo todo.

A Regra do Escoteiro diz: deixe o acampamento mais limpo do que você o encontrou. No código, faça o check-in um pouco mais limpo do que encontrou: um nome melhor, uma função dividida em duas, uma duplicação removida. É o jeito mais barato de pagar a dívida, porque você já está com o código aberto e o entende. Ela pede passos pequenos, não uma reescrita a cada visita.

Às vezes a gerência pede um grande redesenho no céu: os melhores desenvolvedores constroem um sistema novo do zero enquanto os outros continuam estendendo o antigo. É uma corrida. O sistema novo precisa fazer tudo o que o antigo faz, e o antigo continua andando. Pode levar anos, e uma equipe com os mesmos hábitos vai bagunçar o código novo também.

  sistema antigo:  v1 -> v2 -> v3 -> v4 -> v5 ...  (clientes pedem mudanças)
  sistema novo:    começo ...... ainda correndo atrás ......

  a linha de chegada não para de se afastar
A reescrita persegue um alvo em movimento

Leis de Code Simplicity

  • Propósito: o propósito do software é ajudar pessoas. Um projeto é bom quando deixa o software o mais útil possível, continuar útil, e ser fácil para os programadores criarem e manterem.
  • Lei da simplicidade: a facilidade de manutenção de qualquer software é proporcional à simplicidade de suas partes individuais. Ninguém guarda um sistema grande inteiro na cabeça, então o que conta é se a parte que você está mudando pode ser entendida sozinha.
  • Lei da probabilidade de defeitos: a chance de introduzir um bug é proporcional ao tamanho da mudança.

O livro também traz a equação do projeto de software. A desejabilidade de uma mudança (D) cresce com o valor e diminui com o esforço, e os dois são divididos no tempo:

        Vn + Vf        Vn = valor agora     Vf = valor futuro
  D = -----------
        Ei + Em        Ei = esforço de implementar
                       Em = esforço de manter
A equação do projeto de software

A implementação é paga uma vez, mas a manutenção é paga enquanto o software viver, e o valor futuro também continua se somando. Então, para um software que vive por anos, reduzir o esforço de manutenção importa mais do que reduzir o esforço de implementação. Um projeto simples faz exatamente isso. A equação é um jeito de raciocinar sobre trade-offs, não uma calculadora que dá notas exatas.

Erros diante da mudança

O livro cita três jeitos de errar, e dois deles vêm de tentar adivinhar o futuro:

  • Escrever código que não é necessário (uma funcionalidade ou um sistema de plugins que ninguém pediu).
  • Não deixar o código fácil de mudar.
  • Ser genérico demais (construir para muitos casos quando existe só um).

A cura é construir só o que você sabe que é necessário, mantê-lo simples para que seja fácil de mudar, e generalizar quando aparecer um segundo caso real.

// Superprojetado: ninguém pediu outros formatos ainda
class ReportExporter { /* plugins, uma linguagem de config, 3 camadas... */ }

// Simples: resolve a necessidade de hoje
const toCsv = (rows: string[][]): string => rows.map((r) => r.join(",")).join("\n");
Resolva o problema de hoje

Mudanças pequenas são mais seguras

Imagine duas formas de consertar um bug: mudar 10 linhas, ou reescrever um arquivo inteiro de 600 linhas no caminho. Pela lei da probabilidade de defeitos, a reescrita tem muito mais chance de introduzir um bug novo, porque uma mudança 60 vezes maior tem 60 vezes mais lugares para dar errado. Código novo não tem os bugs antigos, mas tem os seus próprios. Então faça mudanças pequenas e passo a passo, para que cada uma seja fácil de conferir. Uma reescrita ainda pode ser justificada, mas nunca por ser a escolha segura.

↑ Voltar ao sumário

Juntando tudo

Todas as seções acima são partes de uma só história: você descobre do que as pessoas precisam (requisitos), desenha e projeta (UML, arquitetura), constrói de forma limpa, confere (testes, inspeções), gerencia pessoas, tempo e risco, e mantém vivo (evolução), sempre simples e seguro.

Ideias para lembrar
IdeiaQuemResumo em uma linha
Cascata vs incrementalSommervillePlanejar tudo antes vs entregar em pedaços
EspiralBoehmCada volta reduz risco
Lei de BrooksBrooksAcrescentar gente a um projeto atrasado o atrasa mais
No Silver BulletBrooksNenhuma ferramenta isolada dá ganho de dez vezes
Clean CodeMartinNomes, funções pequenas, responsabilidade única
Lean StartupRiesConstruir, medir, aprender; aprendizado validado
Dívida técnicaCunninghamAtalhos acumulam juros

Erros comuns

  • Achar que ágil quer dizer sem documentação ou sem plano.
  • Achar que mais gente sempre acelera um projeto.
  • Confundir disponibilidade com confiabilidade.
  • Escrever requisitos que não podem ser testados.
  • Refatorar sem testes.
  • Ler uma métrica (como a complexidade ciclomática) como se fosse a própria qualidade.
↑ Voltar ao sumário