O Ciclo de Vida e os Pilares da Engenharia de Software
O ciclo de vida de um software abrange o período desde a concepção inicial do produto até a sua descontinuação e retirada de uso. Este ciclo é tradicionalmente segmentado em fases estruturadas: levantamento de requisitos, planejamento, design, codificação, testes e manutenção. A engenharia de software sustenta essas fases através de três elementos fundamentais: ferramentas (o ambiente de suporte automatizado), métodos (as técnicas e abordagens técnicas para execução) e processos (o fluxo de trabalho e as etapas gerenciais).
Modelos de Processo de Desenvolvimento
A escolha do modelo de processo dita como as atividades do ciclo de vida serão organizadas e executadas.
Modelo em Cascata
Este modelo linear e sequencial exige que cada fase (requisitos, design, implementação, testes, manutenção) seja concluída e aprovdaa antes que a próxima se inicie. É orientado a documentos e ideal para projetos com requisitos estáveis e bem compreendidos. Suas principais limitações incluem a dificuldade em acomodar mudanças tardias e a descoberta de erros de design apenas nas fases finais de teste.
Modelo V e Modelos Evolutivos
O Modelo V estende o cascata associando cada fase de desenvolvimento à sua correspondente fase de teste, enfatizando a validação e verificação paralelas. Já os Modelos Evolutivos (como a Prototipagem) são iterativos, permitindo a construção de versões incrementais do software para refinar requisitos incertos. O Modelo em Espiral combina a natureza iterativa com uma forte ênfase em análise de riscos, sendo adequado para sistemas grandes, complexos e de alto risco.
Modelos Incrementais, Fonte e Unificado
- Incremental: O sistema é entregue em partes funcionais. O primeiro incremento geralmente contém as funcionalidades centrais (núcleo), permitindo feedback antecipado do usuário.
- Fonte (Fountain): Focado no desenvolvimento orientado a objetos, caracteriza-se pela sobreposição de fases (iteratividade) e ausência de barreiras rígidas entre análise, design e codificação.
- Processo Unificado (UP/RUP): Um framework iterativo e incremental orientado a casos de uso e centrado na arquitetura. Divide-se nas fases de Iniciação, Elaboração, Construção e Transição.
Paradigmas e Metodologias de Desenvolvimento
As metodologias definem a abordagem técnica e gerencial para a construção do software.
- Estruturada: Focada no processo e no fluxo de dados, utilizando a abordagem "top-down, stepwise refinement".
- Jackson: Orientada a estruturas de dados, mapeando a entrada e saída de dados para a arquitetura do programa.
- Orientada a Objetos (OO): Baseia-se em conceitos de encapsulamento, herança e polimorfismo, utilizando UML para modelagem.
- Ágil: Prioriza a entrega contínua de valor, adaptação a mudanças e colaboração intensa com o cliente. Frameworks como Scrum (iterações chamadas de Sprints) e Extreme Programming - XP (focado em práticas de engenharia como programação em par, TDD e integração contínua) são amplamente utiilzados.
Engenharia de Requisitos e Design de Sistema
A análise de requisitos captura as expectativas do usuário, dividindo-se em requisitos funcionais (o que o sistema deve fazer) e não funcionais (restrições de desempenho, segurança, usabilidade e ambiente).
O design de software é dividido em Design Arquitetural (ou de Alto Nível), que define a estrutura modular e as interfaces, e o Design Detalhado, que especifica os algoritmos e estruturas de dados internas. O princípio cardinal do design é alcançar alta coesão e baixo acoplamento. Outras regras de ouro incluem: minimizar a profundidade da árvore de chamadas, favorecer alto fan-in (muitos módulos usando um módulo comum) e baixo fan-out (um módulo chamando poucos outros), e garantir que o escopo de controle de um módulo contenha seu escopo de ação.
Estratégias e Técnicas de Teste de Software
O teste é o processo de execução de um programa com o objetivo de encontrar defeitos. As estratégias tradicionais progridem do微观 para o macro:
- Teste Unitário: Foca na lógica interna e estruturas de dados de módulos individuais, frequentemente utilizando stubs e drivers.
- Teste de Integração: Combina módulos testados. Pode ser Top-Down (usando stubs) ou Bottom-Up (usando drivers).
- Teste de Regressão: Reexecuta subconjuntos de testes para garantir que novas alterações não introduziram defeitos.
- Teste de Fumaça (Smoke Testing): Avalia a estabilidade básica do sistema integrado antes de testes mais rigorosos.
Métodos de Caixa Preta e Caixa Branca
O teste de Caixa Preta ignora a estrutura interna, focando em requisitos funcionais usando técnicas como Partição de Equivalência e Análise de Valor Limite. O teste de Caixa Branca examina a lógica interna e o fluxo de controle.
Para ilustrar a métrica de complexidade de McCabe e as técnicas de cobertura de caixa branca, considere o seguinte trecho de código:
def classificar_risco_cliente(idade, score_credito, renda_mensal):
nivel_risco = "baixo"
if idade > 65 or score_credito < 300:
if renda_mensal < 2000 and score_credito < 300:
nivel_risco = "critico"
else:
nivel_risco = "alto"
return nivel_risco
A Complexidade Ciclomática de McCabe é calculada pela fórmula V(G) = Arestas - Nós + 2 ou V(G) = Regiões Fechadas + 1. No grafo de fluxo de controle do código acima, existem 2 predicados compostos, o que resulta em um V(G) = 3, indicando que são necessários no mínimo 3 caminhos básicos independentes para cobrir a lógica.
As técnicas de cobertura lógica incluem: Cobertura de Instrução (executar cada linha), Cobertura de Decisão (cada branch verdadeiro/falso), Cobertura de Condição (cada subcondição verdadeiro/falso) e Cobertura de Caminho (executar todas as combinações possíveis de caminhos).
Manutenção, Qualidade e Métricas
A manutenção é a fase mais longa do ciclo de vida, classificada em: Corretiva (reparar defeitos ocultos), Adaptativa (ajuste a novos ambientes/hardware), Perfeitiva (melhoria de desempenho ou novas funções) e Preventiva (melhorias para facliitar manutenções futuras).
As métricas de qualidade quantitativas incluem:
- Confiabilidade: Probabilidade de operação sem falhas. Medida por
MTTF / (1 + MTTF), onde MTTF é o Tempo Médio Entre Falhas. - Disponibilidade: Medida por
MTBF / (1 + MTBF), onde MTBF é o Tempo Médio Entre Falhas (incluindo reparo). - Manutenibilidade: Medida por
1 / (1 + MTTR), onde MTTR é o Tempo Médio Para Reparo.
Modelos como o ISO/IEC 9126 (focando em Funcionalidade, Confiabilidade, Usabilidade, Eficiência, Manutenibilidade e Portabilidade) e o McCall (focado em Operação, Revisão e Transição) fornecem frameworks para avaliar a qualidade do produto.
Gestão de Projetos, Riscos e Configuração
A estimativa de custos utiliza modelos como o COCOMO (Construtive Cost Model), que evoluiu do modelo básico (estático) para o COCOMO II, incorporando pontos de função e objetos.
O cronograma é visualizado através de Diagramas de Gantt (foco em duração e paralelismo) e Redes PERT (foco em dependências e caminho crítico). Abaixo, uma implementação lógica para cálculo de tempos e identificação do caminho crítico em uma rede PERT:
def calcular_pert(atividades):
# atividades é um dicionário: {no: [(no_destino, duracao), ...]}
nos = set(atividades.keys())
for destino in [dest for dest, _ in sum(atividades.values(), [])]:
nos.add(destino)
ve = {no: 0 for no in nos} # Tempo Mais Cedo
vl = {no: float('inf') for no in nos} # Tempo Mais Tarde
# Cálculo Forward Pass (Tempo Mais Cedo)
for no_atual in sorted(nos):
if no_atual in atividades:
for prox_no, duracao in atividades[no_atual]:
if ve[no_atual] + duracao > ve[prox_no]:
ve[prox_no] = ve[no_atual] + duracao
tempo_total = ve[max(nos, key=lambda x: ve[x])]
vl[max(nos, key=lambda x: ve[x])] = tempo_total
# Cálculo Backward Pass (Tempo Mais Tarde)
for no_atual in sorted(nos, reverse=True):
if no_atual in atividades:
for prox_no, duracao in atividades[no_atual]:
if vl[prox_no] - duracao < vl[no_atual]:
vl[no_atual] = vl[prox_no] - duracao
# Identificação do Caminho Crítico (Folga = 0)
caminho_critico = []
for no in nos:
folga = vl[no] - ve[no]
if folga == 0:
caminho_critico.append(no)
return caminho_critico, tempo_total
A Gestão de Configuração controla mudanças através de repositórios (Biblioteca de Desenvolvimento, Biblioteca Controlada e Biblioteca de Produto). A Gestão de Riscos envolve a identificação, projeção (Probabilidade x Impacto) e mitigação de riscos de projeto, técnicos e de negócios, utilizando planos RMMM (Risk Mitigation, Monitoring, and Management).
Tolerância a Falhas e Revisão de Software
A garantia de qualidade passa por revisões formais (focadas no design e na lógica do código) e pela implementação de técnicas de tolerância a falhas. A tolerância a falhas baseia-se primordialmente no conceito de redundância, que pode ser classificada em:
- Redundância de Estrutura: Inclusão de hardware ou módulos de software adicionais (estática, dinâmica ou híbrida) para assumir o controle em caso de falha.
- Redundância de Informação: Adição de bits de verificação (como paridade ou CRC) para detectar e corrigir erros em dados.
- Redundância de Tempo: Reexecução de instruções ou transações para mitigar erros transitórios.
- Tecnologias Adicionais: Software e hardware auxiliares necessários para detectar, isolar e recuperar o sistema de estados errôneos.