Einführung und Betrieb von Enterprise Haskell bei H-E-B
(blog.haskell.org)- Im Zuge der Modernisierung der Lieferkette für mehr als 435 Filialen und Logistikzentren wurde Haskell eingeführt; acht Jahre später laufen rund 1 Million Zeilen Code, die von mehreren Teams betreut werden, in geschäftskritischen Produktionssystemen
- Haskell reduziert typische Laufzeitfehler und unterstützt Refactorings auf Basis von GHC sowie die Integration mit Legacy-Systemen, erfordert aber eigene Pflege wegen fehlender SDKs und den Umgang mit der Komplexität fortgeschrittener Typ-Features
- Ob die Einführung gelingt, hing stärker von Teamfähigkeit und Kultur als von der Sprache ab; bei zwei Projekten mit Speicherleck-Problemen und dem Weggang zentraler Entwickler konnte sich die Einführung nicht etablieren
- Die GHC-Laufzeit war bei H-E-B nie die Ursache eines Produktionsausfalls; mit Fortschritten im Ökosystem wie HLS, OpenTelemetry und Profiling ist Production Haskell zu einer wettbewerbsfähigen Option geworden
- Fortgeschrittene Features sollten auf kleine Bibliotheken mit klaren Interfaces beschränkt werden, begleitet von Tests, Observability und Wissensaustausch; im Zeitalter von Coding Agents werden GHC-Typprüfung und schnelles Feedback noch wichtiger
Aufgabe der Mainframe-Modernisierung
- Für die Kunden des Einzelhändlers H-E-B, des größten nicht börsennotierten Unternehmens in Texas, ist es wichtiger, dass Produkte in den Regalen stehen, als wie die internen Systeme aussehen
- Über Jahrzehnte liefen COBOL-Mainframes stabil, doch in den letzten zehn Jahren kamen technikgetriebene Funktionen wie Curbside Pickup, Lieferung nach Hause, Echtzeitbestand und Produktsuche im Laden hinzu
- Mit der Ausweitung des Lieferkettenbetriebs auf mehr als 435 Filialen und die unterstützenden Logistikzentren wurden die bestehenden Kernsysteme mit Anforderungen konfrontiert, die über ihren ursprünglichen Entwurfsrahmen hinausgingen
- Die größte Schwierigkeit der Mainframe-Modernisierung besteht nicht darin, Code in die Cloud zu verlagern, sondern das über Jahrzehnte in Legacy-Business-Logik angesammelte organisationsspezifische Wissen zu bewahren
- Die Aufteilung eines Monolithen mit Hunderten Tabellen in Microservices, die autonomen Engineering-Teams gehören, ist technisch und politisch enorm aufwendig und teuer
Einführung von Haskell und Betriebsgröße
- Berater schlugen Haskell als gemeinsame Sprache für ein neues Projekt vor, das die größte Mainframe-Anwendung des Unternehmens ersetzen sollte
- Was als Experiment begann, wuchs zu mehreren geschäftskritischen Produktionssystemen in der Lieferkette heran
- Acht Jahre nach der Einführung warten mehrere Teams in unterschiedlichen Domänen rund 1 Million Zeilen Haskell-Code
- Der langfristige Produktionseinsatz hat neben realen Erfolgen auch technische und organisatorische Schwierigkeiten sichtbar gemacht
Vorteile für das Geschäft
- Unternehmen wollen nicht Lazy Evaluation, pure Funktionen oder unveränderliche Daten um ihrer selbst willen, sondern Systeme wie ein COBOL der nächsten Generation, das 40 Jahre lang ohne Nullzeigerfehler und mit niedrigen Wartungskosten läuft
- Haskell-Anwendungen eliminieren von Haus aus viele Arten von Laufzeitfehlern, die in imperativen und dynamisch typisierten Programmen häufig sind
- Bugs verschwinden nicht vollständig, aber die verbleibenden Probleme konzentrieren sich eher auf Fehler in der Business-Logik als auf Nullzeigerfehler oder Typverwechslungen
- Bei einer über Jahre laufenden Legacy-Migration nach dem Strangler-Fig-Muster unterstützt GHC sichere Refactorings
- Während sich Anforderungen herausbilden, kann wiederholt angepasst werden, sodass die Migration agil statt nach dem Wasserfallmodell erfolgen kann
- Unternehmen können Ergebnisse schrittweise messen, statt vorab in falsch verfestigte Resultate zu investieren
- Haskell-Parser erlauben es, tausendzeilige Legacy-Formate wie feste IBM-Copybooks direkt als Vertrag zu nutzen
- Über eine starke Foreign Function Interface (FFI) ist auch die Integration mit bestehenden Diensten möglich, die nur C-Bibliotheken bereitstellen
- H-E-B schrieb Haskell-Bibliotheken als Wrapper um
simdjsonC++, OracleODPI-Cund IBM MQ
- H-E-B schrieb Haskell-Bibliotheken als Wrapper um
Fehlende SDKs und Eigentümerschaft an Verträgen
- Wenn Haskell-SDKs fehlen, müssen benötigte Clients und Integrationslogik selbst erstellt und dauerhaft gepflegt werden
- Dafür besitzt man den Vertrag selbst, vermeidet Vendor Lock-in und muss nicht darauf warten, dass Maintainer übergeordneter Projekte Patches veröffentlichen
- H-E-B hielt diesen Tausch bei Kernabhängigkeiten für lohnend
- Coding Agents können die mechanische Arbeit beim Übertragen von Go-Bibliotheken nach Haskell gut erledigen und so Geschwindigkeitsunterschiede zwischen Sprachen verringern
- Eine bloße Übersetzung ohne sorgfältige Prompts nutzt die Vorteile des Haskell-Typsystems jedoch womöglich nicht voll aus
Mehrsprachige Integration über eingebettete DSLs
- Haskell eignet sich stark als Host-Sprache für eingebettete DSLs (eDSLs)
- Ein zentrales Team kann Domänenmodell oder Business-Logik einmal definieren und daraus typsicheren Code für Sprachen erzeugen, die andere Teams verwenden
- Andere Teams können so integrieren, ohne selbst Haskell schreiben zu müssen
- Hydra ist ein Open-Source-DSL-Framework, das aus Ubers Arbeit mit Graphdaten hervorgegangen ist
- Es wird in Haskell gebootstrapt und erzeugt semantisch äquivalente Implementierungen für Java, Python, Scala und andere Sprachen
- Eine gemeinsame Testsuite stellt die Gleichwertigkeit der Implementierungen zwischen den Sprachen sicher
- H-E-B prototypisierte eine Haskell-DSL, über die Haskell-, TypeScript- und Java-Teams sensible Finanzberechnungen gemeinsam nutzen können
- Außerdem wurde eine bidirektionale JSON-Schema-DSL aufgebaut
- Ähnlich wie autodocodec, aber mit einer tagless-final-Kodierung, durch die Nutzer die Kerndomänen über eigene Typeclasses erweitern können
- Damit werden Schemaversionierung sowie die Generierung von Dokumentation und OpenAPI ebenso wie Regressions- und Golden-Tests standardisiert
- Durch ad-hoc geschriebene Interpreter lassen sich JSON-Schemata in PostgreSQL-Tabellendefinitionen umwandeln oder
aesondurchsimdjsonersetzen und zusätzliche Validierung anwenden
Recruiting und Engineering-Kultur
- Da moderne Sprachen bereits viele von Haskell beeinflusste Features für Typsicherheit, Purity und Nebenläufigkeit bieten, liegt der eigentliche Unterschied in der Engineering-Kultur, die eine Sprache fördert
- Haskell zieht Entwickler an, die Wert auf Korrektheit und das Design von Abstraktionen legen
- Die Besetzung von Haskell-Rollen war nicht so schwierig, wie das Management erwartet hatte
- Es gab Bewerber, die sich unabhängig vom besonderen Interesse an Lieferketten gerade wegen Haskell bewarben
Grenzen des Komplexitätsbudgets
- Haskell ist eine ältere Sprache mit verschiedenen Schwächen, daher ist es wichtig, aktuelle GHC-Releases mitzugehen
- Wenn einem Team Experten fehlen, kann sich ein einzelner Sprachmangel über das gesamte Projekt ausbreiten
- Zu Beginn setzten erfahrene Berater Guardrails, um übermäßigen Einsatz von Language Extensions und benutzerdefinierten Operatoren zu verhindern
- Vor den Language Editions GHC2021 und GHC2024 verwaltete jedes Team seinen eigenen Satz an Standard-Extensions
- Das diente als Komplexitätsbudget, das den Sprachumfang begrenzte, den neue Mitarbeitende vor ihren ersten Beiträgen lernen mussten
- Auch Abhängigkeiten verbrauchen Komplexitätsbudget, weshalb eine organisationsweite Durchsetzung schwierig ist
servantbeschreibt APIs als typstufige DSL; Erweiterungen mit benutzerdefinierten Kombinatoren bringen Type Families und schwer verständliche Fehlermeldungen mit sichhaxlmodelliert alle Datenquellen als GADTs, die über den Ergebnistyp indiziert sind, um parallele Arbeiten zu bündeln
- Je erfahrener Entwickler werden, desto größer wird die Versuchung, Muster wie GADT, Type Family, Generic, Tagless Final, Arrow, Free Structure und Higher-Kinded Data Type einzuführen
- Werden solche Konzepte auf eine ohnehin komplexe Business-Domäne aufgesetzt, wird das Versprechen leichter Wartbarkeit schwächer
- Da fortgeschrittene Features mit wachsender Vertrautheit im Ökosystem schrittweise zunehmen, kann der Komplexitätsanstieg erst spät sichtbar werden
Betriebsprinzipien für langweiliges Haskell
- Auf Organisationsebene lässt sich Boring Haskell leicht empfehlen, aber schwer erzwingen
- Wird fortgeschrittenes Haskell hinter vernünftigen Interfaces gekapselt, lässt sich die Reichweite begrenzen, und das Komplexitätsbudget bleibt auf kleine Bibliotheken beschränkt, die von wenigen Experten gepflegt werden
- Sprachliche Komplexität ist kein exklusives Haskell-Problem, aber Haskell bietet eine besonders große Bandbreite fortgeschrittener Werkzeuge
- Weniger als Zeilenzahl oder Tokenverbrauch zeigt bei Haskell-Projekten die Frage, wie viel Code beim nächsten GHC-Upgrade bricht, die tatsächliche Komplexität
Zwei Projekte, in denen sich Haskell nicht etablierte
- Haskell kann ein Team stärken und für ein anderes eine Hürde sein; auch bei H-E-B konnte es sich aus unterschiedlichen Gründen in zwei Projekten nicht etablieren
- Im ersten Fall folgten erfahrene Entwickler ohne Haskell-Erfahrung den Projekt-Templates und Konventionen anderer Teams, doch ein Speicherleck (space leak) brachte das gesamte Programm zum Stillstand
- Da kein starker Haskell-Entwickler das Team anleitete, führte dieses eine Problem zum Projektabbruch
- Im zweiten Fall wechselten die Ingenieure, die Haskell unterstützt und die Anwendung gebaut hatten, wegen geänderter Prioritäten in andere Projekte
- Der Grund hatte nichts mit der Sprache selbst zu tun
- Beide Teams hatten die Option, zu bestehenden Technologien zurückzukehren, und waren nicht überzeugt, dass Haskell ihre Produktivität erhöhte
- Wenn es einen Ausweg wie ein Spring-Boot-Template gibt, kann die Motivation gering sein, ein schwieriges Speicherleck bis zum Ende zu diagnostizieren
- Da LLMs inzwischen ganze Codebasen mechanisch übersetzen können, werden Umschreibungen als Option noch breiter verfügbar
- Die langfristige Verteidigungslinie ist nicht cleverer, schwer neu zu schreibender Code, sondern ein Team, das Haskell weiter betreiben will
Produktionsausfälle und Diagnose
- Auch in Teams, die Haskell angenommen hatten, traten mehrere schwerwiegende Probleme auf
- Bei benutzerdefiniertem Reference Counting trat ein Double-Free-Fehler auf
- In einem Projekt wurde die parallele Garbage Collection deaktiviert, um ein außer Kontrolle geratenes Speicherleck zu beheben
- Eine Endlosschleife war ein besonders gefürchteter Bug, weil schon ihre Entdeckung schwierig war
- Die GHC-Laufzeit war nie die eigentliche Ursache eines Produktionsausfalls bei H-E-B
- Durchsatz und Speicherverbrauch im Normalbetrieb waren für die Workloads von H-E-B ausreichend wettbewerbsfähig; die Problemfälle waren Ausnahmen, nicht die Regel
- Die Ingenieure lösten Probleme, indem sie Core Dumps von Segmentation Faults, Garbage-Collection-Metriken und Heap-Profile analysierten
- Wegen des starken Typsystems sollte nicht an Testabdeckung gespart werden
- Da Haskell-Anwendungen einfache Fehlertypen bereits beim Kompilieren eliminieren, kann Chaos Engineering reale Logik- und Ressourcenfehler direkter sichtbar machen
Veränderte Entwicklerwerkzeuge und das Ökosystem
- Die ersten Systeme starteten mit GHC 8.2; damals mussten die meisten API-Clients und SDKs für gängige Plattformen noch selbst geschrieben werden
- Haskell Language Server existierte noch nicht, GHCup war gerade erst erschienen, und Runtime-Profiling war schwierig
- Entwickler, die an Java-IDEs gewöhnt waren, empfanden die Tooling-Umgebung für Haskell als unbequem; H-E-B unterstützte sogar die Entwicklung von Debug Adapter Protocol Support für VS Code
- Heute hat sich Production-Haskell über bloße Einsatzfähigkeit hinaus zu einer wettbewerbsfähigen Option entwickelt
- Zum Werkzeugkasten gehören HLS,
ghcid,ghciwatch,static-ls, tricorder und Editor-Plugins- Einige nutzen GHC-Plugins für eine reichhaltige, GHCi-zentrierte Entwicklung, andere arbeiten mit VS Code und HLS
- So können Entwickler Werkzeuge nach ihren Vorlieben wählen
- Für Observability werden
hs-opentelemetryund opentelemetry-auto eingesetztopentelemetry-autoist ein GHC-Plugin, das OTel-Spans automatisch in Haskell-Funktionen einfügt- Damit lässt sich ein Instrumentierungsumfeld schaffen, das mit Java und Python vergleichbar ist
Produktionsbibliotheken und Laufzeitverbesserungen
- Die Services verwenden Bibliotheken wie
warp,aeson,attoparsec,stm,conduit,servantund orville-postgresql - Wenn dem Ökosystem benötigte Funktionen fehlen, werden eigene Bibliotheken entwickelt
- arbiter ist eine transaktionsbasierte PostgreSQL-Job-Queue aus der Arbeit bei H-E-B und läuft inzwischen in Produktion
- GHC verbessert Runtime-Diagnostik, Compiler-Performance, Monitoring-Funktionen wie Eventlog Streaming und Exception Annotations
- Seit der Einführung von Info Table Profiling in GHC 9.2 sind die Sorgen über Speicherlecks deutlich kleiner geworden
Interne Community und Verteilung von Wissen
- H-E-B misst internen Bibliotheken große Bedeutung bei und ermutigt Entwickler, sowohl an internen als auch an Open-Source-Projekten mitzuwirken
- Über Jahre wurde alle zwei Wochen ein Haskell-Treffen organisiert, auf dem Ingenieure ihre Erkenntnisse teilen und andere ausbilden konnten
- Um Silos in einer Enterprise-Organisation zu verringern, müssen Ideen und Muster zwischen Teams ausgetauscht werden
- Wenn mehr Ingenieure die Codebasis lesen und mitverantworten, sinkt das Bus-Faktor-Risiko, dass der Weggang einer einzelnen Person ein Projekt erschüttert
Kriterien für den Einsatz fortgeschrittener Features
- Es gibt lockere Grundregeln, aber wo das Komplexitätsbudget eingesetzt wird, entscheidet am Ende der Tech Lead
- Wenn gespeicherte personenbezogene Daten (PII) verschlüsselt werden müssen, kann sich eine typstufige Garantie lohnen, die sensible Datenlecks von einem Laufzeitvorfall in einen Kompilierfehler verwandelt
- Dagegen ist das Vereinheitlichen von vier API-Verträgen mit dem Trees-that-Grow-Muster womöglich nicht wert, externe Schemata hinter Type Families zu verstecken
- Boring Haskell verbietet weder Type-Level-Programming noch fortgeschrittene GHC-Features, sondern setzt prinzipientreue Konventionen und bewertet die Kosten von Abweichungen
- Eine zentrale Konvention lautet: „keine unerwarteten Effekte aus der Ferne“
- Codegenerierung mit
TemplateHaskellund aufGenericbasierende Serialisierungslogik werden vermieden - Boilerplate wird bewusst ausgeschrieben, damit für Programmierer klar ersichtlich ist, was geschieht
- Quasiquoter oder Deriving über
Genericallysind nicht vollständig verboten
- Codegenerierung mit
- Wenn jemand für ein Webformular eine typstufige Zustandsmaschine bauen will oder GADTs dort einsetzt, wo gewöhnliche ADTs reichen, braucht es gesunden Zweifel und Widerspruch auf Teamebene
- Fortgeschrittenes Haskell existiert sowohl in Bibliotheken als auch in Produktionsanwendungen, aber der Umfang von Experimenten muss begrenzt bleiben
- In der Balance zwischen Arbeit und technischem Spiel entsteht das eigentliche Engineering
Die Zukunft von Haskell und Coding Agents
- Über viele Jahre mit GHC wurden einige wenige Bugs entdeckt und gemeldet, aber nichts, was die Arbeit ernsthaft zum Stillstand gebracht hätte
- Produktionsvorfälle waren selten, und sie lagen weit häufiger in der Business-Logik als in Typfehlern oder der Laufzeit
- Je größer der von Coding Agents geschriebene Codeanteil wird, desto stärker verlagert sich der Engpass vom Schreiben zur Verifikation
- Das Typsystem von GHC hilft Menschen beim sicheren Refactoring und bietet zugleich eine schnelle Feedbackschleife, die von Agents halluzinierten Code noch vor der Ausführung zurückweist
- Damit sich das Potenzial von Haskell in der Industrie erweitert, müssen mehr echte Erfolgsgeschichten aus der Produktion geteilt werden
1 Kommentare
Kommentare auf Lobste.rs
Außerhalb von Texas ist das vielleicht wenig bekannt, aber H-E-B ist eine ikonische Institution in Texas; Größe und Einfluss sind so groß, dass man sie fast als eine weitere Behörde der Landesregierung bezeichnen könnte.
Viele Filialen haben Anlagen zur Tortilla-Herstellung, und für ein Produkt aus Großproduktion ist die Qualität hervorragend. Außerdem heißt es, dass sie auch Haskell einsetzen.
Dass ein Unternehmen wie H-E-B Haskell gewählt hat, um COBOL zu ersetzen, ist wirklich erstaunlich.
Die zwei Sätze, in denen es heißt, ein Coding-Agent könne Go-Bibliotheken mechanisch nach Haskell übertragen und dadurch den Unterschied in der Entwicklungsgeschwindigkeit verringern, während zugleich sofort eingeräumt wird, dass er ohne geschickte Prompts das Typsystem möglicherweise nicht ausreichend nutzt, zeigen ein widersprüchliches Problem auf.
Andererseits ist der überzeugendste Grund, Haskell in Betracht zu ziehen, dass triviale Fehler bereits beim Kompilieren eliminiert werden; wenn man also per Chaos Engineering Infrastrukturfehler injiziert, treten statt des Rauschens dynamisch typisierter Sprachen die tatsächlichen Logik- und Ressourcenfehler zutage.
Letzte Woche habe ich versucht, in Bazel isolierte Haskell-Builds einzurichten, aber keine sofort brauchbare Lösung gefunden.
rules_haskellist standardmäßig nicht isoliert, und der gängige Ansatz scheint stark vonnixpkgsabzuhängen. Haskell scheint vom C++-Linker des Systems abhängig zu sein; ich frage mich, ob das derzeit tatsächlich der Stand der Dinge ist oder ob ich eine Alternative übersehen habe.