Pontos Principais
- A correção no software é limitada a componentes bem compreendidos
- No desenvolvimento contínuo de software, o trabalho principal é atualizar o código
- Criar softwares mutáveis sobre componentes existentes e bem compreendidos
- Melhorar as automações de entrega faz com que a equipe tenha um melhoramento no seu trabalho principal
- Quando o software interage com os seres humanos, precisamos procurar o "melhor" ao invés do mais "correto"
Quando comecei a programar, adorei o aspecto de solução de quebra-cabeças, no sentido de como posso fazer o computador dar a resposta certa? Mas há um problema mais complicado, como sei que esta resposta está correta? Quando os programas ficam maiores do que uma pessoa pode produzir, esse problema se torna complexo. Utilizamos tipografia estática e testes automatizados, até assistentes de verificação e testes de propriedades quando estamos realmente empenhados. No entanto, tudo isso tem uma suposição importante, sabermos o que é o "correto".
No segundo trabalho de programação, em reuniões com os stakeholders que criaram nossos requisitos, procurei ao máximo por casos semelhantes e interações de recursos. Todo mundo começou a ficar irritado comigo, mas como poderíamos fazer o programa de maneira correta se nem eles sabiam o que era o "certo"? Hoje eu sei que eles tinham motivos para ficarem irritados. Os requisitos conflitantes são uma regra, e seguimos adiante mesmo assim.
Às vezes, a correção é possível e importante. O restante do tempo, otimizamos a mudança, para que possamos encontrar e abordar incrementalmente o que é o "correto".
Quando a correção é possível?
Estamos escrevendo um código para ser inserido no hardware? Ou para uma análise de dados científicos? Talvez para um processamento financeiro? Quem sabe para o rastreamento interno de atividades? Ou ainda para um site de uma campanha publicitária de 30 dias?
O software não está ligado a somente um setor.
Naquela época, as pessoas pensavam que todo software deveria ser escrito com uma especificação completa, porque a correção era fundamental. Meir Lehman, no seu artigo de 1980 "Programas, Ciclos de Vida e Leis da Evolução de Software", insistiu que busquemos especificações completas, mesmo quando forneceu claras evidências contra sua utilidade.
O documento contém uma categorização de programas de importância crucial e uma análise perspicaz, porém equivocada, sobre o ciclo de vida do software. Lehman também mostrou algumas "leis" da evolução do software nas quais as pessoas focam, mas não são relevantes para este artigo.
Distinguir entre situações: Modelo S/P/E
Lehman divide os programas com base no nosso entendimento da solução. Com as distinções feitas pelo autor, podemos resolver muitos argumentos sobre o que é melhor, correção ou alteração. O truque é fazermos a pergunta:
De que tipo de programa estamos falando? Lehman propõe três categorias:
- Programas S, que são especificados e solucionáveis;
- Programas P, onde podemos melhorar a definição, mas não podemos melhorar a resolução; e
- Programas E, que fazem parte do sistema que estamos tentando melhorar.
S: uma solução é especificada
Quando podemos definir completamente o que é o "correto", sendo computável em um tempo razoável, então implementamos com sendo um programa S. "Programas S são programas cuja função é formalmente definida e derivável de uma especificação." Isso funciona em pequena escala, quando precisamos de uma função e sabemos exatamente o que ela deve fazer. Em um programa grande, com que frequência conhecemos todos os requisitos, com todos os conflitos resolvidos?
As especificações são extremamente difíceis de serem escritas. Hila Peleg disse que, quando o grupo de pesquisa está tentando provar que um programa implementa corretamente uma especificação e no caminho encontram uma discrepância, o erro normalmente está na especificação. Agradeço às pessoas dos grupos de padronizações, que estudam os detalhes de especificações como CSS, pois estão fazendo o árduo trabalho de definir o que é "correto".
Lehman observa que as implementações dos programas S, uma vez corretas, nunca precisam mudar, são o formato final. Podemos ajustar alguma coisa para obter desempenho, tão somente. Mas se os requisitos mudarem, então iremos desenvolver um programa S diferente. Podemos usar o código já escrito como ponto de partida, mas o programa original ainda está correto pela própria definição. O Santo Graal, de acordo com Lehman, é dividir todos os sistemas de software em programas S. Lehman tem total certeza que a intenção é extremamente útil, porém está equivocada pois é impossível concluir essa atividade antes de iniciar o desenvolvimento.
Os programas S são satisfatórios de serem escritos pois são os quebra-cabeças que podemos resolver.
P: um problema é especificado
Outros quebra-cabeças não podem ser resolvidos totalmente, mas podemos buscar soluções cada vez melhores. Essas perguntas não são computáveis no tempo de uma geração, como o problema do caixeiro viajante ou de um jogo de xadrez. Não podemos "acertar", mas podemos definir "melhor". Lehman chama esses sistemas de programas P. "A definição do problema e a solução se aproximam de uma situação do mundo real".
Programas estocásticos pertencem a esta catergoria, como Monte Carlo e outras simulações estatísticas que tentam responder à pergunta, "Qual a probabilidade?". Esses programas são impossíveis de serem testados completamente, pois não fazem a mesma coisa todas as vezes. Isso torna as provas e o raciocínio matemático valiosíssimos.
Lehman reconhece que os programas P mudam com o passar do tempo, à medida que aprendemos mais sobre o problema. O caixeiro viajante, por exemplo, pode começar a se preocupar com o gasto de gasolina por quilômetro rodado ou onde estará na tarde de sexta-feira. O uso do algoritmo nos ensina mais sobre ele, e como podemos melhorá-lo. Por esse motivo, Lehman os agrupa na próxima categoria, sob as "aplicações".
E: o software está entrelaçado no espaço do problema
Aqui a gente coloca o resto. Todo software construído com requisitos incompletos e todo sistema cujos requisitos mudam após a entrada em produção. Soa familiar? Queremos que essa imprecisão seja um fracasso das organizações humanas, que devem estar produzindo boas especificações. Mas, como Lehman destaca, isso é mais do que endêmico, é inevitável. O software que é utilizado por humanos não está separado dos sistemas humanos que o utilizam. O software faz parte do sistema pois está entrelaçado nele. Portanto, qualquer programa que altere o sistema leva a uma nova situação, e a novas demandas de alteração. "O programa se tornou parte do mundo que modela".
Lehman usa a palavra "incorporado" para esse tipo de programa, mas a expressão não significa incorporado no hardware. Ok, é confuso, por isso preferimos a palavra "entrelaçado". O autor está se referindo a programas que participam de um sistema sociotécnico, onde o software precisa aprender e crescer junto com os seres humanos que o utilizam. O programa está entrelaçado com o problema que está ajudando a resolver. Nas palavras de Lehman: "A necessidade de mudança contínua é intrínseca à natureza do uso do computador".
Esse tipo de software nunca está "concluído".
Se desejamos quebra-cabeças completos, não ficaremos satisfeitos com este trabalho. Mas se queremos ajudar pessoas com problemas reais, os programas E são o melhor caminho.
Há uma razão para a maioria dos softwares personalizados serem desse tipo, que chamamos de "aplicações".
Compor aplicações de programas S sempre que puder
Lehman percebeu que os programas E e P podem ser decompostos, de modo que muitas das partes geram programas S, que são bem definidos e solucionáveis. Lehman estava certo, pois queremos compor as aplicações a partir dos programas S, tanto quanto possível. Mas o autor entendeu errado a ideia. Programas E alterados e imprecisos vêm em primeiro lugar, e programas S, aparecem posteriormente. Lehman também pensou que cada programa S seria implementado pela equipe de desenvolvimento. Agora, temos uma estratégia diferente, temos que encontrar componentes existentes e adaptá-los o design da aplicação para conseguirmos utilizá-los. O desenvolvimento de software personalizado está mais ligado a integração do que a construção. Lehman previu isso quando disse: "Todos os grandes programas (sistemas de software) serão construídos como estruturas de programas S". Porém ele não viu isso como um aspecto essencial de um problema S, que pode ser resolvido por outra pessoa.
Programas S são reutilizáveis - otimize para corrigr
Os programas S são valiosos, bem especificados, corretos e são componentes reutilizáveis, além disso, são satisfatórios para implementarmos, o que levou a algo que o Lehman não esperava: uma grande quantidade de bibliotecas e estruturas em código aberto, além de software como serviço.
Durante a codificação, sempre que refino um problema até chegar em uma peça bem definida e abstrata, as coisas começam a ficar divertidas e algo começa a surgir na minha cabeça: alguém já resolveu este problema. Está na hora de procurar a implementação de outra pessoa e se adaptar a ela.
Quando estamos criando um programa S reutilizável, a correção é importante. Agora que já sabemos o que é a correção, precisamos otimizá-la. Este é um trabalho para provas, tipos e testes de propriedades, o que se encaixar no nosso programa.
Os problemas passam de E para S depois que o entendemos
Em 1980, mesmo com a percepção crucial de que "a qualidade em geral não pode ser projetada e incorporada a um programa ab initio, mas sim alcançada de maneira gradualmente através de mudanças e aprimoramentos evolutivos", o autor ainda afirmava que, se projetarmos o suficiente, poderemos dividir cada aplicação em programas S. Ele não estava totalmente errado.
Lehman sempre pensava em dividir o programa antes de começarmos a desenvolver, mas não é assim que as coisas funcionam. Os componentes das aplicações podem ser definidos como programas S depois de serem reescritos diversas vezes, em diversas circunstâncias diferentes.
No final dos anos 90, criamos estruturas ORM dentro das aplicações, até que o problema se tornasse amplo e compreendido o suficiente para que as estruturas como o Hibernate pudessem resolver os problemas enfrentados por muitas empresas. Depois disso, os comitês poderiam escrever padrões como o JPA. As pessoas costumavam escrever aplicativos CRUD na mão, até que os padrões se tornassem claros o suficiente para que uma estrutura como o Rails os ajustasse de maneira ampla. Os bons padrões emergem de muitas implementações e, em seguida, alimentam as implementações corretas.
Os programas E são, normalmente, softwares personalizados - otimize para mudar
O objetivo dessa categorização é deixar de lado a correção quando estivermos em conflito com a mudança.
Se estamos criando um software para uso humano, provavelmente não estamos criando um programa S. O software "personalizado" é adequado ao objetivo, já que podem fazer parte das operações de um sistema comercial específico. Essas aplicações entrelaçadas pelo sistema não são reutilizáveis. Cada uma é projetada para uma situação, para ajudar seres humanos em particular com uma atividade ou levá-los a um comportamento em particular. Uma aplicação precisa estar correta o suficiente e precisa ser constantemente melhorada. Por isso, otimize para mudar.
O que significa, otimizar para mudanças?
Não escreva o código, mude-o
No artigo, Lehman dissertou o ciclo de vida do software, dos Requisitos ao Design, Design Detalhado, Programação, Teste, Implementação e Manutenção. Ele observou que os problemas ocorriam quando mudávamos de uma fase para outra, notando também um ponto crucial: Todas as outras fases são encapsuladas dentro da Manutenção. No entanto, Lehman não encontrou a solução que agora parece estar clara, ficando preso em "precisamos separar ao máximo as fases", ao invés de "esquecê-las, exceto pela manutenção". Comecemos em algum lugar e então façamos a iteração. Faça alterações pequenas e simples, para que mal possamos distinguir uma fase da outra, assim, os problemas de transferência desaparecem.
Deixemos de lado a ideia de que desenvolvemos aplicações. Podemos escrever um programa S, e dizer que está pronto. Quando a aplicação estiver entrelaçado no sistema, todo o desenvolvimento é alterado.
Ao configurar o primeiro esqueleto do programa, precisamos incluir a integração e entrega contínuas. Precisamos de compilações, testes automatizados, uma maneira de executar verificações de segurança e implantação automatizada. Estas são apostas no jogo de "mudança do software".
Reduzir o medo de alterar o código
O maior obstáculo ao movimento não é o trabalho, mas o medo. Muitos padrões modernos ajudam a reduzir isso. A refatoração, com testes automatizados, ajuda a criar um modelo claro do código, para que possamos alterá-lo com confiança. Os microservices ajudam a limitar o impacto da alteração de uma parte específica. Lehman chegou a prever os microservices: "Cada módulo pode ser implementado como um programa rodando em seu próprio microprocessador e o sistema implementado como um sistema distribuído." Ele sabia que o mundo não estava preparado para eles no momento em que escreveu o livro: "Muitos problemas relacionados ao design e a construção de tais sistemas ainda precisa ser resolvido". Alguns já foram resolvidos. Temos automação para a manutenção de componentes de software em grandes quantidade, como também as APIs para infraestrutura, construções e entrega.
Alterar como mudamos o código
A pergunta principal na arquitetura de software é "como vamos mudar isso?". Projetamos o estado futuro do sistema e um caminho para o atingirmos.
Para continuar crescendo, precisamos continuar melhorando em como fazemos as mudanças. Ao configurar o processo de entrega e as automações, precisamos considerar a flexibilidade. A entrega é sempre um programa E. O software que queremos entregar não é o mesmo, a infraestrutura em que o implantamos varia e as verificações de qualidade necessárias da estrutura de entrega mudam com as necessidades das organizações. Para mantermos nosso código flexível, necessitamos continuar aprendendo como desenvolvedores, continuar ensinando as automações como fazerem um trabalho melhor.
Quando não souber o que é correto, procure pela melhor opção
Lehman disse: "A correção absoluta do programa como um todo não é o problema real, mas sim a usabilidade do programa e a relevância da produção em um mundo em constante mudança que deve ser a nossa principal preocupação". Isso não é fácil de testar, mas podemos medir ou detectar os efeitos no sistema em que nosso programa está entrelaçado. A Microsoft diz que um recurso não é "enviado" até emitir dados de telemetria que revelam se as pessoas o utilizam. Isso informa ao desenvolvedor se as alterações tiveram impacto na empresa.
Essa é a beleza do trabalho do desenvolvimento. "Fundamentalmente, a natureza peculiar dos sistemas de software sempre deixará a engenharia de software em uma classe diferenciada". Na programação, resolver quebra-cabeças é apenas o começo. Efetuar mudanças no mundo é o jogo mais atraente.
Não quero alterar o código. Eu quero mudar a realidade.
Sobre a autora
Jessica Kerr (desenvolvedora da Atomist) começou sua carreira de software porque era algo fácil e depois ficou porque era difícil. Vinte anos depois, ainda desenvolve produtos para ajudar os desenvolvedores a automatizar mais o próprio trabalho. Trabalha remotamente na Atomist de St. Louis, onde cria dois filhos e faz podcasts para o Greater Than Code e Arrested DevOps. Gosta de falar sobre symmathesy, automação, e uma infinidade de linguagens e ferramentas diferentes. Podemos encontrá-la nas conferências de software em todo o mundo.