Descubra como a engenharia de confiabilidade assegura sistemas de software robustos e disponíveis em qualquer situação.
Tempo de Leitura: 8 minutos
Introdução
Os sistemas de software estão intimamente incluídos em nossas atividades pessoais e profissionais. Esse fenômeno acontece há tanto tempo que gera uma dependência dos sistemas em todos os aspectos da vida humana. De tal maneira, esperamos que o software funcione sem paradas ou falhas de segurança e que preserve nossos dados e informações pessoais. Isso significa que ele deve estar disponível independentemente do horário ou dia, e operar adequadamente. Ou seja, o software deve ser confiável.
A confiabilidade é uma das propriedades da dependabilidade - ela, em questão, foi abordada especificamente em um outro artigo do blog que você pode conferir aqui. Podemos entender a confiabilidade como a capacidade do sistema para prestar serviços conforme especificados. Para alcançá-la, é necessário compreender as métricas que especificam o quão confiável é um sistema, bem como as boas práticas de programação e engenharia de software. Ao longo das últimas décadas, o uso de engenharia de software, linguagens de programação melhores e gestão da qualidade causou melhorias na confiabilidade de software. Contudo, ainda existem falhas de sistema que impactam sua disponibilidade e/ou funcionamento. Portanto, técnicas especiais de engenharia de confiabilidade devem ser utilizadas para alcançar altos níveis de confiabilidade e disponibilidade.
Neste artigo, faremos a distinção entre confiabilidade e disponibilidade de software. Ademais, apresentaremos métricas para especificação da confiabilidade e como são usadas para especificar requisitos de confiabilidade mensurável. Por fim, explicaremos algumas práticas de programação para obter engenharia de software confiável e compreender como a confiabilidade de um sistema de software pode ser medida usando um teste estatístico.
Conteúdo
MostrarOcultar- Introdução
- Disponibilidade e Confiabilidade
- Métricas de Confiabilidade
- Programação para Confiabilidade
- Limitar a visibilidade das informações em um programa
- Fornecer tratamento para todas as exceções
- Minimizar o uso de construtos propensos a erro
- Fornecer capacidade de reinicialização
- Conferir limites do vetor
- Timeouts (tempo de espera) quando invocar componentes externos
- Dar nome a todas as constantes que representam valores do mundo real
- Conclusão
Disponibilidade e Confiabilidade
Considerando que o sistema é algo que fornece algum tipo de serviço (enviar mensagens, registrar pagamentos, reproduzir músicas, por exemplo), podemos considerar que a disponibilidade se refere a esse serviço estar ou não ativo e funcional, enquanto a confiabilidade se refere ao sistema apresentar ou não os resultados corretos. As definições mais específicas, segundo Ian Sommerville, são:
- Confiabilidade: A probabilidade de operação isenta de falhas ao longo de um período especificado, em um determinado ambiente, para um propósito específico.
- Disponibilidade: A probabilidade de que um sistema, em algum momento, estará operacional e será capaz de prestar serviços requisitados
A confiabilidade do sistema não é um número absoluto porque depende de onde e de como o sistema é utilizado. Ao testar um sistema em um ambiente de escritório e em um ambiente universitário, obteremos resultados bem diferentes. Isso ocorre porque haverá distintos níveis de interesse e comportamento dos usuários, o que pode resultar em mais ou menos falhas de sistema. Portanto as percepções de confiabilidade do sistema em cada um desses ambientes são variáveis.
A definição de confiabilidade se baseia em uma ideia de operar sem falhas. E a falha, por sua vez, é um comportamento que não está em conformidade com a especificação do sistema. Sommerville ressalta dois problemas nesta definição:
- As especificações de software frequentemente estão completas ou incorretas e é responsabilidade dos engenheiros de software interpretar como o sistema deveria se comportar. Em via de regra, eles não são especialistas do domínio e podem não implementar o comportamento que o usuário espera. Assim o software pode se comportar conforme as especificações técnicas, mas disforme das expectativas dos usuários.
- Além dos desenvolvedores, quase ninguém lê as documentações de especificação de software. Consequentemente, os usuários podem prever que o software se comporte de uma maneira quando a especificação estabelece algo divergente.
Considerando esses pontos, a falha não é algo que pode ser definido objetivamente, mas sim um julgamento feito pelos usuários do sistema. Por esse motivo, usuários não têm a mesma impressão de confiabilidade e os defeitos que a afetam podem nunca aparecer nitidamente para eles.
Já a disponibilidade de um sistema não depende apenas do seu número de falhas, como também do tempo necessário para consertar as causas dessas falhas. Vamos imaginar um sistema A que falha apenas uma vez por ano e um sistema B que falha uma vez por mês. Inicialmente pode-se imaginar que A é mais confiável que B. Porém, se B leva 5 minutos para reiniciar após uma falha e A leva 6 horas, a disponibilidade do sistema B é muito maior do que a do sistema A.
A quantidade de tempo em que o sistema está indisponível não é o único fator que gera perturbação: o momento em que o sistema falha também é muito importante! Se o sistema estiver indisponível por uma hora todo dia entre 01h00 e 02h00 da manhã pode não afetar os usuários. Entretanto, se a disponibilidade falhar por 10 minutos durante o horário comercial o efeito será muito maior.
Apesar da confiabilidade e da disponibilidade estarem intimamente relacionadas, às vezes uma pode ter mais prioridade do que a outra. Se é indispensável para os usuários usufruir do serviço contínuo do software, então o requisito de alta disponibilidade tem precedência sobre a confiabilidade. Isso pode acarretar em falhas mais frequentes, mas se o sistema puder se recuperar rapidamente delas, sem perder dados do usuário, então essas falhas podem não afetar tanto os usuários.
Um bom exemplo de um cenário em que a disponibilidade é mais importante do que a confiabilidade são as centrais telefônicas que encaminham chamadas. Os usuários esperam realizar chamadas quando tirarem o telefone do gancho ou ativar em um aplicativo. Portanto o sistema tem requisitos de alta disponibilidade. Ocorrendo um defeito no sistema enquanto a conexão estiver sendo estabelecida, sua correção costuma ser simples e rápida. Switches na estação básica reiniciam o sistema e tentam novamente a conexão. Assim, os usuários podem nem notar que houve uma falha. Além disso, mesmo se a chamada for interrompida, as consequências geralmente não são graves. Os usuários podem simplesmente reconectar se isso acontecer.
Métricas de Confiabilidade
Pode-se considerar a confiabilidade com uma probabilidade de ocorrência de falha no sistema quando eles tiverem uso em um ambiente operacional específico. Imagine que um sistema falhe 1 a cada 1000 transações. Nesse caso teríamos uma probabilidade de falha de 0,001. O que não determina que sempre haverá uma falha a cada mil transações, mas sim que se você observar N mil transações o número de falhas deve ser de aproximadamente N x 0,001.
Existem três métricas principais empregadas para especificar a confiabilidade e a disponibilidade:
- Probabilidade de falha sob demanda (POFOD, sigla em inglês para probability of failure on demand): O uso dessa métrica estabelece a probabilidade de que uma demanda por serviço de um sistema resulte em uma falha. Assim, uma POFOD igual a 0,001 significa que há chance de 1/1000 de que uma falha ocorra quando houver demanda.
- Taxa de ocorrência de falha (ROCOF, do inglês, rate of occurrence of failures): Essa métrica estabelece o provável número de falhas de um sistema que tendem a ser observadas em relação a um certo período - por exemplo uma hora, um minuto um dia - ou a quantidade de exceções do sistema. O inverso da ROCOF é o tempo médio até a falha (MTTF, mean time of failure). Uma ROCOF de duas falhas por hora equivale a um MTTF de 30 minutos.
- Disponibilidade (AVAIL, do inglês availability): É a probabilidade de um sistema estar operacional quando houver uma demanda por um serviço. Desta forma, uma disponibilidade de 0,9999 significa que em média o sistema estará disponível em 99,99% do tempo de operação.
O POFOD é utilizado em situações onde a falha sob demanda pode levar a uma falha grave do sistema, independentemente da frequência das demandas. Um sistema de proteção que monitora um reator químico e interrompe a reação se ele estiver superaquecido deve ter a sua confiabilidade especificada usando POFOD, por exemplo. Comumente, as demandas sobre um sistema de proteção desse tipo são pouco frequentes porque o sistema é a última linha de defesa após todas as outras estratégias de recuperação terem falhado. Desta forma, uma POFOD de 0,01 poderia parecer arriscado. Apesar disso, se houver apenas duas demandas do sistema em toda a sua vida útil, então provavelmente esse sistema jamais falhará.
Por sua vez, a ROCOF, deve ser utilizada quando as demandas do sistema acontecem periodicamente, em vez de irregular ou imprevisível. Imagine um sistema que opere grandes quantidades de transações. Uma ROCOF de 10 falhas por dia significaria aceitar que, em média, 10 transações por dia não serão executadas com sucesso e terão de ser canceladas ou reenviadas. Caso o ambiente de uso priorize o tempo absoluto entre falhas, pode-se especificar a confiabilidade em MTTF.
Programação para Confiabilidade
É difícil abordar “programação” sem entrar em detalhes de uma linguagem de programação específica. Contudo, quando observamos a engenharia de confiabilidade, existe um conjunto de práticas de programação indicadas que são razoavelmente universais e que ajudam a reduzir os erros nos sistemas produzidos. Abaixo, você encontrará uma lista com oito práticas de programação que aumentam a confiabilidade.
Limitar a visibilidade das informações em um programa
Este é um princípio de segurança da informação adotado até por organizações militares. Apenas os indivíduos que precisam conhecer uma determinada informação para executar suas atividades recebem essa informação. Se a informação não é diretamente relevante para o seu trabalho, então o sujeito não precisa saber dela.
Desta forma, o programador deve adotar um princípio parecido para controlar o acesso de variáveis e as estruturas de dados utilizados. Os componentes do programa só devem ter acesso autorizado aos dados dos quais necessitam para sua implementação. Os outros dados do programa devem ficar inacessíveis e escondidos dele. Ao esconder a informação, ela não poderá ser corrompida pelos componentes do programa que não deveriam utilizá-la. Se a interface continuar igual, a representação dos dados poderá ser modificada sem afetar outros componentes do sistema.
Conferir a validade de todas as entradas
Em muitos casos, a especificação do sistema não define quais ações devem ser tomadas se a entrada estiver incorreta. O risco disso é que os ataques maliciosos podem contar com a entrada deliberada de informações inválidas. Mesmo quando as entradas provêm de sensores ou de outros sistemas, esses sistemas podem estar errados e fornecer valores incorretos. Há várias formas de checagens indicadas a depender das próprias entradas. Por exemplo: checagem de intervalo, de tamanho, de representação e de razoabilidade.
Fornecer tratamento para todas as exceções
O tratamento de exceções dentro de um programa permite que ele detecte e se recupere de alguns erros de entrada e eventos externos imprevistos. Isso aumenta o grau de tolerância a defeitos. Como a maioria dos erros de entrada e eventos externos inesperados normalmente são passageiros, frequentemente é possível continuar a operação normal após o processamento da exceção.
Minimizar o uso de construtos propensos a erro
O erro humano é a maior causa de falhas de programas e algumas abordagens de programação são mais propensas a introduzir erros. Um exemplo de construto a ser evitado é o uso de números com ponto flutuante porque a precisão deles é limitada pela sua representação no hardware. Isso torna as comparações entre números muito grandes ou muito pequenos menos confiáveis. A alocação dinâmica de memória também tem potencial de erro elevado se a gestão da memória é explícita no programa, pois é fácil esquecer-se de liberar memória quando ela deixa de ser necessária.
Fornecer capacidade de reinicialização
Todos os sistemas devem permitir a reinicialização baseada na manutenção de cópias dos dados coletados ou gerados durante o processamento. O recurso de reinicialização deve permitir que o sistema seja reiniciado usando essas cópias em vez de ter que começar tudo de novo desde o início. Essas cópias podem ser referidas como checkpoints.
Conferir limites do vetor
A ausência de verificação dos limites dos vetores leva à vulnerabilidade de segurança da informação. Algumas linguagens, como Java, já possuem a conferência automática. Mas outras, como C ou C++, não possuem essa função nativa. Nestas, deve-se sempre incluir verificações para garantir que o índice do vetor está dentro dos limites.
Timeouts (tempo de espera) quando invocar componentes externos
Ao consumir um componente externo, é necessário definir um timeout para evitar que, na demora de uma resposta dele, o programa não fique em espera infinita. A ausência de timeout apresenta ao usuário uma falha silenciosa, sem resposta do sistema. Não resta alternativa senão matar o processo e reiniciar o sistema.
Dar nome a todas as constantes que representam valores do mundo real
Este princípio reduz as chances de uso de valores incorretos devido à identificação adequada. Além disso, quando o valor não muda, não é necessário examinar todo o código para descobrir onde ele foi usado, uma vez que o nome dele já permite seu reconhecimento.
Conclusão
Neste artigo, exploramos detalhadamente a engenharia de confiabilidade de software, abordando sua importância, como ela pode ser medida e as práticas recomendadas para alcançá-la. Entendemos que a engenharia de confiabilidade não é apenas um conceito técnico, mas uma necessidade crítica para garantir a continuidade e segurança dos serviços que dependem de sistemas de software. Ao adotar as práticas de programação recomendadas e utilizar as métricas adequadas, você estará mais preparado para desenvolver sistemas robustos e confiáveis.
Gostaríamos de convidar você a participar do Fórum da Casa do Desenvolvedor. Lá, você pode tirar dúvidas sobre engenharia de confiabilidade, compartilhar suas experiências e aprender com outros profissionais da área. Junte-se à nossa comunidade e contribua para a troca de conhecimento sobre este e outros temas importantes no desenvolvimento de software.