| ~kiko | Resumé | Software | Diary | Papers | Outdoors | Travel | Book shelf | Unix help |
Christian Reis
<kiko at async.com.br>
A visão exposta por Fred Brooks em ``No Silver Bullet'' é uma tentativa de se desfazer mitos associados ao poder da Engenharia de Software. Embora a maior parte dos pontos que levanta estejam corretos, vale a pensa tentar levantar os pontos mais positivos da ciência, e expor novos desenvolvimentos na área.
Embora Brooks descreva, corretamente, que o maior problema do desenvolvimento será especificar, projetar e testar o sistema, sua análise dos novos desenvolvimentos na área (como programação em alto nível, projeto iterativo e reuso de componentes) é bastante pessimista, e a impressão que seu artigo fornece é de que as soluções possíveis são as já existentes, e de que o problema da complexidade do desenvolvimento nunca será resolvido. Além disso, o artigo releva os pontos associados ao desenvolvimento e codificação em si como sendo marginais aos problemas do desenvolvimento.
O artigo de Brooks foca em uma margem de 10 anos de tempo. Fazendo uma análise dos últimos 40 anos, no entanto, podemos ter uma idéia melhor da evolução que a engenharia sofreu. Na época, o problema que a computação enfrentava era o de criar programas simples que perfaziam tarefas algoritmicas comuns. Embora não tenha aparecido uma solução mágica, e tenha levado mais de 10 anos para ser melhorado o processo de desenvolvimento, houve uma melhora inegável de uma ordem de magnitude no desenvolvimento de sistemas simples.
O ponto mais importante em gerar esta mudança foi o aparecimento de frameworks conceituais de propósito comum [chamada no texto do artigo original de vanilla ou ``baunilha'']. A base deste paradigma foram as noções da dicotomia de dados e controle, e formas convenientes de estruturar e combinar estes. Daí surgiram os tipos e estruturas de de dados, e uma série de métodos aritméticos para atacar o problema: programação dinâmica e dividir-para-conquistar, por exemplo. Podemos entender, portanto, que para programas simples, a solução para o problema da engenharia de software nos mostra que os problemas essenciais (especificação, modelagem e teste) estavam intimamente relacionados aos acidentais (a codificação em si).
A situação atual, onde o problema é desenvolver sistemas complexos, pode ser similar. Destes sistemas, focaremos aqui nos programas reativos, categoria que inclui sistemas embutidos, concorrentes e de tempo real, por serem considerados particularmente problemáticos. Será exposta uma proposta para uma base para o desenvolvimento deste tipo de sistema.
Para capturar o conceito do sistema, precisamos criar abstrações que
serão a base da solução. A tarefa seguinte é classificar estas
abstrações em conjuntos análogos aos conjuntos dados e controle da
programação algoritmica. Baseando-se em modularização e ocultação da
informação, criamos uma hierarquia de atividades, que
capturam as funções do sistema decompostas até um nível confortável, e
associamos à hierarquia elementos e repositórios de dados às entradas e
saídas das atividades. Associado a esta hierarquia, criamos descrições
de comportamento que serão as atividades de controle, que se comportam
como um sistema nervoso central do sistema, ativando funções e causando
leitura de dados, por exemplo.
Junto ao modelo conceitual, é importante gerar um modelo físico, que descreva a arquitetura do sistema com relação aos módulos, canais e áreas de armazenamento.
A forma melhor encontrada para se modelar o comportamento tem sido
descrever modos ou estados, junto com eventos e condições que geram
transições entre as anteriores. Um série de soluções tem sido propostas
para esta descrição, que vão desde estender máquinas de estados até
statecharts.
Para a modelagem dos dados, podemos usar uma versão adaptada do modelo entidade-relacionamento, e associar as descrições aos repositórios de dados.
Ao invés de se abstrair todo o sistema em descrições, é útil separar os níveis de refinamento em diversas camadas, onde existe um mapeamento entre os modelos refinados e os módulos em si. Esta idéia se adapta bem ao conceito de desenvolvimento iterativo top-down que Brooks defende.
Uma representação visual, embora muitas vezes ignorada, tem impacto direto sobre a forma com que pensamos na hora de construir o modelo. Embora flowcharts não seja mais apropriados, é interessante montar uma série de formalismos visuais com semânticas matemáticas associadas para descrever as relações entre os modelos e hierarquias. O surgimento de técnicas 3D para visualização pode influenciar bastante o uso destas formas de representação.
Criar um framework de propósito geral onde se possa capturar e representar um modelo de sistema, ao invés de se dar soluções completas e inflexíveis, parece ser a melhor forma de se criar um método para o desenvolvimento. Construir modelos bons, e escolher os guias corretos permanecerá como a tarefa do engenheiro de sistemas. Desta forma -- assim como temos diversas técnicas algoritmicas -- teremos um conjunto de metodologias que se adaptam a uma certa classe de problemas.
Embora seja comprovada a utilidade e necessidade de se fazer análise e teste do software, esta avaliação em geral começa tarde demais no processo de desenvolvimento, e se foca apenas em aspectos ``sintáticos'' do modelo. Para fazer uma análise real de erros semânticos e lógicos deste modelo, precisamos realmente testar e analisar o modelo.
Uma noção interessante é a de criar uma especificação executável do modelo, apoiado em semânticas formais para este modelo. A execução do modelo geraria eventos externos e acessaria as estruturas de dados internas do sistema, simulando a operação do sistema de uma forma passo-a-passo.
Uma ferramenta poderia permitir ao usuário especificar e fazer esta
execução passo-a-passo o sistema. Além disso, seria útil poder
especificar um lote de comandos para processamento automático. O
importante é que seja possível realizar esta execução em qualquer ponto
e tempo do desenvolvimento, e que se construa o sistema em torno de uma
base que está continuamente sendo corroborada.
Partindo deste conceito, poderíamos criar uma meta-linguagem para a execução do programa, associando breakpoints, especificando entradas - possivelmente randômicas - e gerando cenários diferentes. Usando a ferramenta para testar estes cenários seria muito mais informativo que simples análise de dependências de processos.
Embora testar exaustivamente o modelo completo seja em geral infactível, em geral é possível testar uma parte delimitada do sistema, ou uma seção com nível de complexidade reduzído. Desta forma, um teste poderia levar uma quantidade de tempo razoável, e ajudar a descobrir se condições pré-definidas poderiam ser alcançadas em algum momento.
Para estabelecer condições mais específicas do sistema, é possível utilizar a execução exaustiva associada a uma especificação de comportamento - um watchdog. Podem ser modelados em watchdogs também expressões de lógica temporal, ajudando a especificar limitações globais no comportamento do sistema. Um passo além da execução exaustiva poderia consistir em aplicar verificação automatizada e maquinas de estado muito grandes ao conceito de lógica temporal.
A compilação do modelo em código, se apoiada em um modelo mais completo
como o descrito neste artigo, poderia ser feita muito mais completa e
profundamente que atualmente é oferecida em ferramentas de geração. Este
modelo mais completo teria já aspectos comportamentais, e o código
poderia levar em conta estes relacionamentos. Partes não-especificadas
mas disponíveis em bibliotecas poderiam ser ligadas ao código na hora da
geração.
Este código, além de poder ser executado para teste, pode ser associado a uma simulação da interface para avaliação do cliente. Alterações ao modelo poderiam ser recompiladas em código e re-executadas. Código poderia também ser gerado para seções limitadas de comportamento, como os watchdogs discutidos acima.
Técnicas de análise de modelo podem ajudar a estabelecer se o refinamento das camadas foram corretos ou não. Baseados na executabilidade do modelo, bastaria comparar modelos de níveis diferentes para verificar se tem o comportamento esperado ou não. Mudanças nos sub-sistemas inferiores poderiam refletir em mudanças nos níveis superiores, de forma que o sistema ficaria cada vez mais completo. Aliado à otimização do código, esta técnica tem chance de oferecer finalmente a viabilidade à geração do código.
Existem realmente pontos onde este framework é menos aceito; a
especificação de comportamento do modelo é um exemplo. No entanto, a
idéia de um framework geral é boa, e certamente surgirá alguma alternativa
vencedora. É bastante provável que inclua comportamento reativo,
semânticas rigorosas, e visualização gráfica.
O surgimento de ferramentas com poder de geração e execução de código
poderosas é bastante provável, e o poder de executar e testar o modelo
será de grande valor. Em algum tempo, podem surgir técnicas associadas à
executabilidade, como análise de timing e performance.
Estes prospectos para melhora, associados à cooperação entre os pesquisadores, sugerem que, ao invés de uma época de complexidade sem solução, é bastante possível que estejamos entrando em um período de desenvolvimentos e melhoras significativas.