Pontos Principais
-
Mudança é a única constante na vida. Em nenhum lugar isso é mais aparente do que na engenharia de software, onde o trabalho diário de um desenvolvedor é modificar, adaptar, ajustar, ou até refazer os sistemas pelos quais ele é responsável.
-
À medida que os aplicativos crescem e as equipes escalam, torna-se mais desafiador manter um entendimento claro do próprio software. A complexidade prejudica a capacidade dos desenvolvedores de entender e alterar efetivamente o software, conforme necessário.
-
Compreensibilidade é o conceito de que um sistema deve ser apresentado para que um engenheiro de software possa compreendê-lo facilmente. Quanto mais compreensível for um sistema, mais fácil será para os engenheiros alterá-lo de maneira previsível e segura.
-
Um sistema é compreensível se atender aos seguintes critérios: completo, conciso, claro, e organizado.
-
A compreensibilidade e a observabilidade são complementares, mas a segunda se concentra mais em duas coisas: a capacidade de alertar quando o sistema se comporta de forma inesperada, e ajudar a identificar as causas para que o serviço normal possa ser restaurado; e a capacidade de ajudar a identificar gargalos de desempenho para que recursos adicionais possam ser alocados ou que as equipes relevantes sejam informadas.
Há 2.500 anos, Heráclito disse que "a mudança é a única constante na vida". Em nenhum lugar isso é mais aparente do que na engenharia de software, onde o trabalho diário de um desenvolvedor é modificar, adaptar, ajustar, ou até refazer os sistemas pelos quais ele é responsável. Outro aspecto que torna a engenharia de software relativamente única entre as disciplinas é a vasta liberdade que temos para moldar nossos trabalhos, dentro dos limites criados pelo homem definidos pela mecânica da ciência da computação.
Nossa capacidade de exercitar efetivamente esse grande poder frequentemente fica aquém de uma limitação muito surpreendente - nossa capacidade de conhecer nossas próprias criações. À medida que os aplicativos crescem e as equipes escalam, fica ainda mais difícil manter uma compreensão clara do próprio software, levando ao fracasso dos projetos.
Na prática
A maioria das tarefas comerciais de engenharia de software por aí não começa com um histórico perfeito: existe um aplicativo, escrito usando uma ou mais linguagens, dependente de um conjunto de estruturas e bibliotecas, e rodando em cima de alguns sistemas operacionais.
Assumimos a responsabilidade de alterar esse aplicativo para que ele atenda a alguns requisitos, como desenvolver um novo recurso, corrigir um bug existente etc. Simultaneamente, temos que continuar atendendo a todos os requisitos existentes, e não documentados, e mantendo o comportamento existente o máximo possível.
E, como todo engenheiro júnior de software descobre em seu primeiro dia de trabalho, escrever um código para resolver um problema simples de ciência da computação (ou copiar a resposta do StackOverflow) não chega nem perto do nível de complexidade de resolver o mesmo problema dentro um sistema grande e intrincado.
O que é compreensibilidade?
Tomando emprestado do setor financeiro (Understandability definition): "Compreensibilidade é o conceito que um sistema deve ser apresentado para que um engenheiro possa compreendê-lo facilmente". Quanto mais compreensível for um sistema, mais fácil será para os engenheiros alterá-lo de maneira previsível e segura.
Aproximando-se mais dessa analogia, podemos dizer que um sistema é compreensível se atender aos seguintes critérios:
- Completo. O sistema deve ser apresentado usando um conjunto predefinido de fontes (código-fonte, documentação etc.) para cobrir todas as informações importantes. Nenhuma informação crítica pode ser deixada para a imaginação do engenheiro.
- Conciso. O código fonte do sistema não deve sobrecarregar o usuário com uma quantidade excessiva de detalhes. É aqui que a separação de preocupações (Separation of Concerns) e a abstração entram em cena, permitindo que o engenheiro se concentre na tarefa em questão.
- Claro. Use uma metodologia de apresentação que torne fácil a leitura do código. É aqui que a consistência (Consistency in Software), as convenções de codificação, a formatação do código-fonte, os comentários do código, e o destaque da sintaxe fazem uma enorme diferença.
- Organizado. O engenheiro deve ser capaz de localizar facilmente informações com referências cruzadas no sistema. É aqui que a modularidade (Software Modularity), a documentação do software, os controles de navegação do código-fonte, e as ferramentas de gerenciamento do código-fonte permitem que os engenheiros entendam o sistema.
Compreensibilidade em tempo de execução
Com o surgimento do Software como Serviço (SaaS) e outros novos paradigmas de entrega de software (The New Pattern of Software Delivery), muitas empresas estão praticando a propriedade total do software (total ownership of software), capacitando os engenheiros a assumirem a responsabilidade pelo aplicativo durante todo o seu ciclo de vida.
Nessas empresas, a compreensibilidade assume uma forma ainda mais poderosa, determinando quão bem os engenheiros podem entender como o software opera e como está sendo utilizado pelos clientes.
Assim, informações altamente valiosas, como padrões de uso, entradas e saídas do mundo real e estatísticas reais de desempenho e disponibilidade, podem se tornar acessíveis às equipes determinadas a tê-las.
Observabilidade não é compreensibilidade
Lendo isso, você deve estar pensando: é exatamente para isso que servem as ferramentas de observação (Introduction to observability) e monitoramento. Infelizmente, não é esse o caso. Essas ferramentas existem para dar suporte a problemas de TI mais tradicionais, com foco em:
- Alertar quando o sistema se comportar de forma inesperada, e ajudar a identificar a causa raiz para que o serviço normal possa ser restaurado.
- Identificar gargalos de desempenho para que recursos adicionais possam ser alocados ou que as equipes relevantes sejam informadas.
- Manter registros de eventos detalhados para fins de operações, segurança, e suporte.
Esses são ótimos casos de uso com os quais a TI vem lidando desde tempos imemoriais e, como o ROI é bem claro, um grande número de fornecedores oferece ótimas ferramentas para resolver esses problemas. No entanto, esses não são os casos de uso que as equipes de engenharia de software lidam, e essas ferramentas nunca foram criadas para ajudar a essas equipes.
O que todos esses casos de uso de TI têm em comum é que alguém, com um conhecimento básico do sistema, precisa saber exatamente como o sistema se comportou em uma instância específica para poder responder a ela adequadamente. Isso significa que coletamos dados sobre um conjunto predefinido de eventos, o que tende a ser sobre como o sistema está interagindo com o mundo ao seu redor.
As equipes de engenharia de software, por outro lado, têm um conhecimento profundo do funcionamento interno do sistema e buscam entender mais sobre como ele funciona. Os dados coletados mudam diariamente (se não a cada hora), com base nas alterações específicas que estão sendo feitas no sistema
Vencendo a complexidade
À medida que o software escala e é alterado, ele se torna cada vez mais complexo. Uma grande parte dessa complexidade é inerente, devido ao simples fato de o número de requisitos de negócios estar sempre aumentando. O restante dessa complexidade é indesejável, causado pela maneira como o aplicativo foi reajustado ao longo do tempo, bem como por más escolhas de design, geralmente chamado de dívida tecnológica (Technical debt).
Independentemente de sua origem, a complexidade prejudica a capacidade dos engenheiros de entender e alterar efetivamente o software, conforme necessário. Esse problema geralmente é agravado pela perda de conhecimento causada pela rotatividade de pessoal.
Obviamente, na indústria de software, é do conhecimento geral que a complexidade do software deve ser minimizada. Quanto mais complexo o software, mais caro será o desenvolvimento de novos recursos e a qualidade geral do sistema será menor. Muito já foi escrito sobre como criar software que reduz ao mínimo a complexidade e permite que os sistemas e equipes sejam escalados melhor.
Compreensibilidade em novos projetos de software
Se você estiver desenvolvendo um novo software, provavelmente negligenciará a compreensibilidade em favor de um de seus proxies: a complexidade. Ao focar em evitar e reduzir a complexidade, você naturalmente manterá a compreensibilidade. Para sua sorte, a complexidade do software está no foco de inúmeras ferramentas e técnicas de desenvolvimento de software.
Em primeiro lugar, comece com uma força de trabalho de alta qualidade. Os engenheiros talentosos se valem de sua experiência para expressar problemas complexos de negócios de maneira simples e elegante, tanto no código-fonte quanto na arquitetura do software, criando um software mais fácil de entender.
Em seguida, tente tornar o sistema o menor possível, pois sistemas menores por natureza são menos complexos e mais fáceis de entender. Os sistemas podem ser reduzidos a partir do "topo", concentrando-se nos requisitos de negócios que realmente importam, deixando de fora, pelo menos temporariamente, requisitos que não são obrigatórios. Os sistemas também podem ser reduzidos de baixo para cima, usando abstrações de alto nível, como novas linguagens de programação, frameworks avançados, e bancos de dados modernos.
Por último, mas não menos importante, certifique-se de ter um plano para lidar com a complexidade quando ela surgir. Escreva testes automatizados na forma de testes de unidade e de sistema para garantir que sua equipe de engenharia possa refatorar com segurança essa complexidade. Coloque ferramentas de observação de alta qualidade para ajudá-lo a obter uma compreensão de alto nível do sistema. Automatize seus pipelines de integração e implantação para permitir que melhore e itere rapidamente.
Compreensibilidade no software existente
Por outro lado, no que diz respeito ao software existente, tendemos a aceitar a falha de nossa equipe de engenharia em entender o código como uma catástrofe forjada por Deus. Culpar a complexidade de cara não é mais uma abordagem viável para melhorar a compreensibilidade.
Na maioria das vezes, você se depara com um sistema legado que foi escrito usando instrumentos de nível inferior ao que está disponível atualmente, por pessoas que há muito tempo deixaram o projeto, e nenhum plano para lidar com isso. Reclamar sobre a dívida técnica com a qual você precisa lidar e o "código ilegível" que seus engenheiros não conseguem entender não o levará muito longe. Nem sonha com refatorações e migrações de longo prazo que raramente acontecerão.
É aí que as plataformas de depuração da produção brilham, permitindo que os engenheiros tenham uma visão detalhada do código em atividade, pode ajudá-los a começar a entendê-lo. E, ao seguir as peças pelos ambientes e casos de uso, eles podem desvendar essa complexidade passo a passo.
Para finalizar
Agora, temos uma melhor perspectiva sobre a importância da compreensibilidade no desenvolvimento de software. Por um lado, sempre soubemos que manter o código fácil de ler e de manter era importante e que grande parte da engenharia de software estava focada nesse feito. E, no entanto, agora apreciamos mais a diferença que pode fazer ao permitir que o software cresça e evolua com o tempo.
Por outro lado, também desafiamos a suposição de que a compreensibilidade sempre pode ser aprimorada ao lidar com a complexidade e ao projetar e escrever software melhor e mais simples. Frequentemente, nos encontramos embarcando no trem no meio do caminho, com pouco ou nenhum controle sobre como ele chegou lá. Portanto, devemos começar a rastrear e gerenciar a compreensibilidade como sua própria métrica principal, maximizando a velocidade e a qualidade da engenharia, mesmo em condições abaixo do ideal.
Sobre o autor
Liran Haimovitch é o co-fundador e CTO da Rookout. Ele é um defensor das metodologias modernas de software como agile, lean e devops. A paixão de Liran é entender como o software realmente funciona. Quando ele não está pensando em código, geralmente está mergulhando ou caminhando.