H-E-B의 엔터프라이즈 Haskell 도입과 운영
(blog.haskell.org)- 435개가 넘는 매장과 물류센터의 공급망을 현대화하는 과정에서 Haskell을 도입했으며, 8년 뒤 여러 팀이 관리하는 약 100만 줄의 코드가 핵심 프로덕션 시스템에서 운영되고 있음
- Haskell은 런타임 오류 유형을 줄이고 GHC 기반 리팩터링과 레거시 연동을 지원하지만, SDK 부족에 따른 자체 유지보수와 고급 타입 기능의 복잡성을 감수해야 함
- 성공 여부는 언어보다 팀 역량과 문화에 크게 좌우됐으며, Haskell 전문가가 없던 팀의 공간 누수와 핵심 개발자 이탈로 두 프로젝트에서는 도입이 정착하지 못함
- GHC 런타임은 H-E-B의 프로덕션 장애 원인이 된 적이 없으며, HLS·OpenTelemetry·프로파일링 등 생태계가 발전하면서 프로덕션 Haskell은 경쟁력 있는 선택지로 바뀜
- 고급 기능을 작은 라이브러리와 명확한 인터페이스 뒤에 제한하고 테스트·관측성·지식 공유를 병행해야 하며, 코딩 에이전트 시대에는 GHC의 타입 검증과 빠른 피드백이 더욱 중요해짐
메인프레임 현대화 과제
- 텍사스 최대 비상장 기업인 소매업체 H-E-B의 고객은 내부 시스템보다 매장 선반에 상품이 채워져 있는지를 중시함
- 수십 년간 COBOL 메인프레임이 안정적으로 운영됐지만, 최근 10년 사이 커브사이드 픽업·가정 배송·실시간 재고·매장 내 검색 같은 기술 중심 기능이 등장함
- 435개가 넘는 매장과 이를 지원하는 물류센터로 공급망 운영을 확장하면서 기존 핵심 시스템이 처음 설계된 범위를 넘어서는 요구를 받게 됨
- 메인프레임 현대화의 핵심 난점은 코드를 클라우드로 옮기는 일이 아니라, 수십 년간 레거시 비즈니스 로직에 축적된 조직 고유 지식을 보존하는 데 있음
- 수백 개 테이블을 다루는 모놀리스를 자율적인 엔지니어링 팀 소유의 마이크로서비스로 분할하는 작업은 기술적·정치적으로 거대하고 비용도 많이 듦
Haskell 도입과 운영 규모
- 컨설턴트들은 회사에서 가장 큰 메인프레임 애플리케이션을 대체하는 신규 프로젝트의 공통 언어로 Haskell을 제안함
- 실험으로 시작한 작업은 여러 핵심 공급망 프로덕션 시스템으로 성장함
- 도입 8년 뒤에는 서로 다른 도메인의 여러 팀이 약 100만 줄에 이르는 Haskell 코드를 유지보수하고 있음
- 장기간의 프로덕션 운영을 통해 실제 성과뿐 아니라 기술적·조직적 어려움도 드러남
비즈니스에서 얻은 장점
- 기업이 원하는 것은 지연 평가·순수 함수·불변 데이터 자체가 아니라, 40년간 null pointer exception 없이 낮은 유지보수 비용으로 작동할 차세대 COBOL 같은 시스템임
- Haskell 애플리케이션은 명령형·동적 타입 프로그램에서 흔한 런타임 오류 유형을 기본적으로 제거함
- 버그가 완전히 사라지지는 않지만, 남는 문제는 null pointer exception이나 타입 혼동보다 비즈니스 로직 오류에 집중됨
- 수년에 걸친 교살자 무화과(strangler fig) 방식의 레거시 이전에서 GHC는 안전한 리팩터링을 지원함
- 요구사항이 형성되는 동안 반복적으로 수정할 수 있어 폭포수 방식 대신 애자일하게 이전할 수 있음
- 기업은 잘못 굳어진 결과물에 선불 투자하는 대신 점진적으로 성과를 측정할 수 있음
- Haskell 파서는 고정 폭 IBM copybook처럼 수천 줄에 이르는 레거시 형식을 직접 계약으로 사용할 수 있게 함
- 강력한 외부 함수 인터페이스(FFI) 를 통해 C 라이브러리만 제공하는 기존 서비스와도 연동할 수 있음
- H-E-B는
simdjsonC++, OracleODPI-C, IBM MQ를 감싸는 Haskell 라이브러리를 작성함
- H-E-B는
SDK 부족과 계약 소유권
- Haskell SDK가 부족하면 필요한 클라이언트와 통합 코드를 직접 만들고 계속 유지보수해야 함
- 그 대가로 계약을 직접 소유하고 공급업체 종속을 피하며, 상위 프로젝트 관리자가 수정본을 배포하기를 기다리지 않아도 됨
- H-E-B는 핵심 의존성에 대해서는 이 교환이 충분한 가치가 있다고 판단함
- 코딩 에이전트는 Go 라이브러리를 Haskell로 옮기는 기계적인 작업을 잘 처리해 언어 간 개발 속도 차이를 줄일 수 있음
- 다만 단순 번역은 정교한 프롬프트가 없으면 Haskell 타입 시스템의 이점을 충분히 활용하지 못할 수 있음
내장 DSL을 통한 다언어 통합
- Haskell은 내장 DSL(eDSL) 의 호스트 언어로 강점을 가짐
- 중앙 팀이 도메인 모델이나 비즈니스 로직을 한 번 정의한 뒤 다른 팀이 사용하는 언어의 안전한 타입 코드로 생성할 수 있음
- 다른 팀은 Haskell을 직접 작성하지 않고도 해당 시스템과 통합 가능함
- Hydra는 Uber의 그래프 데이터 작업에서 발전한 오픈소스 DSL 프레임워크임
- Haskell에서 부트스트랩되며 Java·Python·Scala 등의 의미상 동등한 구현을 생성함
- 공유 테스트 스위트로 언어별 구현의 동등성을 보장함
- H-E-B는 민감한 재무 계산을 Haskell·TypeScript·Java 팀이 공유할 수 있는 Haskell DSL을 프로토타이핑함
- 양방향 JSON 스키마 DSL도 구축함
- autodocodec와 유사하지만, 사용자가 자체 타입 클래스로 핵심 의미 체계를 확장할 수 있는 tagless final 인코딩을 사용함
- 스키마 버전 관리와 문서·OpenAPI 생성, 회귀·골든 테스트를 표준화함
- 임시 인터프리터를 작성해 JSON 스키마를 PostgreSQL 테이블 정의로 변환하거나
aeson을simdjson으로 교체하고 추가 검증을 적용할 수 있음
채용과 엔지니어링 문화
- 현대 언어도 Haskell에서 영향을 받은 타입 안전성·순수성·동시성 기능을 다수 제공하므로, 실제 차이는 언어가 촉진하는 엔지니어링 문화에 있음
- Haskell은 정확성과 추상화 설계를 중시하는 개발자를 끌어들임
- Haskell 역할의 채용은 경영진이 예상한 것만큼 어렵지 않았음
- 공급망에 특별한 관심이 없더라도 Haskell 자체 때문에 지원하는 후보가 있었음
복잡성 예산의 한계
- Haskell은 오래된 언어로 여러 결함이 있어 최신 GHC 릴리스를 따라가는 것이 중요함
- 팀에 전문가가 없으면 하나의 언어적 결함이 프로젝트 전체에 퍼질 수 있음
- 초기에는 숙련된 컨설턴트들이 언어 확장과 사용자 정의 연산자의 과도한 사용을 막는 가드레일을 마련함
- GHC2021·GHC2024 언어 에디션이 없던 시기에는 각 팀이 기본 확장 집합을 직접 관리함
- 신규 입사자가 기여하기 전에 익혀야 할 언어 범위를 제한하는 복잡성 예산 역할을 했음
- 의존성 자체도 복잡성 예산을 소모하므로 조직 차원의 강제는 어려움
servant는 API를 타입 수준 DSL로 표현하며, 사용자 정의 결합자(combinator) 확장에 type family와 난해한 오류가 수반됨haxl은 동시 작업을 일괄 처리하기 위해 모든 데이터 소스를 결과 타입으로 인덱싱된 GADT로 모델링함
- 개발자가 숙련될수록 GADT·type family·generic·tagless final·arrow·free structure·higher-kinded data type 패턴을 도입하려는 유혹이 커짐
- 이미 복잡한 비즈니스 도메인 위에 이런 개념이 쌓이면 쉬운 유지보수라는 약속이 약해짐
- 생태계에 익숙해지면서 고급 기능이 점진적으로 늘어나기 때문에 복잡성 증가는 뒤늦게 드러날 수 있음
지루한 Haskell의 운영 원칙
- 조직 차원에서 지루한 Haskell을 권장하기는 쉽지만 강제하기는 어려움
- 고급 Haskell을 합리적인 인터페이스 뒤에 캡슐화하면 영향 범위를 줄이고, 복잡성 예산을 소수 전문가가 관리하는 작은 라이브러리에 한정할 수 있음
- 언어 수준의 복잡성은 Haskell만의 문제는 아니지만, Haskell은 사용할 수 있는 고급 도구의 범위가 특히 넓음
- 코드 줄 수나 토큰 사용량보다 다음 GHC 업그레이드에서 얼마나 많은 코드가 깨지는지가 Haskell 프로젝트의 복잡성을 더 잘 보여줌
정착하지 못한 두 프로젝트
- Haskell은 한 팀의 역량을 강화하면서 다른 팀에는 장벽이 될 수 있으며, H-E-B에서도 서로 다른 이유로 두 프로젝트에 정착하지 못함
- 첫 사례에서는 Haskell 경험이 없는 숙련 개발자들이 다른 팀의 프로젝트 템플릿과 관례를 따랐지만, 공간 누수(space leak) 로 프로그램 전체가 멈춤
- 강한 Haskell 개발자가 팀을 안내하지 못해 이 문제 하나가 프로젝트 중단으로 이어짐
- 두 번째 사례에서는 Haskell을 지지하고 애플리케이션을 만든 엔지니어들이 우선순위 변경으로 다른 프로젝트로 이동함
- 이는 언어 자체와 무관한 이유였음
- 두 팀 모두 기존 기술로 돌아갈 대안이 있었으며 Haskell이 생산성을 높인다고 확신하지 못함
- Spring Boot 템플릿 같은 탈출구가 있으면 어려운 공간 누수를 끝까지 진단할 동기가 약해질 수 있음
- LLM이 코드베이스 전체를 기계적으로 번역할 수 있게 되면서 재작성 선택지는 더욱 넓어짐
- 장기적인 방어선은 영리해서 재작성하기 어려운 코드가 아니라, Haskell을 계속 유지하고 싶어 하는 팀임
프로덕션 장애와 진단
- Haskell을 받아들인 팀에서도 여러 심각한 문제가 발생함
- 사용자 정의 참조 카운팅에서 double-free 오류가 발생함
- 한 프로젝트에서는 폭주하는 공간 누수를 해결하기 위해 병렬 가비지 컬렉션을 비활성화함
- 무한 루프는 발견 자체가 어려워 특히 두려운 버그였음
- GHC 런타임은 H-E-B의 프로덕션 장애 근본 원인이 된 적이 없음
- 정상 상태의 처리량과 메모리 사용량은 H-E-B 워크로드에서 충분히 경쟁력이 있었으며, 문제 사례들은 일반적인 상황이 아니라 예외였음
- 엔지니어들은 세그멘테이션 오류 코어 덤프, 가비지 컬렉션 지표, 힙 프로파일을 추적해 문제를 해결함
- 강한 타입 시스템을 이유로 테스트 커버리지를 희생해서는 안 됨
- Haskell 애플리케이션은 단순한 오류 유형이 컴파일 단계에서 제거되므로 카오스 엔지니어링을 통해 실제 로직·자원 장애를 더 직접적으로 드러낼 수 있음
달라진 개발 도구와 생태계
- 초기 시스템은 GHC 8.2에서 시작했으며, 당시에는 인기 플랫폼용 API 클라이언트와 SDK 대부분을 직접 작성해야 했음
- Haskell Language Server는 존재하지 않았고 GHCup은 막 등장했으며 런타임 프로파일링도 어려웠음
- Java IDE에 익숙한 개발자들은 Haskell 도구 환경을 불편해했고, H-E-B는 VS Code 사용자를 위한 Debug Adapter Protocol 지원 개발을 후원하기도 함
- 현재는 프로덕션급 Haskell 개발이 가능한 수준을 넘어 경쟁력 있는 수준으로 발전함
- 개발 도구에는 HLS,
ghcid,ghciwatch,static-ls, tricorder, 편집기 플러그인이 있음- 일부는 풍부한 GHCi 중심 개발을 위해 GHC 플러그인을 사용하고, 일부는 VS Code와 HLS를 사용함
- 개발자가 선호하는 도구를 선택할 수 있도록 함
- 관측성에는
hs-opentelemetry와 opentelemetry-auto를 사용함opentelemetry-auto는 Haskell 함수에 OTel span을 자동 삽입하는 GHC 플러그인임- Java·Python과 비교해도 유사한 계측 환경을 제공할 수 있음
프로덕션 라이브러리와 런타임 개선
- 서비스는
warp,aeson,attoparsec,stm,conduit,servant, orville-postgresql 같은 라이브러리를 사용함 - 생태계에 필요한 기능이 없으면 자체 라이브러리를 개발함
- arbiter는 H-E-B 작업에서 나온 트랜잭션 기반 PostgreSQL 작업 큐이며 현재 프로덕션에서 운영됨
- GHC는 런타임 진단, 컴파일러 성능, eventlog streaming 같은 모니터링 기능과 예외 주석을 개선하고 있음
- GHC 9.2에 info table profiling이 도입된 뒤 공간 누수에 대한 우려가 크게 줄어듦
내부 커뮤니티와 지식 분산
- H-E-B는 내부 라이브러리를 중요하게 다루며, 개발자가 사내 프로젝트와 오픈소스 프로젝트 모두에 참여하도록 권장함
- 수년간 격주 Haskell 모임을 운영해 엔지니어가 탐구 결과를 공유하고 다른 구성원을 교육함
- 엔터프라이즈 조직의 사일로를 줄이려면 팀 사이에서 아이디어와 패턴이 교류돼야 함
- 더 많은 엔지니어가 코드베이스를 읽고 소유하게 만들면 한 사람의 퇴사가 프로젝트를 흔드는 버스 팩터 위험을 낮출 수 있음
고급 기능을 허용하는 기준
- 느슨한 기본 규칙을 두되, 복잡성 예산을 어디에 쓸지는 기술 리드가 최종 결정함
- 저장된 개인식별정보(PII)를 암호화해야 한다면 민감한 데이터 유출을 런타임 사고가 아니라 컴파일 오류로 만드는 타입 수준 보장이 투자 가치가 있을 수 있음
- 네 개의 API 계약을 trees-that-grow 패턴으로 통합하는 작업은 type family로 외부 스키마를 가릴 만큼 가치가 없을 수 있음
- 지루한 Haskell은 타입 수준 프로그래밍이나 고급 GHC 기능을 금지하지 않고, 원칙 있는 관례를 정한 뒤 이탈 비용을 평가함
- 주요 관례는 “멀리 떨어진 곳의 예기치 않은 작용 없음”임
TemplateHaskell코드 생성과Generic기반 직렬화 로직을 피함- 상용구를 직접 작성해 프로그래머에게 동작을 명시적으로 보여줌
- quasiquoter나
Generically를 통한 deriving을 완전히 금지하지는 않음
- 웹 폼에 타입 수준 상태 머신을 구현하거나 일반 ADT로 충분한 곳에 GADT를 사용할 때는 건전한 의심과 팀 차원의 반론이 필요함
- 고급 Haskell은 라이브러리와 프로덕션 애플리케이션 모두에 존재하지만, 실험의 범위를 제한해야 함
- 일과 기술적 놀이 사이를 조절하는 과정에서 실제 엔지니어링이 이뤄짐
앞으로의 Haskell과 코딩 에이전트
- 여러 해 동안 GHC를 사용하며 소수의 버그를 발견해 보고했지만, 업무를 중단시킬 정도의 문제는 없었음
- 프로덕션 사고는 드물며 타입 오류나 런타임보다 비즈니스 로직에서 발생하는 경우가 훨씬 많았음
- 코딩 에이전트가 작성하는 코드 비중이 커지면서 병목은 코드 작성에서 검증으로 이동하고 있음
- GHC의 타입 시스템은 사람이 안전하게 리팩터링하도록 돕는 동시에, 에이전트가 환각으로 만든 코드를 실행 전에 거부하는 빠른 피드백 루프를 제공함
- 산업 현장에서 Haskell의 가능성을 확장하려면 실제 프로덕션 성공 사례가 더 많이 공유돼야 함
댓글과 토론
Lobste.rs 의견들
-
텍사스 밖에서는 잘 모르겠지만 H-E-B는 텍사스의 상징적인 기관으로, 주 정부의 또 다른 부처라 해도 될 만큼 규모와 영향력이 큼
많은 매장에 토르티야 제조 시설이 있으며, 대규모 생산품치고 품질도 훌륭함. 게다가 Haskell도 사용한다고 함- 이름은 창업자 가문인 Howard Butt의 이니셜 H.B. 에서 유래함. Wegmans, Hannaford Market, Walmart처럼 창업자 이름을 따를 수도 있었지만 다행히 Buttmart라고 하지는 않았음
- 지역판 Costco 같은 곳인지 궁금함
-
H-E-B 같은 회사가 COBOL을 대체하려 Haskell을 선택했다는 사실이 정말 놀라움
코딩 에이전트가 Go 라이브러리를 Haskell로 기계적으로 옮길 수 있어 개발 속도 차이가 줄었다면서, 영리한 프롬프트 없이는 타입 시스템을 충분히 활용하지 못할 수 있다고 곧바로 인정하는 두 문장은 서로 모순된 문제를 드러냄
반면 사소한 오류가 컴파일 단계에서 제거되므로, 카오스 엔지니어링으로 인프라 장애를 주입했을 때 동적 타입 언어의 잡음 대신 실제 논리·자원 오류가 드러난다는 점은 Haskell을 고려할 가장 설득력 있는 이유로 들림- H-E-B는 Walmart처럼 규모가 크고 평판도 꽤 좋은 엔지니어링 조직을 갖추고 있어 아주 놀랍지는 않음. San Antonio 본사 근무를 꺼리는 소프트웨어 인재를 유치하려 Austin에 소프트웨어 전용 사무실까지 운영함
-
지난주 Bazel에서 격리된 Haskell 빌드를 구성하려 했지만 바로 쓸 만한 해결책을 찾지 못했음
rules_haskell은 기본적으로 격리되어 있지 않고, 흔한 접근법은nixpkgs에 크게 의존하는 듯함. Haskell이 시스템 C++ 링커에 의존하는 것 같은데, 이것이 현재의 실제 상황인지 놓친 대안이 있는지 궁금함