1P by GN⁺ | ★ favorite | 댓글 1개
  • 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는 simdjson C++, Oracle ODPI-C, IBM MQ를 감싸는 Haskell 라이브러리를 작성함

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 테이블 정의로 변환하거나 aesonsimdjson으로 교체하고 추가 검증을 적용할 수 있음

채용과 엔지니어링 문화

  • 현대 언어도 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을 받아들인 팀에서도 여러 심각한 문제가 발생함
  • 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-opentelemetryopentelemetry-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++ 링커에 의존하는 것 같은데, 이것이 현재의 실제 상황인지 놓친 대안이 있는지 궁금함