ハクソク

世界を動かす技術を、日本語で。

数百万行のHaskell:マーキュリーにおけるプロダクションエンジニアリング

概要

  • Mercury社で200万行規模のHaskellコードを運用する実例紹介
  • Haskellの型システムを活用した信頼性・知識伝達の工夫
  • **純粋性(purity)**を「境界」として捉える現場視点
  • 新人エンジニアでも運用可能な仕組み作り
  • 運用現場での教訓とベストプラクティスの共有

Mercuryにおける大規模Haskell運用の現実

  • Mercuryはフィンテック企業、30万以上の事業者にバンキングサービスを提供
  • 年間約2億4800億ドルの取引処理、従業員約1,500名体制
  • 全エンジニアの多くがHaskell未経験で入社、現場で習得
  • 約200万行のHaskellコードベースを運用、コメント等を除外した実数
  • 急成長・高頻度な人材入れ替え環境下での知識伝達・システム保守性の重要性

Haskellが大規模運用で機能する理由

  • APIに運用知識を組み込むことで、危険な操作を安全な境界内に封じ込め
  • 型システムを通じて「安全な道」を「簡単な道」にする設計
  • システムの理解容易性を重視し、新人でもコードの意図を把握可能に
  • SVB危機などの非常時にも、堅牢なシステム運用を実現
  • ドキュメントより型定義で知識を残すことで、異動・退職時の知識喪失を防止

信頼性への考え方

  • 従来の「失敗を防ぐ」思考から、「適応力を持たせる」へ転換
  • システムが変動を吸収し、段階的に劣化する設計を重視
  • 新人や短期間の在籍者が多い環境では、組織的知識の伝達が課題
  • 型システムを運用支援ツールと捉え、正しさ証明以上の価値を見出す
  • 安定性エンジニアリングチームによる事前の運用検討・リスク対策

純粋性(purity)は「境界」の設計指針

  • Haskellの**純粋性は「性質」ではなく「インターフェースが守る境界」**という現場観点
  • 内部で危険な操作(ミューテーション等)があっても、型で外部から遮断できれば許容
  • 例: runSTの型(rank-2型)による安全なスコープ管理
  • 危険な操作は閉じ込め、外部からの誤用を防ぐことが本質
  • 新人にとって「純粋性は維持すべき境界」という説明が実践的

「正しいこと」を楽にする仕組み

  • 大規模コードでは操作順序や手順の遵守が重要な場合が多い
    • 例: トランザクションごとに監査ログをflushする、通知をDBトランザクション内でenqueueする等
  • これらの「作法」はドキュメントや口伝、Slackスレッド等に埋もれやすい
  • Haskellの型で運用上の作法や制約を強制できる
    • 例: 必須ステップを型で表現し、忘れや誤用を防止
  • 組織の知識半減期が短い中、型による知識の永続化が有効

まとめ

  • MercuryではHaskellを大規模・高頻度変化の現場で運用し、成功体験を積み重ね
  • 型システム・純粋性の境界設計・知識伝達が、システムの信頼性と運用容易性に直結
  • 現場で役立つHaskellの実践知を、今後も発信予定

Hackerたちの意見

一般的な考えとは逆かもしれないけど、マーキュリーがハスケルを選んだことや、初期のリーダーたちの豊富な経験が、彼らの成功にかなりの影響を与えたと思う。マーキュリーの顧客として、彼らは本当に私のツールキットに欠かせない会社で、ハスケルを選んだことで、彼らの進展や開発、全体の旅がより良くなったと感じざるを得ない。もちろん、ほとんどの言語で同じことが言えるけど、FP言語のハスケルが成功のレシピだとは言わない。でも、「バイブコーディング」やLLMの時代の前にこの意図的な選択をしたのは特に先見の明があったと思うし、投稿で詳しく説明されている彼らのエンジニアリング文化とも相まっている。
一般的な経験がない人を雇うことが実際に役立ったと思うよ。新しいメンバーに自分たちの文化やスタイルをゼロから植え付けられたからね。事前に雰囲気を作る前は、ほとんどの人が何の指示もなくいきなり作業に飛び込むことなんてしたくなかっただろうし。
彼らのアプリは本当にすべてがうまくいくって感じがする。ほかのサービスから来た身としては、すごく満足感があるよ!
キャリアの初期にハスケルの仕事を見つけて、それに専念するのが大事だと思う。入り込むのが本当に難しいから、経験がないと、ハスケルの経験がある他の人たちと競争しなきゃいけないし。仕事は珍しいから、もしうまくいかなくなったら(会社が働きにくくなったり、レイオフされたり)、身動きが取れなくなるかも(まあ、RustやScala、F#に切り替えるかもしれないけど)。
多くのハスケル開発者を採用してきた者として言えるのは、ハスケルの経験が多いことが必ずしもプラスとは限らないってこと。実際にものを作りたいと思っている開発者を確保するために、慎重にフィルタリングしなきゃいけない。ハスケルに後から来たけど、実際にものを作る経験が豊富な人を採用したい。ハスケルの経験が豊富でも、あまり成果を出していない人は大きなリスクだと思う。
特にHaskellのような言語や、Haskellを本当に愛している人を雇うことに対しての不安があるんだ。ツールを問題よりも重視するような特定の性格の人が集まるリスクがあるから。自分もそういう人だったし、そういう人と一緒に働いたこともあるし、そういう人の犠牲になったこともある。彼らは言語XやフレームワークYが大好きで、目の前の問題がそれを使うことで解決できると信じている。今や彼らはハンマーを持っていて、それで釘を探し回っている。Haskellを使っている職場にいたこともあるけど、まあ…普通だったかな? 書くのが好きな人にはいいかもしれないけど、個人的には他のFP言語の方が好きだな。そういうオタクなことが好きで、Lambda the Ultimateとかによく出入りしてたけど、Haskellや他のツールに特別な力があるとは思わない。そういうアプローチで何度も痛い目にあったから。
これ、私にもあった。Haskellで10年間で全ての収入を得た。数百万。全てのものをそれで買った。最初はLinkedInのリクルーターから始まったんだ。
200万行のハスケルが何をしているのか、想像するのが難しい。すごい量のコードだし、ハスケルは「タイト」っていう印象があるから、少ないコードでたくさんのことができるんじゃないかな。もしかしたら、jsonのシリアライズやデシリアライズ、REST APIフレームワーク、ログ記録などをするためのライブラリがたくさんあるのかも?
TFAから: > 問題は、我々が計測できないコードを信頼できないことだ。もしサードパーティのバインディングが具体的な関数を通じてHTTP呼び出しを行うなら、トレーシングを追加する方法も、SLOに合わせたタイムアウトを注入する方法も、テストでパートナーの障害をシミュレートする方法も、トレースの400msのギャップを説明する方法もない。だから、自分たちで書く。最初は手間がかかるけど、我々が書くクライアントは構造上観測可能だから、最初からそのように作った。
ニット: あなたが「タイト」と呼ぶ言語の質は、通常「表現力豊か」と呼ばれる。少ない文字で比較的抽象的なアイデアを表現できる。これを「高レベル」と呼ぶ人もいる。ただ、200万行のコードは、一見思えるほど多くはないと思う。特に金融のような高度に規制された分野の会社にとっては、数年の進展を考えると。
コードベースがどうなってるかは分からないけど、a) Haskellの簡潔さの評判は、学術的なサークルでの過剰な代表性から来てる部分があるんだよね。そこで「St M -> C T」みたいなことを言うのは普通なんだけど、実際のソフトウェアでは「TransactionState Debit -> Verified Transaction」みたいに言う方がずっと役立つよね。b) Haskellの簡潔さの評判のもう一つの要因は文化的なもので、LISPにさかのぼるものがある:人々が難解なトリックやマクロを使って行数を節約しようとしすぎること。たぶん、Mercuryみたいな金融系の会社では、明瞭さや可読性を重視して、そういうのは避けられてると思う。例えば、リントがモナディックなものを堅苦しいマルチラインのdo式に分けるように強制するかもしれないし、>>や>>=で一行で書けるのに。
> Haskellは「タイト」 これは客観的な指標じゃないけど、Haskellは単に違う「アスペクト比」を持ってると思う。行数は少し低いかもしれないけど、単語数はより命令的なOOP言語とほぼ同じだよ。
「ハスケルは、これらの呪文を型でエンコードするためのツールを提供してくれる。これは、私の考えでは、言語が生産エンジニアリング組織に提供する最も価値のあるものだ。ハスケルは確かに、これを行うために最も強力で広く(あるいはやや広く)使われている言語だが、この一般的なパターンはRustやTypeScriptでもうまく機能するし、より良いコードを書くための私のお気に入りのツールの一つだ。ユーザー -> ログインユーザー -> アクセス制御されたログインユーザーのようなことをするのも好きで、ウェブアプリケーションで人々が何度も犯す明らかなAuthZバグを防ぐことができる。このパターンは業界で大きく活用されていないと感じている。」
これはRustやTypeScriptに特有のことじゃない。基本的にどんな言語でもできるよ。セキュリティ上の理由でエスケープされていない文字列とエスケープされた文字列を区別しなきゃいけないと想像してみて。動的型付けの言語でも、エスケープされた文字列をEscapedクラスとして保持できるし、escape(str)->EscapedやdangerouslyAssumeEscaped(str)->Escapedの関数(または静的メソッド)を使える。これにはパフォーマンスコストがあるから、そのトレードオフを考慮しなきゃいけないけど、可能ではあるよ。別の方法としてはApplication Hungarianがあるけど、これはプログラマーに依存する部分が多いね。
> RustやTypeScriptでもうまく機能する もちろん、RustとTypeScriptはHaskellの影響を大いに受けてるよね…ただ、あんまり言及せずに違う名前を使って、「モナドは怖い、チュートリアルを書かなきゃ」みたいな効果を避けてる。モナドの話よりも、型クラスみたいなことの方が重要なんだけどね。模倣は最も誠実な賛辞だよ。
ハスケルをよく使っているけど、クロスコンパイルがすごく難しいことに気づいた。クロスコンパイルが簡単になれば、チップMacで開発してx64/AMDのLinuxサーバーにデプロイできるのに。>ハスケルのバイナリを静的にリンクするのはかなりの挑戦だし、>ビルド要件がプロセスを遅くする。依存関係をキャッシュして、変更されていないものを再コンパイルしないようにするためにDockerを使わなきゃいけないけど、それでも遅いし、大きなバイナリが出てしまう。さらに、Dockerベースのデプロイは、各モジュールを再コンパイルする必要があるから、時間がかかる。部分的にキャッシュできるけど、それでも遅い。一方、Goだと全然苦にならない。私だけじゃなくて、他にも同じ問題を抱えている人がいるよね:https://news.ycombinator.com/item?id=47957624#47972671 ハスケルは美しくてパフォーマンスの高い言語なのに、ビルドが遅いのは残念だ。
親友がこの会社で働いていて、外から見ると良いエンジニアリング文化があるみたい。Haskellがその仕事に合ったツールだと思うし、彼らはその強みを活かしているけど、成功の一因は単に会社がうまく運営されているからじゃないかとも思う。
それは、機能型プログラミング言語を使うことで質の高い労働力や応募者をフィルタリングするという人気のある(真実かどうかは別として)考えに反することにはならないよね。
数年間Haskellで働くのは楽しかった。積極的に探していたわけじゃないけど、チャンスが偶然舞い込んできたんだ。刺激的で頭を使う仕事だった。でも残念なことに、HaskellよりもRustの方が生産性が倍は高い。Haskellだけを3年間やってもね。Haskellには避けるべき落とし穴が多いから、それを知っておかなきゃいけない。時には書き手によっては書き込み専用になってしまうこともあって、消化するのが難しいことがある。ツールはよくNixと結びついていて、これもまた複雑なものだし。言語拡張があちこちに散らばっている感じもする。Cabalファイルはあまり好きじゃないし、コンパイラのエラーにも慣れるまで時間がかかるんだ。
生産性は全体的に2倍なの?それともRustだとあまり生産的じゃない部分もあるの?それと、「書き込み専用」ってどういう意味?
意外だな~、自分は全く逆の経験をしたよ。前のプロダクトでは、クラッシュに疲れたからバックエンドをTypeScriptからRustに切り替えることにしたんだけど、それが今までで一番の技術的なミスだったと思ってる。生産性が大幅に落ちちゃったからね。Rustでしか起こらない時間を浪費する問題を二つシェアするよ:(1) 高階関数を書くこと(例えば、データベース接続を開いて、何かをして、閉じる関数ね。RAIIを使えるってのは分かってるけど)、HaskellやTypeScript、JavaScript、C++、PHPでは簡単なのに、Rustでは[友達のRustエキスパートに助けを求めても]全然無理で、結局諦めることにした。マクロを書くのが時々うまくいくこともあったけどね。(2) リファクタリングを試みて、丸一日型エラーを直して、やっとトップレベルのファイルにたどり着いたら、基本的な設計のどこかで起こった型エラーが原因で、結局リファクタリングが不可能だと分かって、全てを元に戻さなきゃいけなかったことが何度もあった。さらに、Rustは現代の言語の中で、具体的な型ではなくインターフェースを使うことが、やることによっては高度なことから不可能なことの間にある唯一の言語だと思う。アプリケーションコード(システムやライブラリコードとは違って)は、最初の近似として、Rustで書くべきじゃないと結論づけたよ。
自分はターゲット市場じゃないと思う(小さなCUに満足してるし)、彼らの看板を見ると絶対に使いたくなくなるんだけど、でもこの投稿や文化、Haskellを使ってるってことを見て、ちょっと考えが変わってきた。
BellroyのHaskellの成功事例が、今後のメルボルンComposeミーティングのテーマになるよ:https://luma.com/uhdgct1v
> [ライブラリの著者へ] 明らかに、速いペースで動くプロダクションチームが意味する「責任者」はいないから、壊れる変更を行うことに対する理解できるためらいが生まれるんだよね。経験がこれらのシステムを設計するより良い方法を教えてくれても。 > これはボランティアのメンテナーに対する不満ではないよ。これは小さなエコシステムで真剣なシステムを構築する際の周囲のリスクの一つなんだ。だから、すでにドメインの専門知識を持っていて、自分のコードベースを知っているライブラリの著者にお金を払う代わりに、彼らはそれをゼロから書き直すことを選んだんだ。クラシックだね。
これで、自分が必要とする方向でライブラリを開発できるし、それをやってくれる人も雇ってるから、リスク管理としては良さそうだね。