Módulo 06 — Requisitos Funcionais e Não-Funcionais

Onde estamos. No módulo anterior abrimos a engenharia de requisitos e discutimos como descobrir o que um sistema precisa fazer. Agora vamos afiar a distinção que organiza todo esse trabalho: a diferença entre o que o sistema faz e a qualidade com que ele o faz. Quero que você chegue à aula entendendo por que os requisitos não-funcionais costumam ser mais difíceis de escrever, mais fáceis de ignorar e, com frequência, mais determinantes para o sucesso ou o fracasso de um projeto do que qualquer funcionalidade isolada.

Deixe-me começar com uma provocação honesta. Quando você imagina “o que um sistema faz”, provavelmente pensa em uma lista de coisas: cadastrar clientes, emitir uma nota, calcular um frete. Essa lista é importante, mas ela é a parte fácil. O que costuma derrubar um projeto não é a ausência de uma funcionalidade, e sim o sistema fazer tudo o que foi pedido e, mesmo assim, ser lento demais para uso real, inseguro a ponto de vazar dados, ou tão confuso que ninguém consegue operá-lo. Essas propriedades — desempenho, segurança, usabilidade, confiabilidade — quase nunca aparecem espontaneamente na conversa com o cliente, e é exatamente por isso que precisamos de vocabulário e método para caçá-las. Vamos construir esse vocabulário agora.

O que é um requisito funcional

Um requisito funcional descreve um serviço que o sistema deve prestar: uma coisa que ele deve fazer, como deve reagir a determinadas entradas e como deve se comportar em situações específicas. É a resposta direta à pergunta “o que este sistema precisa realizar?”. Sommerville trata o requisito funcional como a declaração de uma função do sistema, e a palavra-chave aqui é função: há uma entrada, um processamento e um resultado observável.

Pense em um sistema de biblioteca universitária. São requisitos funcionais afirmações como: o sistema deve permitir que um usuário pesquise o acervo por título, autor ou assunto; o sistema deve registrar o empréstimo de um exemplar a um usuário cadastrado; o sistema deve impedir novos empréstimos a um usuário com multa pendente. Repare que cada uma dessas frases descreve um comportamento verificável — eu consigo imaginar um teste que confirma se o sistema faz ou não faz aquilo. Essa é a marca do requisito funcional bem escrito: ele é, em princípio, testável de forma binária, ou o sistema atende ou não atende.

NotaComo reconhecer um requisito funcional

Se a frase completa naturalmente a sentença “o sistema deve fazer…”, com um verbo de ação que produz um resultado observável — cadastrar, calcular, exibir, validar, notificar, impedir —, você está diante de um requisito funcional. Ele fala de serviços, não de qualidades.

Há uma sutileza que quero antecipar. Requisitos funcionais também incluem o que o sistema não deve fazer, e essa parte é frequentemente esquecida. “O sistema deve impedir empréstimo a usuário com multa” é tão funcional quanto “o sistema deve registrar empréstimos”. Quando a especificação só descreve os caminhos felizes e silencia sobre as proibições e exceções, ela abre espaço para que cada desenvolvedor invente sua própria regra, e o sistema resultante fica cheio de comportamentos que ninguém combinou.

O que é um requisito não-funcional

Agora vamos ao ponto mais delicado. Um requisito não-funcional não descreve o que o sistema faz, mas como ele deve fazê-lo, ou sob quais restrições. Ele fala de propriedades do sistema como um todo: quão rápido, quão seguro, quão confiável, quão fácil de usar, quão disponível ele precisa ser. Sommerville chama a atenção para um detalhe que muda tudo: os requisitos não-funcionais raramente se referem a uma função isolada; eles são propriedades emergentes, que se aplicam ao sistema inteiro e que dificilmente podem ser garantidas por um único módulo.

Voltando à biblioteca: “uma busca no acervo deve retornar resultados em tempo aceitável mesmo com muitos usuários simultâneos” é não-funcional. Ninguém está pedindo uma função nova; está impondo uma exigência de desempenho sobre uma função que já existe. “Os dados pessoais dos usuários devem estar protegidos contra acesso não autorizado” é não-funcional, e diz respeito a segurança. “O sistema deve estar disponível durante todo o horário de funcionamento da biblioteca” é confiabilidade e disponibilidade. “Um bibliotecário novo deve conseguir registrar um empréstimo sem treinamento prévio extenso” é usabilidade.

Por que esses requisitos são mais difíceis? Por três razões que se somam. Primeiro, eles são atravessados: uma exigência de desempenho não vive em uma tela, ela influencia a modelagem de dados, a escolha de algoritmos, a arquitetura inteira. Segundo, eles competem entre si — reforçar segurança quase sempre cobra um preço em usabilidade ou desempenho, e não existe uma configuração que maximize tudo ao mesmo tempo. Terceiro, e mais grave, eles tendem a ser expressos de forma vaga: o cliente diz “quero um sistema rápido”, e “rápido” não é uma especificação, é um sentimento.

ImportantePor que os não-funcionais decidem o destino do projeto

Uma funcionalidade que falta pode ser adicionada depois com relativa facilidade. Já um requisito não-funcional violado — um sistema que é lento por natureza, ou inseguro por concepção — frequentemente exige refazer a arquitetura, porque a qualidade em questão foi excluída desde as decisões estruturais. É por isso que negligenciar os não-funcionais no início costuma sair caríssimo no fim.

A classificação dos requisitos não-funcionais

Para não tratar os não-funcionais como uma nuvem indistinta, Sommerville propõe uma taxonomia que separa a origem de cada exigência. Ela divide os requisitos não-funcionais em três grandes famílias, e conhecer essa divisão ajuda você a procurar requisitos que de outra forma passariam despercebidos.

Os requisitos de produto especificam ou restringem o comportamento do próprio software. São as propriedades que o sistema, como produto acabado, deve exibir: desempenho, confiabilidade, disponibilidade, segurança, usabilidade, portabilidade. Quando dizemos que a busca deve responder rápido ou que a interface deve ser operável sem treinamento, estamos no reino dos requisitos de produto.

Os requisitos organizacionais derivam de políticas e procedimentos da organização que desenvolve ou que vai usar o sistema. Aqui entram exigências de que o desenvolvimento siga um determinado padrão de codificação, de que o sistema seja entregue em certa plataforma tecnológica por decisão corporativa, ou de que certos processos operacionais da empresa sejam respeitados. Não nascem da natureza do problema, e sim do contexto de quem constrói ou opera.

Os requisitos externos vêm de fatores fora do sistema e da organização: legislação, regulamentação, interoperabilidade com sistemas de terceiros, exigências éticas. Uma lei de proteção de dados que obriga determinado tratamento de informações pessoais é um requisito externo, e ignorá-lo não é um defeito técnico, é uma ilegalidade.

O diagrama abaixo organiza essa taxonomia. Use-o como um checklist mental: ao levantar requisitos, percorra os três ramos e pergunte, para cada um, “o que o meu problema exige aqui?”.

flowchart TB
    NF[Requisitos<br/>Não-Funcionais]
    NF --> P[De Produto]
    NF --> O[Organizacionais]
    NF --> E[Externos]
    P --> P1[Desempenho]
    P --> P2[Confiabilidade e<br/>Disponibilidade]
    P --> P3[Segurança]
    P --> P4[Usabilidade]
    O --> O1[Padrões de<br/>desenvolvimento]
    O --> O2[Ambiente e<br/>plataforma]
    O --> O3[Processos<br/>operacionais]
    E --> E1[Legais e<br/>regulatórios]
    E --> E2[Interoperabilidade]
    E --> E3[Éticos]
    style NF fill:#cfe2ff,stroke:#084298,stroke-width:2px
    style P fill:#d1e7dd,stroke:#0f5132
    style O fill:#fff3cd,stroke:#664d03
    style E fill:#f8d7da,stroke:#842029

Note que uma mesma preocupação pode aparecer sob origens diferentes. A segurança de dados pode ser um requisito de produto (o sistema deve criptografar senhas) e, ao mesmo tempo, um requisito externo (a lei obriga determinado tratamento). Isso não é redundância; é sinal de que a exigência é robusta e vem sustentada por mais de uma fonte.

Tornando o inverificável verificável

Chegamos ao coração metodológico deste módulo. Dissemos que o cliente pede um sistema “rápido”, “seguro”, “fácil”. Esses adjetivos são intenções legítimas, mas inúteis como especificação, porque não permitem decidir se o sistema pronto atende ou não. “Rápido” para quem? Comparado a quê? A engenharia de requisitos resolve isso com uma exigência inflexível: todo requisito não-funcional relevante deve ser traduzido em uma meta mensurável, expressa por uma métrica objetiva com um valor-alvo.

A ideia é substituir a propriedade vaga por uma medida que possa ser aferida com um teste. Em vez de “o sistema deve ser rápido”, escrevemos “a resposta de uma busca no acervo deve ocorrer em, no máximo, dois segundos para noventa e cinco por cento das requisições, com até quinhentos usuários simultâneos”. Agora existe um número, existe uma condição, existe um alvo — e existe um teste que confirma ou refuta. Sommerville organiza justamente essa passagem sugerindo métricas típicas para cada propriedade. A tabela a seguir sintetiza esse mapeamento entre propriedade e medida.

Propriedade Medida objetiva possível
Desempenho Tempo de resposta; transações processadas por segundo; tempo até a primeira resposta útil
Tamanho Espaço em disco ocupado; consumo de memória em execução
Confiabilidade Tempo médio entre falhas; probabilidade de indisponibilidade; taxa de ocorrência de falhas
Disponibilidade Percentual de tempo em operação em uma janela definida
Usabilidade Tempo de treinamento até proficiência; número de erros cometidos por usuário típico em uma tarefa
Segurança Número de vulnerabilidades por porte de código; tempo para detectar um acesso indevido
Portabilidade Número de sistemas-alvo suportados; esforço para migrar de plataforma

Quero que você repare no que a tabela faz. Ela transforma sentimentos em contratos. Um requisito com meta mensurável pode ser aceito ou rejeitado na entrega sem discussão subjetiva, e essa objetividade protege tanto o cliente quanto a equipe. Sem ela, “o sistema não é rápido o suficiente” vira uma queixa infinita e sem critério de encerramento.

DicaA pergunta que torna qualquer requisito verificável

Sempre que escrever ou receber um requisito não-funcional, faça a pergunta: “como eu mediria isto?”. Se você não consegue imaginar um número e um teste que o afiram, o requisito ainda não está pronto — está apenas expressando um desejo. Refine até que a medida apareça.

Há uma honestidade a preservar aqui. Nem todo requisito não-funcional se deixa quantificar com facilidade, e forçar um número artificial é tão ruim quanto não ter número nenhum. Quando a quantificação é difícil, o dever da engenharia é ao menos deixar o critério de aceitação o mais explícito e observável possível, para que a avaliação não fique inteiramente ao sabor da opinião.

Os conflitos entre requisitos não-funcionais

Já toquei nisso, mas o tema merece uma seção própria porque é onde mora boa parte da arte de projetar. Requisitos não-funcionais competem por recursos finitos, e otimizar um quase sempre penaliza outro. Um sistema fortemente criptografado e cheio de verificações de segurança gasta tempo de processamento — logo, segurança pressiona desempenho. Uma interface que reduz cada operação ao mínimo de cliques, aumentando a usabilidade, pode remover confirmações que protegiam contra erros graves. Reduzir o consumo de memória pode obrigar a recalcular coisas em vez de armazená-las, custando desempenho.

O diagrama a seguir ilustra algumas dessas tensões clássicas. As arestas não significam que uma propriedade destrói a outra, mas que existe um trade-off a ser negociado conscientemente.

flowchart LR
    S[Segurança] <-->|verificações custam tempo| D[Desempenho]
    D <-->|cache consome espaço| T[Tamanho/Memória]
    U[Usabilidade] <-->|atalhos removem confirmações| S
    D <-->|otimização engessa código| M[Manutenibilidade]
    style S fill:#f8d7da,stroke:#842029
    style D fill:#cfe2ff,stroke:#084298
    style T fill:#fff3cd,stroke:#664d03
    style U fill:#d1e7dd,stroke:#0f5132
    style M fill:#e2d9f3,stroke:#59359a

A lição não é evitar os conflitos — eles são inevitáveis —, e sim torná-los explícitos e decididos. Quando a equipe reconhece que precisa escolher entre mais segurança e mais desempenho, ela pode conversar com o cliente, entender qual propriedade é mais valiosa naquele contexto e registrar a decisão. O desastre não é o trade-off; o desastre é o trade-off resolvido por acaso, na cabeça de um desenvolvedor apressado, sem que ninguém tenha combinado.

A estrutura de um documento de especificação de requisitos

Tudo o que discutimos precisa morar em algum lugar organizado, e esse lugar é o documento de especificação de requisitos — o registro que consolida o que foi acordado sobre o sistema. Ele serve a leitores diferentes: o cliente, que confere se o que foi entendido corresponde ao que precisa; os projetistas e desenvolvedores, que o usam como base de construção; e os testadores, que dele derivam os critérios de aceitação. Um bom documento respeita todos esses leitores.

Embora existam variações, a literatura consolidou uma estrutura de seções que quero que você conheça. Um cabeçalho de introdução descreve o propósito do sistema e o público do documento. Uma seção de descrição geral situa o sistema em seu contexto, apresentando suas funções principais em alto nível e as características esperadas de seus usuários. O núcleo traz os requisitos funcionais, organizados de forma que possam ser localizados e referenciados, e os requisitos não-funcionais, com suas metas mensuráveis. Há ainda espaço para restrições de projeto e implementação impostas de fora, para um glossário que fixa o vocabulário e elimina ambiguidade de termos, e para apêndices com modelos, dados e informações de apoio. A tabela abaixo resume o papel de cada seção.

Seção Papel no documento
Introdução Declara o propósito do sistema e para quem o documento se destina
Descrição geral Situa o sistema, suas funções principais e o perfil dos usuários
Requisitos funcionais Detalha os serviços que o sistema deve prestar, de forma referenciável
Requisitos não-funcionais Registra as restrições e metas de qualidade, com medidas objetivas
Restrições Fixa limitações externas de projeto, plataforma e ambiente
Glossário Define os termos técnicos e do domínio, eliminando ambiguidade
Apêndices Reúne modelos, dados de referência e material de apoio

Um documento de requisitos não é literatura decorativa nem um formulário a preencher por obrigação. Ele é o contrato de entendimento entre quem pede e quem constrói. Quando bem-feito, ele previne a falha mais cara da engenharia de software — a de descobrir tarde que todos concordavam com palavras diferentes sobre a mesma frase.

As características de um bom requisito

De nada adianta uma estrutura impecável se cada requisito, individualmente, for mal escrito. Por isso a literatura estabelece um conjunto de qualidades que uma boa especificação, requisito a requisito, deve exibir. Quero que você memorize essas cinco características, porque elas formam o critério com que você deve inspecionar qualquer requisito que escrever ou receber.

Um bom requisito é não ambíguo: admite uma única interpretação, sem margem para que dois leitores entendam coisas diferentes. É verificável: existe um processo — um teste, uma inspeção, uma medição — capaz de confirmar objetivamente se o sistema pronto o satisfaz. É completo: descreve todo o comportamento pretendido, incluindo respostas a entradas inválidas e situações de exceção, sem deixar buracos que cada desenvolvedor preencherá do seu jeito. É rastreável: pode ser referenciado de forma única, ligando-se à sua origem — quem o pediu e por quê — e aos elementos de projeto e teste que dele derivam. E é consistente: não contradiz nenhum outro requisito do documento, porque uma especificação que se contradiz é logicamente impossível de satisfazer.

NotaO teste de qualidade de um requisito

Leia o requisito e pergunte, em sequência: existe mais de uma forma de entendê-lo? Consigo imaginar um teste que o verifique? Ele cobre os casos de erro, além do caso feliz? Ele tem um identificador e uma origem? Ele briga com algum outro requisito? Se qualquer resposta for desfavorável, o requisito ainda não está pronto.

Repare como essas cinco qualidades conversam com tudo o que já vimos. A verificabilidade é exatamente o que as métricas objetivas garantem para os não-funcionais. A não ambiguidade é o que o glossário protege. A consistência é o que a gestão dos conflitos entre requisitos preserva. Não são regras avulsas; são faces de uma mesma disciplina.

Um requisito não-funcional influenciando a implementação

Quero fechar mostrando, em código, que um requisito não-funcional não é conversa abstrata: ele desce até a decisão de implementação e muda o que você escreve. Suponha o requisito de desempenho “a consulta ao saldo de pontos de um cliente deve responder em no máximo cinquenta milissegundos”. A versão ingênua abaixo cumpre a função, mas recalcula o total percorrendo todo o histórico a cada chamada.

class ContaPontos {
  final List<int> lancamentos;

  ContaPontos(this.lancamentos);

  // Recalcula somando tudo a cada consulta — custo cresce com o histórico.
  int saldoAtual() {
    var total = 0;
    for (final valor in lancamentos) {
      total += valor;
    }
    return total;
  }
}

Funcionalmente essa versão está correta: ela devolve o saldo certo. Mas o requisito não-funcional de desempenho a condena, porque, à medida que o histórico cresce para milhares de lançamentos e a consulta é chamada com frequência, o tempo de resposta sobe e a meta de cinquenta milissegundos deixa de ser cumprida. A exigência de qualidade força outra decisão de projeto: manter um total acumulado, atualizado a cada lançamento, de modo que a consulta seja imediata.

class ContaPontos {
  final List<int> _lancamentos = [];
  int _saldo = 0; // Saldo mantido incrementalmente para consulta em tempo constante.

  void registrar(int valor) {
    _lancamentos.add(valor);
    _saldo += valor;
  }

  // Responde em tempo constante, independentemente do tamanho do histórico.
  int saldoAtual() => _saldo;
}

A segunda versão é um pouco mais complexa e exige disciplina para manter o saldo coerente com os lançamentos. Ela não faz nada de novo do ponto de vista funcional — o serviço prestado é idêntico. A diferença inteira nasce de um requisito não-funcional, que empurrou a implementação de “somar sob demanda” para “manter acumulado”. É esse o mecanismo que quero que você enxergue: qualidades atravessam o código e determinam estruturas, e por isso não podem ser deixadas para depois.

Síntese

Quero que você retenha três ideias desta conversa. A primeira é a distinção de fundo: requisitos funcionais dizem o que o sistema faz; requisitos não-funcionais dizem com que qualidade e sob quais restrições ele o faz, e estes últimos, por serem emergentes, conflitantes e vagos por natureza, costumam ser mais difíceis e mais determinantes. A segunda é o dever da verificabilidade: um requisito não-funcional só vale como engenharia quando é traduzido em uma métrica objetiva com valor-alvo, porque só assim ele deixa de ser desejo e vira contrato aferível. A terceira é o padrão de qualidade de qualquer requisito — não ambíguo, verificável, completo, rastreável e consistente — que você deve aplicar como crivo a tudo o que escrever. Chegue à aula com essas três ideias firmes, porque sobre elas construiremos o projeto e os testes dos próximos módulos.

Para consolidar antes da aula: pegue um requisito vago que você já ouviu, do tipo “o sistema tem que ser rápido e seguro”, e reescreva-o como dois requisitos não-funcionais verificáveis, cada um com uma métrica e um valor-alvo. Se você conseguir transformar o desejo em medida, entendeu o essencial deste módulo.