Image: AI generated
Ele volta exatamente onde foi corrigido
Eu construi uma ferramenta para fechar o drift.
A tese do yongol e simples. Decisoes que nao vivem em uma unica fonte autoritativa (SSOT) acabam a deriva. Por isso coloco as decisoes no SSOT e trato o codigo como uma projection descartavel, redesenhada a cada geracao. O drift de logica de negocio — quando uma coluna definida como BIGINT silenciosamente volta a ser INT apos um unico refactoring — ficou assim fechado.
Porem, ha pouco tempo, ao analisar um lote de defeitos no codigo gerado pelo yongol, notei algo estranho. Os defeitos confessavam-se na mesma estrutura frasal. “A coleta de import esta desacoplada de ‘o handler realmente usa time?’” “A inferencia de requiredness esta desacoplada de ‘a API alvo realmente exige esse parametro como required?’” Parametros de caminho sao sempre required; import so quando o token e realmente usado. As mesmas decisoes estruturais estavam incrustadas no codigo do gerador como proxies locais convenientes, sem registro em qualquer SSOT.
O drift nao havia desaparecido. Tinha subido um nivel. A logica de negocio foi fechada pelo SSOT, mas as decisoes estruturais do proprio gerador — aquele que le o SSOT e produz codigo — nao tinham SSOT algum. A tese do yongol retornou ao proprio yongol. Exatamente no lugar que a teoria previa.
Entao a pergunta muda. Todo mundo sabe por que o drift surge. A verdadeira pergunta e esta: por que ele volta mesmo depois de corrigido?
A raiz: decisao e detalhe sao coisas diferentes
Reconstruamos a partir da fisica.
Uma decisao e informacao. E informacao de baixa entropia. “Esta coluna deve ter 64 bits” e uma escolha intencional entre inumeros estados possiveis. A natureza nao gosta de baixa entropia. Deixada em paz, a informacao se dispersa no ruido ao redor e desaparece. A segunda lei da termodinamica se aplica tambem as decisoes.
A engenharia de software observa esse colapso ha muito tempo. As leis de evolucao de software de Lehman afirmam que a complexidade de sistemas E-type aumenta a menos que haja esforco explicito para reduzi-la (1980). A fisica da informacao vai mais fundo. Landauer mostrou em 1961 que apagar um unico bit exige um custo termodinamico minimo (kT ln 2). Alterar e reter informacao nao e, por principio, gratuito. Manter uma decisao em seu lugar custa energia continuamente.
Para a informacao sobreviver, duas coisas sao necessarias: um armazenamento autoritativo (authoritative store) e uma reprojecao ativa constante a partir dele (error correction). E assim com o DNA do nosso corpo; e assim com os bits de paridade do armazenamento digital. Guarda-se o original separadamente e, a cada vez, restaura-se a partir dele.
O drift acontece quando essa restauracao se rompe. O mecanismo e um so. Chamo-o de proxy binding. Quando o meio nao consegue preservar a distincao entre decisao e detalhe, a proxima pessoa (ou o proximo agente) nao consegue ler a decisao na fonte autoritativa e a re-deduz de um sinal correlato conveniente ao lado. Algo como “esta coluna e timestamptz, entao devo importar time.” Na maioria das vezes esta certo. Por isso e perigoso. As vezes esta errado, e quando erra a decisao desaparece em silencio.
Codigo raw e exatamente esse tipo de meio. O codigo nao distingue entre “isto e uma decisao” e “isto e acidentalmente verdadeiro aqui.” Por isso modelos maiores nao resolvem o problema. O proprio meio nao consegue carregar a decisao — nao importa o quao inteligente seja o leitor, nao ha nada para ler.
Esse fenomeno nao era sem nome. Na arquitetura de software, Perry e Wolf distinguiram violacao de principios como erosion e a insensibilidade a arquitetura como drift (1992); Cunningham chamou os juros de codigo malfeito de divida tecnica (1992). Os sintomas foram bem nomeados em cada area. O que acrescento e o mecanismo unico subjacente (proxy binding) e a estrutura em que esse mecanismo recursa para cima a cada camada fechada. Nao e uma questao de nomenclatura, mas de causalidade.
Por que sobe
Ate aqui, a historia e conhecida. O novo vem a seguir.
Para fechar o drift sao necessarias duas coisas: um repositorio que armazene decisoes com autoridade (SSOT) e um agente de fechamento que o leia e produza artefatos (gerador). Porem, o proprio agente de fechamento tambem toma decisoes. Decisoes estruturais como “parametros de caminho sao required.” O meio em que essas decisoes vivem — o codigo do gerador — tambem nao consegue distinguir decisao de detalhe.
O mesmo mecanismo se repete um nivel acima. O ato de fechar cria, ele proprio, um meio nao fechado um nivel acima. O drift nao foi erradicado — mudou-se de endereco. Para uma camada sem autoridade.
Levando isso ate o fim, chega-se a uma conclusao desconfortavel. Se dermos um SSOT ao gerador, o que quer que crie esse SSOT armazenara suas proprias decisoes em um meio nao fechado. A cada nivel que se sobe a area de superficie diminui, mas no topo sempre resta uma camada sem autoridade. Seja humana, seja um gerador de geradores. O drift e assintoticamente inerradicavel. (Isso e mais uma conjectura forte do que uma prova. Contudo, toda camada que fechei ate hoje abriu a camada acima no instante em que foi fechada.)
Essa e a resposta para “por que volta mesmo depois de corrigido?” Nao volta. Quando fechamos uma camada, a ferramenta de fechamento abre a proxima. A mesma agua vazando por um dique mais alto.
A assimetria do remedio: o que pode ser declarado e o que so pode ser verificado
Entao, como fechar a camada de cima? Aqui se revela uma assimetria decisiva.
As decisoes de logica de negocio sao geralmente valores. A coluna e 64 bits, o acesso e apenas do proprietario, a paginacao e por cursor. Valores podem ser declarados. Anotados em DDL, OpenAPI ou um arquivo de especificacao, tornam-se SSOT. Fecham-se por declaracao.
As decisoes estruturais do gerador sao diferentes. “Parametros de caminho sao required”, “import esta vinculado a referencia real de token”, “required (a chave existe) e nao-vazio sao coisas diferentes.” Isso nao e um valor — e uma propriedade comportamental de uma funcao sobre todas as entradas. Propriedades comportamentais nao podem ser enumeradas por declaracao. Porque as entradas sao infinitas. Nao ha como escrever em uma linha de YAML “esta transformacao deve se comportar assim para todos os casos.”
Por isso, as decisoes dessa camada so se fecham por verificacao. Type checkers, testes de propriedade, gates de compilacao. Nao se grava a decisao como dado, mas se instala um portao que a maquina cruza a cada vez para capturar violacoes.
E isso que quis dizer em outro artigo ao escrever “codifique a revisao humana.” Algumas promessas podem ser declaradas e o SSOT as guarda; outras nao podem ser declaradas e o gate as guarda. Nao ha SSOT capaz de registrar se o codigo gerado compila — so se confirma compilando a cada vez. Sem esse portao, a promessa “generate com sucesso = build possivel” flutua fora da arquitetura, e validate passa 0/0 enquanto o artefato esta quebrado.
Drift que pode ser declarado se fecha com SSOT; drift que so pode ser verificado se fecha com gates. Confundir os dois e tentar bloquear com declaracao o que so cede a verificacao — e perseguir toupeiras para sempre.
O mesmo rio, diques diferentes
Essa estrutura se repete fora do codigo.
No conhecimento, o drift e a perda da fonte. Quando se perde quem afirmou, quando, e com que evidencia, a afirmacao se espalha como ruido rotulado de “fato.” A proxima pessoa nao consegue ler na autoridade (fonte original) e re-deduz a partir do contexto ao redor. E por isso que projetei GEUL como uma linguagem que forca origem, momento e grau de confianca em toda informacao. A epistemologia de que nao existem fatos, apenas afirmacoes, e um dispositivo de seguranca contra proxy binding na camada do conhecimento.
No direito, o drift e o desvio da jurisprudencia em relacao a decisao original. A civilizacao nao deixou isso a consciencia do juiz em cada caso; codificou regras, definiu violacoes e anexou mecanismos de aplicacao. Um bom juiz nao e o SSOT — e um proxy. A lei escrita e o SSOT.
E o mesmo rio. Se a decisao nao vive em um lugar autoritativo, se o meio nao distingue decisao de detalhe, ela deriva. Seja codigo, conhecimento ou direito.
Conclusao: nao erradicacao, mas elevacao
A luta contra o drift nao pode ter a erradicacao como meta. A erradicacao e impossivel. A ferramenta de fechamento sempre abre a proxima camada.
A meta e outra. Empurrar o drift para camadas mais altas, de superficie menor, e armar essas camadas com verificacao mecanica. Ao reunir em um unico SSOT decisoes antes dispersas por dezenas de milhares de linhas de codigo raw, a superficie passivel de deriva encolhe dramaticamente. A superficie restante — invariantes comportamentais do gerador — se bloqueia com gates. Ainda assim, no topo permanece a ultima camada que nao pode ser delegada: o julgamento humano. Ai, verificamos de novo a cada vez e cravamos a promessa mais uma vez.
Isso e o ratchet. Gira em uma so direcao. Um dente que subiu nao desliza de volta. A entropia tenta puxar as decisoes para baixo; o ratchet as reergue um dente de cada vez. Nao ha equilibrio. Parar e derivar.
O drift nao morre. Por isso nos nao paramos. Construir promessas contra a entropia nao e uma vitoria unica — e um ratchet permanente.
Artigos relacionados
- Ratchet Pattern — como fazer o agente chegar ate o fim
- Por que o loop do seu agente diverge
- Reins Engineering — IA com redeas
Leitura adicional (externa)
- Lehman’s laws of software evolution — Panorama das leis empiricas segundo as quais software nao mantido se torna mais complexo.
- Landauer’s principle — O custo termodinamico de apagar informacao.
Fontes
- Perry, D. E. & Wolf, A. L. (1992). Foundations for the Study of Software Architecture. ACM SIGSOFT Software Engineering Notes, 17(4), 40-52. ACM — A distincao entre erosion e drift.
- De Silva, L. & Balasubramaniam, D. (2012). Controlling software architecture erosion: A survey. Journal of Systems and Software, 85(1), 132-151. ScienceDirect
- Lehman, M. M. (1980). Programs, Life Cycles, and Laws of Software Evolution. Proceedings of the IEEE, 68(9), 1060-1076. IEEE — A lei do crescimento da complexidade e a lei da mudanca continua.
- Landauer, R. (1961). Irreversibility and Heat Generation in the Computing Process. IBM Journal of Research and Development, 5(3), 183-191. IBM — O custo termodinamico minimo da eliminacao de informacao.
- Shannon, C. E. (1948). A Mathematical Theory of Communication. Bell System Technical Journal, 27, 379-423. DOI — Os fundamentos de informacao, entropia e error correction.
- Cunningham, W. (1992). The WyCash Portfolio Management System. OOPSLA ‘92 Experience Report. c2.com — Divida tecnica e “os juros de codigo malfeito.”