Quando uma abstração de software merece o seu lugar
Um quadro prático para decidir se uma abstração de software elimina repetições relevantes ou apenas desloca a complexidade para trás de uma nova fronteira.
Supermoon Software / 1 de setembro de 2026 / 5 min de leitura

Uma abstração de software pode facilitar o trabalho com um sistema, mas apenas se ocultar os detalhes certos. Ocultar código não é suficiente. A pergunta útil é se uma estrutura partilhada elimina decisões que não devem ser tomadas repetidamente, preservando ao mesmo tempo as diferenças que continuam a ser importantes.
Isto transforma a abstração numa escolha de conceção, em vez de um ritual de limpeza. Uma equipa precisa de identificar a promessa que está a ser criada, os casos que se enquadram nessa promessa e o trabalho necessário quando um caso não se enquadra. Os indícios mais sólidos resultam de mudanças concretas e condições operacionais, não da elegância que a estrutura aparenta ter quando é considerada isoladamente.
Perguntar o que a abstração proporciona
Uma abstração é uma interface simplificada colocada sobre os detalhes subjacentes. Uma interface, neste sentido, é o conjunto de entradas, saídas e expectativas através do qual uma parte de um sistema interage com outra. Uma abstração útil proporciona a quem a utiliza um problema mais pequeno e estável para compreender.
O primeiro passo é dar um nome à decisão que desapareceria. Se vários módulos tiverem de formatar pedidos equivalentes, aplicar a mesma transição ou traduzir o mesmo estado interno, uma fronteira partilhada pode eliminar duplicação relevante. Se a camada proposta apenas der um novo nome a uma operação existente, o seu valor é menos claro.
- Exprimir a decisão repetida em linguagem comum, em vez de fazer referência apenas ao código repetido.
- Identificar os detalhes subjacentes que quem utiliza a abstração já não deve precisar de compreender.
- Descrever a promessa estável que a abstração faria a todas as partes que a utilizam.
- Manter as diferenças visíveis quando a sua combinação exigir sinalizadores, exceções ou ramificações ocultas.
Começar com uma sequência de trabalho real
Um candidato útil torna-se frequentemente mais claro quando a equipa acompanha uma tarefa real desde a entrada até ao resultado. O percurso deve incluir validação, mudanças de estado, efeitos secundários e tratamento de falhas. Um efeito secundário é uma mudança exterior ao cálculo imediato, como armazenar dados ou enviar um pedido.
O objetivo não é forçar todos os percursos a entrar num único fluxo de trabalho. É encontrar pontos de controlo onde vários percursos exigem a mesma decisão. Esses pontos de controlo podem tornar-se fronteiras entre módulos, enquanto os passos verdadeiramente diferentes permanecem explícitos. Esta abordagem mantém a abstração ligada ao trabalho que o sistema já precisa de realizar.
- Escolher uma tarefa representativa que seja suficientemente bem compreendida para poder ser examinada sem suposições.
- Assinalar cada ponto em que o código tem de interpretar, validar, transformar, armazenar ou comunicar algo.
- Comparar outra tarefa relevante e registar quais decisões são equivalentes, em vez de apenas próximas.
- Delinear uma fronteira em torno das decisões partilhadas e deixar as decisões diferentes fora dessa fronteira.
- Percorrer um caso difícil antes de assumir um compromisso com a forma proposta.

Contar o custo de ambos os lados
A abstração substitui um tipo de complexidade por outro. A repetição pode diminuir, enquanto a indireção aumenta. Indireção significa que compreender uma operação exige acompanhá-la através de outra camada. Esta troca pode valer a pena quando a nova camada centraliza uma regra relevante, mas é menos vantajosa quando os leitores têm de alternar entre ficheiros para reconstruir uma ação simples.
A avaliação deve incluir a manutenção fora do percurso principal. É necessário considerar a depuração, os testes, a migração, a comunicação de erros e a remoção. Se a abstração for responsável por uma política, também precisa de um local claro onde essa política possa ser inspecionada e alterada. Caso contrário, a complexidade foi ocultada, em vez de reduzida.
- Custo de leitura: quem faz a manutenção consegue compreender o que acontece sem abrir repetidamente a implementação subjacente?
- Custo da mudança: uma mudança comum torna-se local ou as partes que utilizam a abstração ainda têm de coordenar as suas próprias variações?
- Custo da falha: é possível rastrear um erro até à fronteira relevante com contexto suficiente para agir?
- Custo de saída: a camada poderia ser removida ou substituída sem reescrever partes não relacionadas do sistema?
Definir fronteiras e vias de saída
Uma fronteira deve indicar aquilo por que é responsável e o que continua a ser responsabilidade de quem a utiliza. Isto reduz o acoplamento acidental, que é uma dependência entre componentes que não foi concebida deliberadamente. Por exemplo, uma abstração de armazenamento pode ser responsável pela tradução de dados e deixar a política de novas tentativas ao fluxo de trabalho que a utiliza, se essa separação corresponder às necessidades operacionais do produto.
As vias de saída merecem a mesma atenção. Um caso pouco comum pode precisar de acesso direto a uma operação de nível inferior, de uma implementação separada ou de não participar de todo na abstração. A recomendação não é disponibilizar formas ilimitadas de contornar a abstração. É evitar distorcer a interface comum até que esta consiga representar todos os casos hipotéticos.
Testar a promessa perante mudanças e falhas
O melhor teste é um pequeno conjunto de mudanças plausíveis. Importa perguntar o que aconteceria se uma entrada recebesse um novo campo, se uma dependência devolvesse um erro desconhecido ou se um fluxo de trabalho precisasse de um ponto de controlo adicional. Estas são verificações hipotéticas, não previsões. Expõem se a abstração representa um conceito estável ou uma semelhança temporária.

A fiabilidade também depende de uma responsabilidade visível. Um mapa de fiabilidade pode mostrar quais estados envolventes são tratados pelo sistema central, quais são comunicados pelo sistema central e quais ainda exigem atenção noutro local. A abstração não deve dar a entender que resolve condições que ultrapassam a sua responsabilidade. É mais fácil raciocinar sobre uma promessa limitada e exata do que sobre uma promessa ampla sustentada por exceções ocultas.
Escolher a promessa estável mais pequena
Antes de adotar uma abstração, importa escrever a sua promessa numa frase e testar essa frase perante os casos atuais, um caso difícil e uma mudança plausível. Depois, é necessário comparar a estrutura proposta com a opção mais simples de manter o código separado. Se a camada eliminar decisões repetidas e clarificar a responsabilidade, tem uma razão prática para existir.
Se a promessa depender de muitas condições, devem manter-se as implementações separadas e o padrão deve ser reavaliado quando o comportamento relevante for mais bem compreendido. A decisão não é entre abstração e desordem. É entre diferentes formas de posicionar a complexidade. Deve preferir-se a fronteira mais pequena que torne uma regra importante mais fácil de encontrar, alterar, testar e remover.