H-E-B에서 대규모 공급망 시스템을 Haskell로 구축하고 운영하며 얻은 경험, 장점, 어려움, 그리고 엔터프라이즈 환경에서의 교훈을 다룹니다.
Joshua Miller 2026년 7월 16일 [현장의 Haskeller들] #Production #H-E-B 이 글은 "현장의 Haskeller들" 시리즈의 두 번째 글입니다. 이 시리즈에서는 숙련된 엔지니어들을 초대해 자신이 전문성을 가진 주제, 모범 사례, 그리고 Haskell과 함께한 프로덕션 현장의 이야기를 들려주십니다.
2018년에 H-E-B에 합류했을 때, 저는 이전까지 맡아왔던 어떤 기술 직무와도 크게 다른 세계에 들어서고 있었습니다. H-E-B는 무엇보다도 소매 회사이며, 텍사스에서 가장 큰 비상장 기업입니다. 우리 고객들은 시스템 자체보다도 진열대가 가득 차 있기를 원하고, 수십 년 동안 그 시스템들은 제 역할을 해냈습니다. 즉, 견고한 COBOL 메인프레임이 매일같이 안정적으로 업무를 수행해 온 것입니다. 그런 수준의 신뢰성은 결코 작은 일이 아닙니다. 하지만 고객의 기대치는 달라졌습니다. 커브사이드 픽업, 자택 배송, 실시간 재고, 매장 내 검색, 그리고 그 밖의 수많은 기술 중심 기능들은 사실상 지난 10년 사이에 본격적으로 등장했고, 이런 수요를 충족하려면 435개가 넘는 매장과 그 매장들에 물류를 공급하는 유통 센터 전반에서 공급망 운영을 확장해야 합니다. 이런 종류의 성장은 핵심 시스템에 대해 원래 설계 당시보다 훨씬 더 많은 것을 요구하며, 특히 식료품 비즈니스가 해마다 더 디지털화되고 더 경쟁적으로 변해가는 상황에서는 더욱 그렇습니다.
세계의 상당 부분이 여전히 메인프레임 위에서 돌아간다는 사실은 비밀도 아닙니다. 현재 시스템이 현상을 유지하는 데 충분히 잘 작동하는데도 막대한 자본을 들여 대규모 리프트 앤 시프트 방식으로 클라우드로 옮길 유인은 대개 크지 않습니다. 여기서 진짜 어려움은 한 기계의 코드를 다른 기계로 복사하는 일이 아닙니다. 보통 이런 종류의 프로젝트가 기대한 성과를 내지 못하는 이유는 수십 년에 걸쳐 축적된 레거시 비즈니스 로직 속에 녹아든 맞춤형 조직 지식을 갖추지 못했기 때문입니다. 수백 개의 테이블을 대상으로 동작하는 하나의 모놀리식 애플리케이션을 어떻게 분리해 각기 독립적인 엔지니어링 팀이 소유하고 운영하는 마이크로서비스들로 나눌 수 있을까요? 이것은 엄청난 규모의 기술적 과업이자 정치적 과업이며, 비용도 많이 듭니다. 그리고 어떤 결과물이 나오든, 비즈니스 오너들은 이 과정을 다시는 반복하고 싶어 하지 않는다는 점만은 분명합니다.
Haskell은 원래 회사에서 가장 큰 메인프레임 애플리케이션을 대체하기 위한 새로운 이니셔티브의 공용어로 컨설턴트들에 의해 제안되었습니다. 저는 이 새로운 프로젝트가 막 시작되던 시점에 합류했고, 실제 영향력이 있는 실제 프로젝트에서 Haskell을 사용할 생각에 들떠 있었습니다. 실험으로 시작한 일은 이제 여러 개의 프로덕션 시스템으로 성장해 핵심 공급망 애플리케이션을 운영하고 있습니다. 8년이 지난 지금도 그 시스템들은 여전히 프로덕션에서 돌아가고 있으며, 거의 100만 줄에 이르는 Haskell 코드가 여러 도메인에 걸친 여러 팀에 의해 유지보수되고 있습니다. 그러니 분명한 성공도 있었지만, 분명한 어려움도 있었습니다. 저는 이 프로젝트들에서 얻은 제 경험이, Haskell을 프로덕션에서 운영하는 일을 궁금해하거나 이미 그렇게 하고 있는 분들에게 가치 있는 교훈으로 전해지기를 바랍니다.
프로그래밍 언어를 비즈니스 해법으로 제안하는 일은 험난한 싸움입니다. Haskell은 엔터프라이즈 언어로 알려져 있지 않고, 프로그래밍 언어 연구에서는 강세를 보이지만, 입문 과정에서는 한때 차지했던 입지를 잃었습니다. 비록 Dijkstra가 UT Austin Budget Council에 보낸 편지에서 Java보다 Haskell을 옹호한 유명한 변론을 남기긴 했지만 말입니다. 하지만 "엔터프라이즈 언어"라는 말은 언어 설계보다 시장의 힘을 더 많이 말해줍니다. 입문 과정을 Java로 채운 바로 그 관성이 채용 시장과 벤더 SDK 목록도 채우고 있으며, 그 관성의 거의 전부는 명령형 언어들에 속합니다. 이런 언어들은 채용하기 더 쉽고, 요즘에는 프롬프트하기도 더 쉽다고 여겨집니다. 엔터프라이즈는 지연 계산, 순수 함수, 불변 데이터, 또는 람다 계산과 객체지향 정통성을 저울질하는 어떤 것에도 관심이 없습니다. 엔터프라이즈가 원하는 것은 그저 또 하나의 COBOL입니다. 즉, 널 포인터 예외 없이, 더 낮은 유지보수 부담으로, 앞으로 40년 더 돌아갈 시스템입니다. 저는 Haskell이 바로 이 틈새에 들어맞는다고 굳게 믿습니다. 하지만 강화된 다형적 람다 계산과 software-transactional memory의 이점을 비즈니스 사례로 번역하는 일은 결코 단순하지 않습니다.
그렇다면 제 제안은 무엇일까요?
저는 CTO에게 Haskell 애플리케이션은 기본적으로 더 적은 종류의 런타임 실패를 겪게 되며, 그 결과 명령형 및 동적 타입 프로그램이 매일같이 마주치는 버그 범주 전체를 배제할 수 있다고 말할 수 있습니다. 물론 버그가 아예 없어지는 것은 아니지만, 남는 버그는 널 포인터 예외나 타입 혼동보다는 대체로 우리의 비즈니스 로직 안에 있는 흥미로운 종류의 것들입니다.
수년에 걸쳐 진행되는 레거시 시스템의 strangler fig 마이그레이션 앞에서, 저는 Haskell 개발자들에게 _실수해도 괜찮다_고 말할 수 있습니다. 비즈니스 요구사항이 아직 형태를 잡아가는 동안에도 반복적으로 개선할 수 있기 때문입니다. GHC가 고통 없는 리팩터링을 위한 도구를 제공하기 때문입니다. 바로 그 자유가 마이그레이션을 폭포수 방식이 아니라 애자일 프로젝트처럼 진행되게 합니다. 비즈니스는 잘못된 채로 굳어져 1년 안에 버려질 제품에 선불로 대가를 치르는 대신, 결과를 점진적으로 측정할 수 있습니다.
레거시 시스템과의 통합은 Haskell 프로그램에게 일상적인 업무입니다. 혹시 수천 줄에 달하는 고정 폭 IBM copybook을 파싱해야 하나요? Haskell의 파서는 최고 수준입니다. 레거시 시스템이 XML이나 JSON을 쉽게 출력할 수 없다면 그럴 필요도 없습니다. 그 시스템의 고유 형식 자체가 계약이 되기 때문입니다. 오래된 서비스가 C 라이브러리만 제공하나요? Haskell은 업계 최고 수준의 외부 함수 인터페이스를 갖추고 있습니다. 우리는 예를 들어 simdjson(C++), Oracle의 ODPI-C, IBM의 MQ를 감싼 Haskell 라이브러리들을 작성해 왔습니다.
현실 세계에서 Haskell SDK가 부족하다는 점은 처음에는 치명적인 문제처럼 보입니다. 실제로는 해당 유지보수 부담을 영원히 떠안는 대신, 계약을 완전히 스스로 소유하고, 벤더 종속을 피하며, 수정 사항이 업스트림 메인터이너에 의해 배포되기를 기다릴 필요가 없어집니다. 핵심 의존성에 대해서는 우리는 그 거래가 충분히 가치 있다고 판단했습니다. 오늘날에는 속도 차이도 겉보기만큼 크지 않습니다. 왜냐하면 여러분이 좋아하는 Go 라이브러리를 Haskell로 포팅하는 일은 코딩 에이전트가 잘 해내는 종류의 기계적 작업이기 때문입니다. 물론 그런 식의 기계적 번역은 영리한 프롬프팅이 없다면 타입 시스템의 장점을 충분히 살리지 못할 수도 있습니다.
Haskell을 매력적인 핵심 엔터프라이즈 언어로 만드는 더 미묘한 장점도 있습니다. 바로 임베디드 DSL의 호스트로서의 강점입니다. 중앙 팀은 도메인 모델이나 비즈니스 로직을 Haskell eDSL에 한 번 인코딩해 두고, 다른 팀들이 이미 사용하는 어떤 언어로든 안전하고 타입이 보장된 코드를 생성할 수 있습니다. 그러면 다른 팀은 Haskell을 직접 한 줄도 쓰지 않고도 여러분의 시스템과 연동할 수 있습니다. Uber의 그래프 데이터 작업에서 파생된 오픈소스 프로젝트 Hydra는 인상적인 사례입니다. 이것은 Haskell로 부트스트랩된 DSL 프레임워크로, Java, Python, Scala 등 여러 언어에 대해 의미적으로 동등한 구현을 생성하며, 공유 테스트 스위트가 동등성을 보장합니다. 여러분은 소스 단계에서는 Haskell의 정확성 보장을 유지하면서도, 다른 모든 팀이 이미 있는 자리에서 그들을 만날 수 있습니다.
우리는 H-E-B에서 이 임베디드 DSL 아이디어를 여러 방식으로 탐구해 왔습니다. 한 사례에서는 Haskell, TypeScript, Java 팀 간에 공유할 수 있는 민감한 금융 계산용 Haskell DSL을 프로토타이핑했습니다. 또 다른 사례에서는 양방향 JSON 스키마를 위한 Haskell DSL을 구축했는데, 개념적으로는 autodocodec 라이브러리와 유사하지만, 사용자가 자신의 타입 클래스로 핵심 의미론을 확장할 수 있도록 tagless final 인코딩을 사용했습니다. 이를 통해 우리 Haskell 팀들은 스키마 버저닝 전략, 문서화/OpenAPI 생성, 그리고 회귀/golden 테스트를 표준화할 수 있습니다. 그 외의 용도에 대해서는 개발자들이 ad-hoc 인터프리터를 작성할 수 있습니다. 예를 들어 JSON 스키마를 PostgreSQL 테이블 정의로 변환하거나, aeson 대신 simdjson을 사용하거나, 추가 검증 계층을 얹는 식입니다.
저는 타입 안정성, 순수성, 우아한 동시성 측면에서 Haskell이 프로그래머에게 제공하는 것에 대해 더 길게 이야기할 수도 있습니다. 하지만 요즘 대부분의 현대 언어도 그와 비슷한 기능을 많이 제공합니다. 그중 일부는 Haskell 자체에서 영감을 받은 것들이기도 합니다. 차이는 그 언어가 장려하는 엔지니어링 문화에 있습니다. Ian Duncan의 글이 이 영역을 잘 다루고 있으며, 제가 여기서 그것을 되풀이할 위험이 있습니다. 요지만 말하자면, 그의 일반적인 조언은 여전히 유효합니다. Haskell은 정확성을 중시하고 자신이 만드는 추상화를 신중하게 생각하는 이상주의자들을 끌어들입니다. Haskell 역할을 위한 채용은 리더들이 걱정하는 것만큼 문제가 된 적이 없습니다. 오히려 후보자들은 공급망 자체에는 특별한 관심이 없더라도 Haskell 그 자체 때문에 우리에게 왔습니다.
이제 잠깐 현실적인 이야기를 해봅시다. Haskell은 오랫동안 존재해 왔고, 군데군데 거친 부분이 있으며, 최신 GHC 릴리스를 따라가는 일이 정말 중요합니다. 팀 안에 전문가가 없다면, 그 거친 부분 하나만으로도 곪아 전체 프로젝트를 감염시키기 충분합니다. 초창기에는 우리는 광범위한 Haskell 경험을 가진 컨설턴트들이 있어 운이 좋았습니다. 그들은 언어 확장과 사용자 정의 연산자 난무에 압도되지 않도록 가드레일과 몇 가지 기본 규칙을 제시해 주었습니다.
그 시절에는 아직 GHC2021이나 GHC2024 언어 에디션이 없었기 때문에, 합리적인 기본 확장 집합은 각 팀이 손수 관리해야 했습니다. 그 기본선은 사실상 복잡성 예산이었습니다. 즉, 신규 입사자가 기여하기 전에 흡수해야 하는 언어적 복잡성의 상한선이었습니다. 그런데 이것을 강제하는 일은 결국 지는 게임이라는 것이 드러났습니다. 왜냐하면 의존성이 여러분 대신 그 예산을 써버리기 때문입니다. servant나 haxl 같은 것을 들여오는 순간, 비즈니스가 보기에는 배관 설비에 불과한 부분에서 이미 예산 전체를 써버리게 됩니다. servant로 웹 요청을 처리한다는 것은 API를 타입 수준 DSL로 인코딩한다는 뜻이고, 여기에 여러분만의 조합자를 추가하려면 type families와 그에 딸린 난해한 오류 메시지들까지 끌어오게 됩니다. haxl로 동시 작업을 배치 처리한다는 것은 모든 데이터 소스를 결과 타입으로 인덱싱된 GADT로 모델링한다는 뜻입니다. 이런 설계는 우아하지만, 공짜는 아닙니다.
초보자가 숙련자가 되고 숙련자가 베테랑이 되어갈수록, 복잡성 예산이라는 인식은 점차 희미해집니다. 더 고급 기능들은 사용하고 싶어지고, 그것들이 채택될수록 쉬운 유지보수라는 비즈니스와의 약속은 약해집니다. 신규 입사자들은 이미 복잡한 비즈니스 도메인 위에 GADT, type families, generics, tagless final 인코딩, arrows, free 구조, higher-kinded data type 패턴, 그리고 그 밖의 여러 가지 기교를 이해해야 하게 됩니다. 우리가 처음부터 언어의 모든 고급 패턴과 기능을 사용하고 싶어 했던 것은 아니었습니다. 사실 그럴 수도 없었습니다. 그것들의 실용성은 엔지니어들이 생태계에 더 익숙해지면서 시간이 지나며 드러났습니다. 그리고 이런 점진적인 확장은 정말 모르는 사이에 여러분을 덮칠 수 있습니다.
"지루한" Haskell은 설교하기는 쉬워도 조직 차원에서 강제하기는 어렵습니다. 우리 엔지니어들은, 음, 지루해지고, 그럴 필요가 없는 문제에까지 GHC 기능을 활용하고 싶은 유혹을 자주 받습니다. 여기서 핵심은 Ian의 글을 의도적으로 되풀이하는 것이기도 한데, 고급 Haskell을 합리적인 인터페이스 뒤에 캡슐화하면 폭발 반경을 줄일 수 있고, 복잡성 예산을 소수 전문가가 유지하는 작은 라이브러리 범위로 한정할 수 있다는 점입니다. 언어 수준에서 복잡성이 폭증할 위험은 결코 Haskell만의 문제는 아니지만, 그렇게 많은 밧줄을 제공하는 언어는 많지 않습니다. 비즈니스는 복잡성을 코드 줄 수나 사용된 토큰 수로 측정할 수 있겠지만, Haskell 프로젝트에 더 나은 척도는 다음 GHC 업그레이드 때 얼마나 많은 부분이 깨질 것인가입니다.
냉정한 진실 하나는 모든 팀이 똑같이 만들어지지 않았다는 것입니다. Haskell은 어떤 팀에게는 힘을 실어주지만, 다른 팀에게는 힘을 빼앗을 수도 있습니다. 이것이 사실이 아니었다면, 오늘날 우리에게 지금보다 더 많은 Haskell 팀이 있었을 것입니다. 저는 Haskell이 두 개의 별도 프로젝트에서 자리 잡지 못하는 모습을 지켜봐야 했고, 그 이유는 겉보기에는 서로 관련이 없어 보였습니다. 한 경우에는 심각한 space leak가 전체 프로그램을 멈춰 세웠습니다. 그 팀은 Haskell 경험은 전혀 없지만 숙련된 개발자들로 구성되어 있었고, 형제 팀에서 복사해 온 프로젝트 템플릿을 사용하고 있었습니다. 그들은 단지 물려받은 관례를 따르기를 바랐을 뿐이었지만, 이들을 이끌어줄 강한 Haskell 개발자가 없었던 탓에 그 space leak 하나로 전체 시도가 무너졌습니다. 다른 경우에는 Haskell을 주장하고 애플리케이션을 구축했던 엔지니어들이 다른 프로젝트로 이동해 버렸습니다. 우선순위가 바뀌었을 뿐이고, 언어 자체와는 무관했습니다.
두 경우 모두 언제나 대체 수단이 있었습니다. 엔지니어들은 Haskell 이전에 자신들이 쓰던 것으로 다시 돌아갔고, Haskell이 자신들을 더 생산적으로 만들어 준다는 확신을 갖지 못했습니다. 이것은 Haskell을 단지 목적을 위한 또 하나의 수단, 그저 또 하나의 프로그래밍 언어로 보는 데서 오는 결과입니다. 엔지니어들은 언어를 충분히 배웠습니다. 다만 그것에 대한 열정을 끝내 발견하지 못했습니다. 결국, 다소 감상적으로 들릴지 모르지만, 이것이 엔터프라이즈에서 성공적인 Haskell 프로젝트를 가르는 차이점입니다. Spring Boot 템플릿으로 빠져나갈 탈출구가 있다면 아무도 space leak를 진단하려는 동기를 갖지 않을 것이고, 이제는 LLM이 전체 코드베이스를 기계적으로 번역할 수 있게 되면서 그 탈출구는 어느 때보다 넓어졌습니다. 아무리 영리한 코드베이스라도 다시 쓰지 못할 정도는 아니므로, 유일한 진짜 방어책은 Haskell을 계속 유지하고 싶어 하는 팀을 만드는 것입니다.
Haskell을 적극적으로 받아들인 팀들 안에서도, 우리가 우리의 선택을 되돌아보게 만들 만큼 아찔한 순간들이 있었습니다. 우리는 사용자 정의 참조 카운팅 로직에서 비롯된 double-free 오류를 겪었고, 한 프로젝트에서는 걷잡을 수 없는 space leak를 고친 유일한 방법이었기 때문에 병렬 가비지 컬렉션을 비활성화하기도 했습니다. 최악의 경우, 무한 루프는 가장 무서운 버그 중 하나입니다. 단순히 찾기가 어렵기 때문입니다.
GHC 런타임 자체가 우리에게 프로덕션 장애의 근본 원인이었던 적은 한 번도 없습니다. 안정 상태에서 그 처리량과 메모리 사용량은 우리 워크로드에 대해 충분히 경쟁력이 있었고, 위의 전쟁담은 일상이라기보다 예외에 가까웠습니다. 여기서 중요한 점은 엔지니어들이 세그멘테이션 오류 코어 덤프, 가비지 컬렉션 메트릭, 힙 프로파일을 추적해 이런 문제들을 해결할 수 있었다는 것입니다. 여기서 얻은 결정적인 교훈은 강력한 타입 시스템을 이유로 테스트 커버리지를 희생해서는 안 된다는 것입니다. 오히려 저는 Haskell 애플리케이션이 chaos engineering의 최적 후보라고까지 생각합니다. 사소한 오류의 전체 범주가 컴파일 단계에서 제거되기 때문에, 인프라 장애를 주입하면 동적 타입 프로그램에서 지배적일 잡음 대신 시스템의 진짜 로직 실패와 자원 실패가 드러나기 때문입니다.
우리가 시스템을 만들기 시작했을 때는 GHC 8.2를 사용하고 있었습니다. 인기 있는 플랫폼에 대한 API 클라이언트와 SDK 대부분을 직접 작성해야 했습니다. Haskell language server는 존재하지 않았습니다. GHCup은 막 등장한 상태였습니다. 런타임 프로파일링은 비전문가에게는 거의 암흑 기술에 가까웠습니다. 그 시절에 저에게 Haskell이 속도를 높여 줄 것이냐고 물었다면, 저는 몇 가지 단서를 달았을 것입니다. 비즈니스는 모든 것을 기성품으로 원하고, 바퀴를 다시 만드는 데 돈을 쓰고 싶어 하지 않으며, 우리는 필요한 것을 직접 만드는 데 자원을 써야 했습니다. Java IDE에 익숙한 개발자들은 Haskell 도구 환경의 상태를 보고 질색했습니다. H-E-B는 심지어 Haskell용 Debug Adapter Protocol 지원 개발을 후원하기까지 했습니다. VS Code 사용자를 위해서 말입니다.
하지만 이제 GHC와 생태계 전반에서 수년간의 고된 노력 끝에, 프로덕션급 Haskell 애플리케이션 구축은 단지 가능하다는 수준을 넘어 진정으로 경쟁력 있는 선택이 되었습니다.
도구 측면에서 우리는 HLS, ghcid, ghciwatch, static-ls, tricorder, 그리고 Haskell을 위한 일급 코드 편집기 플러그인을 갖추게 되었습니다. H-E-B의 우리 중 일부는 더 풍부한 GHCi 중심 개발을 돕기 위해 GHC 플러그인을 사용하고, 또 다른 일부는 VS Code와 HLS를 사용합니다. 우리는 개발자들이 선호하는 도구를 사용할 자유를 소중히 여깁니다.
가시성 측면에서는 이제 hs-opentelemetry가 있고, 우리는 Haskell 함수에 OTel span을 자동으로 계측해 주는 GHC 플러그인 opentelemetry-auto를 사용합니다. 비즈니스가 계측 환경 때문에 Java나 Python을 선호한다고 말한다면, 우리는 이것을 가리키며 Haskell에도 같은 이야기가 있다고 말할 수 있습니다.
라이브러리 진열장도 훨씬 풍성해졌습니다. 우리 서비스는 warp, aeson, attoparsec, stm, conduit, servant, 그리고 orville-postgresql 같은 주력 라이브러리 위에서 돌아갑니다. 빈틈이 보이면 우리는 직접 작성합니다. 예를 들어 제가 유지보수하는 트랜잭션 기반 PostgreSQL 작업 큐 arbiter는 H-E-B에서의 작업에서 탄생했고, 오늘날 프로덕션에서 운영되고 있습니다.
이 모든 것은 누군가가 "이 애플리케이션은 Haskell을 사용합니다"라고 말할 때 개발자와 운영 담당자 모두 더 이상 몸서리칠 필요가 없다는 뜻입니다. GHC가 자체 런타임 진단, 컴파일러 성능, 모니터링 기능(예: eventlog streaming), 그리고 예외 주석을 개선해 나가면서 상황은 계속 더 좋아지고 있습니다. 참고로, GHC 9.2에 info table profiling이 도입된 이후로 저는 더 이상 space leak를 두려워하지 않습니다.
우리 조직에서는 내부 라이브러리가 매우 중요하며, 우리는 개발자들이 내부 프로젝트와 오픈소스 프로젝트 모두에 참여하도록 장려합니다. 우리는 수년 동안 격주로 Haskell 밋업을 운영해 왔고, 회사 전반의 관심을 끌어보려 노력했으며, 때로는 성공했습니다. 이 밋업은 엔지니어들이 자신의 Haskell 탐구 결과를 공유하고 다른 이들을 가르치는 공개 포럼 역할을 합니다. 엔터프라이즈에는 사일로가 어디에나 있기 때문에, 아이디어와 패턴의 교차 수분은 중요합니다. 이것은 bus-factor 위험에 대한 우리의 답이기도 합니다. 더 많은 엔지니어가 코드베이스를 읽고 소유할 수 있을수록, 누군가 한 명의 이탈이 그것을 침몰시킬 가능성은 줄어듭니다.
어떻게 하면 개발자에게 힘을 실어주면서 동시에 지루한 Haskell을 옹호할 수 있을까요? 물론 우리는 몇 가지 느슨한 기본 규칙을 세우지만, 복잡성 예산을 어떻게 쓸지는 기술 리드가 최종 결정을 내립니다. 어떤 팀이 저장 시 암호화된 PII를 저장해야 한다면, 민감한 데이터 유출을 런타임 사고가 아니라 컴파일 오류로 만들 수 있는 타입 수준 보장을 추가하는 것이 아마도 충분히 가치 있을 것입니다. 그것이야말로 보안 검토가 실제로 신경 쓰는 종류의 보장이기 때문입니다. 반대로 어떤 팀이 반복 코드를 줄이고 trees-that-grow 패턴을 사용해 네 개의 API 계약을 통합하고 싶어 한다면, 공개 스키마를 type families로 흐리게 만드는 일은 그만한 가치가 없을 수도 있습니다.
우리에게 지루한 Haskell은 타입 수준 프로그래밍이나 고급 GHC 기능을 배제하는 뜻이 아닙니다. 오히려 원칙 있는 관례를 세우고, 그것에서 벗어나는 비용을 따져본다는 의미에 가깝습니다. 큰 원칙 중 하나는 "멀리서 일어나는 유령 같은 작용은 없다"는 것입니다. 예를 들어 TemplateHaskell 코드 생성도 없고, Generic 유도 직렬화 로직도 없습니다. 이 경우의 관례는 반복 코드를 직접 쓰고 모든 것을 프로그래머에게 명시적으로 남겨두는 것입니다. 듣기만큼 엄격한 것은 아닙니다. quasiquoter나 Generically를 통한 deriving을 원하는 팀이 완전히 금지되는 것은 아니기 때문입니다. 우리는 어떤 엔지니어가 웹 폼을 위한 타입 수준 상태 기계를 구현하거나, 일반 ADT로 충분한 상황에서 GADT를 집어 들 때 건강한 회의감을 갖길 원합니다. 엔지니어는 자신의 복잡성이 필수적이라고 주장할 수 있습니다. 하지만 그것이 우발적인 복잡성인지 알아차리려면 팀 전체와, 이견 제기가 환영받는 문화가 필요합니다.
그렇다고 해도 우리 프로젝트 전반에는 라이브러리와 프로덕션 애플리케이션 양쪽 모두에 고급 Haskell이 충분히 존재합니다. 제 생각에는 이것이 영원히 지루한 상태에 머물 필요는 없다는 점을 보여줍니다. 다만 놀이 시간을 허용하는 범위를 제한해야 할 뿐입니다. 우리는 일도 하고 놀이도 하고 싶어서 Haskell을 사용하며, 그 중간 어딘가에서 엔지니어링이 일어납니다.
저는 업계에서 Haskell과 GHC의 미래를 매우 낙관적으로 보고 있습니다. 오랜 기간 GHC를 사용하면서 우리는 몇 가지 버그를 발견하고 보고했지만, 그 어느 것도 우리에게 치명적이지는 않았습니다. 프로덕션 사고는 드물고, 실제로 발생하는 소수의 사고도 타입 오류나 런타임보다는 비즈니스 로직으로 거슬러 올라가는 경우가 훨씬 많습니다.
또한 이제 Haskell에 유리한 새로운 논거도 형성되고 있습니다. 코딩 에이전트가 이제 우리 코드의 점점 더 큰 비중을 작성하고 있으며, 이로 인해 병목은 코드 작성에서 코드 검증으로 이동하고 있습니다. 그리고 GHC 같은 컴파일러는 바로 그 작업에 잘 맞습니다. 인간에게 리팩터링을 쉽게 만들어 주는 바로 그 타입 시스템이 에이전트에게도 빡빡한 피드백 루프를 제공해, 환각으로 생성된 코드가 실행되기 전에 거부되도록 해줍니다.
우리는 Haskell의 성공 사례를 더 많이 필요로 하며, Haskell Foundation이 그것들을 세상에 드러내기 위해 노력하고 있다는 점에 감사하고 있습니다.