Adoção e operação de Haskell corporativo na H-E-B
(blog.haskell.org)- A Haskell foi adotada no processo de modernização da cadeia de suprimentos de mais de 435 lojas e centros de distribuição e, 8 anos depois, cerca de 1 milhão de linhas de código mantidas por várias equipes estão em operação em sistemas críticos de produção
- Haskell reduz tipos de erros em tempo de execução e oferece suporte a refatorações baseadas no GHC e à integração com sistemas legados, mas é preciso aceitar a manutenção interna por falta de SDKs e a complexidade dos recursos avançados de tipos
- O sucesso dependeu muito mais da capacidade e da cultura das equipes do que da linguagem; em duas iniciativas, a adoção não se consolidou devido a vazamentos de espaço em uma equipe sem especialistas em Haskell e à saída de desenvolvedores-chave
- O runtime do GHC nunca foi causa de uma falha de produção na H-E-B e, com a evolução do ecossistema em HLS, OpenTelemetry, profiling e outros, Haskell em produção se tornou uma opção competitiva
- Recursos avançados devem ser restringidos por trás de pequenas bibliotecas e interfaces claras, junto com testes, observabilidade e compartilhamento de conhecimento; na era dos agentes de codificação, a validação de tipos do GHC e o feedback rápido se tornam ainda mais importantes
O desafio da modernização do mainframe
- Os clientes da H-E-B, varejista que é a maior empresa privada do Texas, se importam mais com as prateleiras das lojas abastecidas do que com os sistemas internos
- Durante décadas, um mainframe COBOL operou de forma estável, mas nos últimos 10 anos surgiram recursos centrados em tecnologia, como retirada na calçada, entrega em domicílio, estoque em tempo real e busca dentro da loja
- Ao expandir as operações da cadeia de suprimentos para mais de 435 lojas e os centros de distribuição que as apoiam, os sistemas centrais existentes passaram a receber demandas além do escopo para o qual foram originalmente projetados
- A principal dificuldade da modernização do mainframe não é mover código para a nuvem, mas preservar o conhecimento específico da organização acumulado ao longo de décadas na lógica de negócios legada
- Dividir um monólito com centenas de tabelas em microsserviços pertencentes a equipes de engenharia autônomas é uma tarefa técnica e politicamente enorme, além de cara
Adoção de Haskell e escala operacional
- Consultores propuseram Haskell como linguagem comum para um novo projeto que substituiria a maior aplicação de mainframe da empresa
- O trabalho que começou como experimento cresceu e se tornou vários sistemas centrais de produção da cadeia de suprimentos
- Oito anos após a adoção, várias equipes de diferentes domínios mantêm cerca de 1 milhão de linhas de código Haskell
- A operação em produção por longo prazo revelou não apenas resultados reais, mas também dificuldades técnicas e organizacionais
Vantagens obtidas pelo negócio
- O que as empresas querem não é avaliação preguiçosa, funções puras ou dados imutáveis em si, mas um sistema como o COBOL de próxima geração, capaz de operar por 40 anos sem null pointer exception e com baixo custo de manutenção
- Aplicações Haskell eliminam por padrão tipos de erros em tempo de execução comuns em programas imperativos e de tipagem dinâmica
- Bugs não desaparecem completamente, mas os problemas restantes se concentram mais em erros de lógica de negócios do que em null pointer exception ou confusão de tipos
- Em migrações legadas de vários anos pelo padrão strangler fig, o GHC apoia refatorações seguras
- É possível fazer alterações repetidas enquanto os requisitos se formam, permitindo uma migração ágil em vez de waterfall
- A empresa pode medir resultados de forma incremental em vez de investir antecipadamente em um resultado cristalizado de forma equivocada
- Parsers em Haskell permitem usar formatos legados de milhares de linhas, como IBM copybooks de largura fixa, diretamente como contratos
- Uma poderosa interface de função estrangeira (FFI) permite integração também com serviços existentes que oferecem apenas bibliotecas C
- A H-E-B escreveu bibliotecas Haskell que encapsulam
simdjsonC++, OracleODPI-Ce IBM MQ
- A H-E-B escreveu bibliotecas Haskell que encapsulam
Falta de SDKs e propriedade dos contratos
- Quando faltam SDKs para Haskell, é preciso criar os clientes e o código de integração necessários e continuar mantendo-os
- Em troca, a equipe passa a ser dona direta dos contratos, evita dependência de fornecedores e não precisa esperar que o mantenedor upstream distribua correções
- A H-E-B avaliou que, para dependências centrais, essa troca valia o suficiente
- Agentes de codificação lidam bem com o trabalho mecânico de portar bibliotecas Go para Haskell, reduzindo a diferença de velocidade de desenvolvimento entre linguagens
- Porém, uma tradução simples pode não aproveitar plenamente os benefícios do sistema de tipos de Haskell sem prompts sofisticados
Integração multilíngue por meio de DSLs embutidas
- Haskell é forte como linguagem hospedeira para DSLs embutidas (eDSLs)
- Uma equipe central pode definir uma vez o modelo de domínio ou a lógica de negócios e depois gerar código com tipos seguros nas linguagens usadas por outras equipes
- Outras equipes conseguem se integrar ao sistema sem escrever Haskell diretamente
- Hydra é um framework DSL open source que evoluiu a partir do trabalho com dados em grafo na Uber
- Ele é bootstrapado em Haskell e gera implementações semanticamente equivalentes em Java, Python, Scala e outras linguagens
- Uma suíte de testes compartilhada garante a equivalência das implementações por linguagem
- A H-E-B prototipou uma DSL em Haskell para cálculos financeiros sensíveis que poderiam ser compartilhados por equipes de Haskell, TypeScript e Java
- Também criou uma DSL bidirecional de schema JSON
- Ela é semelhante ao autodocodec, mas usa uma codificação tagless final que permite ao usuário estender a semântica central com suas próprias type classes
- Padroniza versionamento de schema, geração de documentação e OpenAPI, além de testes de regressão e golden tests
- É possível escrever intérpretes temporários para converter schemas JSON em definições de tabelas PostgreSQL ou substituir
aesonporsimdjsone aplicar validações adicionais
Recrutamento e cultura de engenharia
- Linguagens modernas também oferecem muitos recursos de segurança de tipos, pureza e concorrência influenciados por Haskell; por isso, a diferença real está na cultura de engenharia que a linguagem incentiva
- Haskell atrai desenvolvedores que valorizam correção e projeto de abstrações
- Contratar para funções em Haskell não foi tão difícil quanto a liderança esperava
- Mesmo sem interesse especial por cadeia de suprimentos, havia candidatos que se inscreviam por causa da própria Haskell
Limites do orçamento de complexidade
- Haskell é uma linguagem antiga e tem várias falhas, por isso é importante acompanhar as versões mais recentes do GHC
- Sem especialistas na equipe, uma única deficiência da linguagem pode se espalhar por todo o projeto
- No início, consultores experientes estabeleceram guardrails para impedir o uso excessivo de extensões da linguagem e operadores personalizados
- Antes das edições de linguagem GHC2021 e GHC2024, cada equipe gerenciava por conta própria o conjunto básico de extensões
- Isso funcionava como um orçamento de complexidade, limitando o escopo da linguagem que novos contratados precisavam aprender antes de contribuir
- As próprias dependências também consomem o orçamento de complexidade, o que dificulta a imposição em nível organizacional
servantexpressa APIs como uma DSL no nível de tipos, e estender combinadores personalizados envolve type families e erros difíceis de entenderhaxlmodela todas as fontes de dados como GADTs indexados pelo tipo de resultado para agrupar operações concorrentes em lotes
- À medida que desenvolvedores ficam mais experientes, aumenta a tentação de introduzir padrões como GADT, type family, generics, tagless final, arrows, free structures e tipos de dados de kind superior
- Quando esses conceitos se acumulam sobre um domínio de negócios já complexo, a promessa de manutenção fácil se enfraquece
- Como recursos avançados aumentam gradualmente à medida que a equipe se familiariza com o ecossistema, o crescimento da complexidade pode aparecer tarde demais
Princípios operacionais de Haskell entediante
- Em nível organizacional, é fácil recomendar Haskell entediante, mas difícil impor
- Encapsular Haskell avançado por trás de interfaces razoáveis reduz o alcance do impacto e limita o orçamento de complexidade a pequenas bibliotecas mantidas por poucos especialistas
- Complexidade no nível da linguagem não é um problema exclusivo de Haskell, mas Haskell oferece uma gama especialmente ampla de ferramentas avançadas
- Mais do que número de linhas de código ou uso de tokens, a quantidade de código que quebra no próximo upgrade do GHC mostra melhor a complexidade de um projeto Haskell
Dois projetos em que a adoção não se consolidou
- Haskell pode ampliar a capacidade de uma equipe e, ao mesmo tempo, virar barreira para outra; na H-E-B, ela não se consolidou em dois projetos por motivos diferentes
- No primeiro caso, desenvolvedores experientes sem vivência em Haskell seguiram templates e convenções de projetos de outra equipe, mas um vazamento de espaço (space leak) travou o programa inteiro
- Sem um desenvolvedor Haskell forte para orientar a equipe, esse único problema levou à interrupção do projeto
- No segundo caso, os engenheiros que defendiam Haskell e criaram a aplicação foram movidos para outros projetos por mudança de prioridades
- Esse motivo não tinha relação com a linguagem em si
- Ambas as equipes tinham alternativas para voltar a tecnologias existentes e não estavam convencidas de que Haskell aumentava sua produtividade
- Quando existe uma saída como um template Spring Boot, pode haver menos motivação para diagnosticar até o fim um vazamento de espaço difícil
- Com LLMs capazes de traduzir mecanicamente bases de código inteiras, as opções de reescrita ficam ainda mais amplas
- A linha de defesa de longo prazo não é um código inteligente demais para ser reescrito, mas uma equipe que queira continuar mantendo Haskell
Falhas de produção e diagnóstico
- Mesmo nas equipes que adotaram Haskell, ocorreram vários problemas sérios
- Um erro double-free surgiu em contagem de referências personalizada
- Em um projeto, a coleta de lixo paralela foi desativada para resolver um vazamento de espaço descontrolado
- Um loop infinito foi um bug especialmente assustador por ser difícil até de descobrir
- O runtime do GHC nunca foi a causa raiz de uma falha de produção na H-E-B
- A vazão e o uso de memória em estado normal foram competitivos o suficiente para as cargas de trabalho da H-E-B, e os casos problemáticos foram exceções, não a situação comum
- Os engenheiros resolveram problemas rastreando core dumps de falhas de segmentação, métricas de coleta de lixo e perfis de heap
- Não se deve sacrificar cobertura de testes por causa do sistema de tipos forte
- Como tipos simples de erros são eliminados na compilação em aplicações Haskell, a engenharia do caos pode expor de forma mais direta falhas reais de lógica e recursos
Ferramentas de desenvolvimento e ecossistema transformados
- Os primeiros sistemas começaram com GHC 8.2 e, na época, era necessário escrever internamente a maior parte dos clientes de API e SDKs para plataformas populares
- O Haskell Language Server ainda não existia, o GHCup estava apenas surgindo, e o profiling de runtime também era difícil
- Desenvolvedores acostumados com IDEs Java achavam o ambiente de ferramentas de Haskell desconfortável, e a H-E-B chegou a patrocinar o desenvolvimento de suporte ao Debug Adapter Protocol para usuários do VS Code
- Hoje, o desenvolvimento Haskell de nível de produção evoluiu para além do viável e chegou a um nível competitivo
- As ferramentas de desenvolvimento incluem HLS,
ghcid,ghciwatch,static-ls, tricorder e plugins de editor- Algumas equipes usam plugins do GHC para um desenvolvimento rico centrado no GHCi, enquanto outras usam VS Code e HLS
- Os desenvolvedores podem escolher as ferramentas que preferem
- Para observabilidade, são usados
hs-opentelemetrye opentelemetry-autoopentelemetry-autoé um plugin do GHC que insere automaticamente spans OTel em funções Haskell- Ele pode oferecer um ambiente de instrumentação semelhante ao de Java e Python
Bibliotecas de produção e melhorias do runtime
- Os serviços usam bibliotecas como
warp,aeson,attoparsec,stm,conduit,servante orville-postgresql - Quando o ecossistema não oferece um recurso necessário, bibliotecas internas são desenvolvidas
- arbiter é uma fila transacional de trabalhos PostgreSQL surgida do trabalho da H-E-B e atualmente em produção
- O GHC vem melhorando diagnósticos de runtime, desempenho do compilador, recursos de monitoramento como eventlog streaming e anotações de exceção
- Depois que o info table profiling foi introduzido no GHC 9.2, a preocupação com vazamentos de espaço diminuiu bastante
Comunidade interna e distribuição de conhecimento
- A H-E-B trata bibliotecas internas como algo importante e incentiva desenvolvedores a participar tanto de projetos internos quanto de open source
- Durante anos, manteve encontros quinzenais de Haskell para que engenheiros compartilhassem descobertas e treinassem outros membros
- Para reduzir silos em uma organização corporativa, ideias e padrões precisam circular entre equipes
- Fazer com que mais engenheiros leiam e sejam donos da base de código reduz o risco de bus factor causado pela saída de uma única pessoa
Critérios para permitir recursos avançados
- Há regras básicas flexíveis, mas a decisão final sobre onde gastar o orçamento de complexidade cabe aos líderes técnicos
- Se dados pessoais identificáveis (PII) armazenados precisam ser criptografados, uma garantia no nível de tipos que transforma vazamento de dados sensíveis em erro de compilação, e não em incidente de runtime, pode valer o investimento
- Unificar quatro contratos de API com o padrão trees-that-grow pode não valer a ponto de ocultar schemas externos com type families
- Haskell entediante não proíbe programação no nível de tipos nem recursos avançados do GHC; ela define convenções com princípios e avalia o custo de desviá-las
- A principal convenção é “sem ação inesperada à distância”
- Evitar geração de código com
TemplateHaskelle lógica de serialização baseada emGeneric - Escrever boilerplate manualmente para mostrar explicitamente o comportamento ao programador
- Isso não proíbe completamente deriving por quasiquoter ou
Generically
- Evitar geração de código com
- Ao implementar máquinas de estado no nível de tipos para formulários web ou usar GADTs onde ADTs comuns bastariam, é necessário ceticismo saudável e contestação em nível de equipe
- Haskell avançado existe tanto em bibliotecas quanto em aplicações de produção, mas o escopo dos experimentos deve ser limitado
- A engenharia real acontece no processo de equilibrar trabalho e brincadeira técnica
O futuro de Haskell e dos agentes de codificação
- Ao longo de vários anos usando GHC, foram encontrados e reportados alguns poucos bugs, mas nenhum problema grave a ponto de interromper o trabalho
- Incidentes de produção são raros e ocorrem com muito mais frequência na lógica de negócios do que em erros de tipo ou de runtime
- À medida que cresce a proporção de código escrito por agentes de codificação, o gargalo se desloca da escrita de código para a validação
- O sistema de tipos do GHC ajuda pessoas a refatorar com segurança e, ao mesmo tempo, fornece um ciclo rápido de feedback que rejeita antes da execução códigos alucinados por agentes
- Para ampliar o potencial de Haskell na indústria, mais casos reais de sucesso em produção precisam ser compartilhados
1 comentários
Opiniões no Lobste.rs
Fora do Texas talvez não seja muito conhecida, mas a H-E-B é uma instituição emblemática do Texas, com porte e influência tão grandes que quase poderia ser outro departamento do governo estadual.
Muitas lojas têm instalações para produção de tortillas, e a qualidade é excelente para um produto feito em larga escala. Além disso, dizem que também usam Haskell
É realmente surpreendente que uma empresa como a H-E-B tenha escolhido Haskell para substituir COBOL.
As duas frases em que dizem que um agente de codificação consegue portar mecanicamente bibliotecas Go para Haskell, reduzindo a diferença de velocidade de desenvolvimento, e logo em seguida admitem que, sem prompts inteligentes, talvez não se aproveite suficientemente o sistema de tipos, expõem um problema contraditório.
Por outro lado, como erros triviais são eliminados na etapa de compilação, quando a engenharia do caos injeta falhas de infraestrutura, aparecem erros reais de lógica e de recursos em vez do ruído típico de linguagens dinamicamente tipadas; isso parece ser o motivo mais convincente para considerar Haskell
Na semana passada tentei configurar builds Haskell isolados no Bazel, mas não encontrei uma solução pronta para uso.
rules_haskellnão é isolado por padrão, e a abordagem comum parece depender bastante denixpkgs. Parece que Haskell depende do linker C++ do sistema; fico me perguntando se essa é a situação real hoje ou se há alguma alternativa que eu deixei passar