エンジニアはLedger構築でスタートアップ的なミスをしてはならない
(news.alvaroduran.com)- 実際のお金を扱うフィンテックでは、数セントの差でもユーザーの信頼を損ない、単式入力で取引を記録していた株式取引スタートアップは dancing cents 問題に直面した
- 単式入力の元帳はお金の入出金しか残さないため、為替、ブローカーの rounding to even、FINRA TAF手数料のように差異を生んだ原因を追跡しにくい
- 複式簿記の元帳は、お金は常にある口座から別の口座へ移動すると捉え、Accounts、Entries、Transactions を分離して、出所と行き先をあわせて記録する
- Entries の pending、discarded、posted 状態と、Transactions の投稿条件を明確にしておけば、部分的な失敗や compensating Entries をより安全に扱える
- Ledger は会計レポートのためのインターフェースであると同時に、お金の整合性を守る system of record でもあるため、拡張の過程で可用性と強い整合性のあいだの緊張が大きくなる
数セントの差がユーザーの信頼を壊した
- 株式取引プラットフォームを作っていたスタートアップは、「make it work, make it right, make it fast」の原則に従い、最初から 複式簿記システム を作らなかった
- リリース直後、ベンダーが認識している金額と内部システムが認識している金額が数セントずつずれていた
- 内部ではこれを dancing cents と呼んでいた
- ユーザーは Apple 株を5ドル分買ったのに、注文が4.98ドルと表示されると、すぐにカスタマーサポートへ問い合わせた
- 問題の本質は損失額よりも 信頼と成長 にあった
- 怒ったユーザーはサービスを勧めず、スタートアップの成長が阻害された
- CEO は誤った取引が発生したら、カスタマーサポートが数セントを手動で補償するよう指示した
- その対応のために Slack ボットまで作られた
お金は現在の残高ではなく未来の価値まで追跡する
- Ledger は お金を追跡するシステム である
- お金は単なる現在残高ではなく、将来受け取る価値や支払う価値を表すときにも必要になる
- 概念的には、お金は 未来の資産 である
- 「ユーザーが5ドル払った」「ユーザーが6ドル払った」のように入出金だけを記録しても、実際の金融フローを説明するには不十分である
- 銀行振込はインターネットの基準では遅く、多くの銀行は振込を 翌営業日 に決済する
- 決済が完了すれば、いずれお金を受け取れるという確信は得られる
- しかし株式は今すぐブローカー経由で買わなければならない
- 数日後に決済される pending 金額と、即座にブローカーへ出ていく金額を同時に表現する必要がある
- 単式入力方式では、エラーが起きたときのロールバックが非常に難しく、一部のコーナーケースではロールバックを試みることすらできない
単式入力の元帳がデバッグを妨げる理由
- 単式入力の元帳は資金の流れを示せても、その流れが なぜ発生したのか までは説明できない
- 特定の資金移動の原因を突き止めるには、複数のモデルのデータをつなぎ合わせる必要があり、場合によってはそれすら不可能だった
- 複式簿記の元帳は、何が起きたのかと、なぜ起きたのかを一緒に残す
- すべての資金移動は、ある口座から別の口座へ発生する
- 各セントがどの口座から出て、どの口座へ行ったのかが記録される
- dancing cents 問題は単式入力システムでは解決が難しかった
- 消えた数セントが為替によるものなのか判断しにくい
- ブローカーの rounding to even mechanism が原因かもしれない
- 1日の終わりに徴収される FINRA TAF fees が原因かもしれない
- システムがどう動いているのか理解できなければ、バグをなくすことも難しい
Ledger データモデル: Accounts, Entries, Transactions
- 多くのエンジニアは、最初にお金を追跡するとき、ドメインモデルの中に金額を入れる
- Order に price 属性を持たせたり、expenses テーブルに amount カラムを置いたりするやり方である
- これは balance as property アプローチである
- この方式は初期には素早く動くが、時間が経つとレポーティングが複雑で遅くなり、決済処理や分析も難しくなる
- 夜間レポート処理に何時間もかかるなら、このアプローチが根本原因かもしれない
- Ledger は、システムのすべての金融取引を導出できる 別個のデータモデル として扱うほうがよい
- 3つのエンティティが基本構造を成す
- Accounts: 価値のバケットであり、時間の経過とともに価値がどう変化するかを見る観点
- Entries: 口座間の資金フローであり、常に価値の交換を表す
- Transactions: Entries が正しくペアになり、意図どおり処理されることを保証する単位
Entries の状態と不変性
- Entries は pending、discarded、posted の3つの状態を持てる
- Entry は常に pending 状態で作成される
- 交換された価値
- 方向である credit または debit
- 参照する account 情報
- 金額の方向を正数と負数で表現するのはよくあるミスである
- Entries は基本的に不変だが、pending Entry は posted Entry を作るために discarded されることがある
- pending Entry を巻き戻すために reversal Entry を作る代替案もある
- しかし reversal Entry 方式は口座履歴を煩雑にすることがある
- discarded 状態を使えば、現在の Entries を見るとき
discarded_atが設定された項目を除外すればよく、履歴も失われない
- 複式簿記システムでは、discarded されていない credit Entries の総額と、discarded されていない debit Entries の総額は等しい
- 概念的には、お金をポケットの中でどう動かしても総額は同じ、という意味である
- 外部世界を表し、Profit and Loss statement に合算される一部の特殊な口座は、例外的にバランスしないことがある
Transactions と部分失敗の処理
- Entries は対で生成され、Transactions はその過程が意図どおり進むことを保証する
- Transaction は、関連する Entries が posted または discarded 状態であり、posted Entries に置き換えられた場合にのみ posted される
- 部分的に失敗した Transaction は compensating Entries によって意味的に巻き戻せる
- このアプローチは Saga pattern と相性がよい
- Saga はアトミック性を可用性と引き換えにする
- 複数テーブルをロックする遅いトランザクションの代わりに、より小さな個別作業と中間チェックポイントでプロセスを分割する
- その間に他のトランザクションが処理できるため、スループットが向上する
Accounts と normal balance
- 単一の Account の観点では、Ledger は単式入力システムのように見える
- 1つの Account は複数の Entries と1対多の関係を持つ
- 総残高は、関連する Entries の個別残高を集計した値と一致しなければならない
- Account の normal balance によって、総額の計算方法は変わる
- Entry 金額に正負の符号を結び付けるのは、会計上は避けるべきである
- ある口座は net credit が正常であり、別の口座は net debit が正常である
- たとえば銀行の現金口座は net debit が正常でも、当座貸越になれば負になることがある
- normal credit balance とは、関連する credit Entries の総額が debit Entries の総額より大きい状態が正常であることを意味する
- normal debit balance はその逆である
会計システムとエンジニアリングシステムの緊張
- Ledger には、異なる要求を持つ2つのシステムが共存している
- Accounting system: 外部から見た Ledger のインターフェース
- Engineering system: Ledger が自分自身をどう見るかという実装
- Accounting system は複数の観点で集計されたデータを公開する
- Reporting
- Financial ratios
- Business Intelligence
- Engineering system はデータの整合性と正確性を保証しなければならない
- フィンテック企業において Ledger は、営業チームの CRM のような source of truth の役割を果たす
- Ledger の拡張が難しい理由は、2つのシステムの要求が互いに異なるためである
- Accounting system は高い可用性と低レイテンシを要求する
- Engineering system は強い整合性と schema-on-write の検証を要求する
開発者が参考にできる会計資料
- An Engineer’s Guide to Double-Entry Bookkeeping: 基本的な Python コードとともに複式簿記を説明している
- Double Entry Accounting For Developers: Django Hordak の開発者向け複式簿記の説明
- Modern Treasury ledger series part I: Ledger の拡張に関する全6回シリーズの第1回
- Beancount, Martin Kleppmann, Modern Treasury Accounting for Developers: 異なる角度から会計を説明する開発者向け資料
- Peter Selinger accounting tutorial: さらに深く学べるチュートリアル
- Uber、Square、Airbnb もそれぞれのシステムで複式簿記 Ledger を実装した方法を公開している
1件のコメント
Hacker Newsのコメント
それをSynapseの顧客にも言ってみてほしい。数百万ドルが消えた
銀行は資金がどこへ行ったのかを厳格なルールに従って帳尻合わせしなければならないが、フィンテックは通常、顧客資金をまとめて置いた1つまたは少数の基盤となるFBO口座の上に独自の台帳を載せ、各顧客の残高を追跡している。Synapseの場合、独自台帳上の顧客残高の合計が実際のFBO口座残高よりはるかに大きかった
多くの人は詐欺を疑っているが、私は単にひどくてバグだらけの台帳だった可能性に賭ける。中身を見たら、フィンテックの預金口座には二度と金を入れたくないし、本物の銀行を使うほうがましだ。フィンテックが預金はFDIC保険の対象だと宣伝していても、それは基盤銀行が破綻したときにしか保護されず、フィンテックが自分の金を追跡できなくなった場合は守ってくれない
参考: https://www.forbes.com/sites/zennonkapron/2024/11/08/what-th...
Andreessen Horowitzが投資していたが、彼らはあらゆる政府規制に対して焦土作戦を仕掛ける側だ
https://finance.yahoo.com/personal-finance/synapse-bankruptc...
その前からコードベースがあまりにひどく、こうしたことをまともに追跡できないだろうと予想していたし、すべてが魔法のように回っていると信じていた上司とも衝突していた。数日後、会計不一致に関する監査が始まるというメールを受け取った
JPMCは内部でキャッシュフローを一貫して管理するために暗号資産の利用を提案していたが、実際にどこまで進んだのかは分からない
https://lex.substack.com/p/podcast-what-really-happened-at-s...
しばらく問い詰めたが、きちんと認めもせず、直してもくれなかった。根拠もなく内部の巧妙な窃盗かもしれないと疑ったが、無能のほうがもっとありそうな説明だと思う
BoAでシステム管理者として働いていた友人は、特定のログを7年間保管しなければならなかったが、ディスクがいっぱいになり始めると普通に削除していたと言っていた
Googleで働き始めたとき、理解するのに時間がかかったことの1つが、スケーラビリティのために信頼性や正確性をトレードオフすることだった
それ以前は課金システムや小規模なOLTP Webアプリケーションを作っていて、許容可能なデータ損失やゼロでないエラー率といった問い自体を考えたことがなかった。毎秒数百万件のリクエストを処理すれば一部は失敗するという事実よりも、エンジニアリングの姿勢の違いのほうが衝撃だった
まさに今この瞬間にもGmailを開いて正しく読み込まれなかったり500エラーを受け取ったりした人が数千人はいるかもしれない。誰もその原因を追跡しない、ユーザーはリロードしてそのまま一日を続けるからだ。逆にストレージ耐久性が年間99.99999%という印象的な数値でも、顧客が20億人いれば200人は本当に最悪の一日を過ごすことになる
ログ内のすべてのエラーを調査していた習慣から、常に何かが少しずつ壊れていて、何かを直す前にまずコストを考えるやり方へ移るのは、かなり衝撃的な転換だった
同程度の規模で私がよく見た経験則は、インフラ全体で100年に1件のデータ損失を目標にすることだ。通常、コストは10%も増えないことが多いが、データ配置アルゴリズムを設計する際に組合せ論を理解している人が必要になる
この分野を理解したいなら、copyset論文がよい出発点だ
解決策を発表しながらこのエラー率を伝えたところ、その20万人のAndroidユーザーはどうやって復旧できるのかと聞かれた。復旧方法はなく、連絡先同期がただ壊れるだけだったので、設計をやり直せと言われた
数字そのものが身を引き締めるものだった。99.99%で十分な領域があるのは確かだが、同じくらいそれでは足りない領域も多い
こういう仕事では、最初から適任者を採ることが助けになる。LeetCode の達人を大量に採っておいて、データ構造やアルゴリズムを頭の中で思い浮かべる能力だけでなく、実際に作りたいものを作れるかを問わなければならない
人々がその対象をどう作るべきか分かっているなら、成長を犠牲にする必要はなく、最初から正しく作られる
ときには会計、金融、生物学のような別の教育背景を持つエンジニアが必要になる。キャリアで最も重要だったのは、自分が作る対象の産業を深く理解し、本当に重要な問いを投げかけられるその分野の専門家を知ることだった。それが問題解決でありエンジニアリングで、残りはプログラミング/コーディングだ
キャリアの大半を、技術と金融が重なる領域、主に売上税・使用税コンプライアンスの分野で過ごしてきた。その過程で、会計士、コントローラー、弁護士たちが自分にどれほど影響を与えていたかを十分に理解していなかった
最近、かなり古いスタートアップの元帳システムについて助言したのだが、金融や会計の背景がないエンジニアが会計システムを作るとどうなるかを見て愕然とした
魔法のような会計士兼エンジニアを見つける必要はない。設計プロセスで実際の会計士をエンジニアリングチームの隣に座らせるだけで十分だ。全体の刷新設計を終えたあと、CPAの友人に完全レビューを任せたところ、いくつかのシナリオで穴を見つけたが、おおむね問題なかった
お金は難しいエンジニアリング問題だ。お金には、その周囲を取り巻く人間のあらゆる妙な振る舞いが付いてくるからだ
エンジニアリング・リーダーシップにおいてドメイン知識が重要だという点を再確認させられる。金融会社で働くなら、正しい技術的判断やトレードオフのために金融をある程度理解している必要があるし、ジャーナリズムや商取引でも同じだ
自分が働いた成功していた組織は、常に技術チームの面接にドメイン特化の非技術的な質問を含めていた。逆に、技術的には非常に優秀なチームの中にも、ドメイン洞察が不足して苦戦していたところがあった
どこを見ても、会計の経歴と比較的短いソフトウェアエンジニア経験を持つ自分より、ドメイン知識はまったくなくてもソフトウェアエンジニア経験が2倍ある人のほうが、はるかに好まれているように見える。これを効果的に活用する方法があるのか気になる
金融も技術的だし、機械工学も技術的で、スポーツ経営や社会学にも大きな技術的要素がある。技術的能力とは何かを広く捉えれば、複数のドメインで協働するのに必要な謙虚さが生まれる
要件が正しいか、作られた製品がその要件を満たしているかを、エンジニアリングと一緒に確認するのがPMの仕事だ。アジャイル環境では、そうした会話と検証が各スプリントごとに行われるので、何かがあまりに長く見逃されたまま通ってしまうのは難しい
PMがいないならエンジニアリングチームに深いドメイン知識が必要だろうが、そうでないならエンジニアリングの責任ではない。プロダクト組織の責任だ
古い話をひとつすると、複式簿記システムを作ったことはないが、何十年も前に売上が8桁まで成長したインターネット/通信スタートアップで課金システムを作ったことがある
若い開発者だった自分はよく分かっておらず、しかも偶然にも初日から課金ロジックを作ることになったのだが、良くも悪くもシステムの2か所にそれを作った。消費者向けの課金Webページと、請求書を生成しクレジットカード決済を実行する別のバックエンドプロセスだ
その2つを一致させて維持するのは驚くほど難しかった。資本を燃やしながら市場の反応を探るために反復開発を続け、新しい製品やサービス、新しい割引・価格方式、従量課金、月額課金、最初のX回無料、企業アカウントの代表支払者/サブアカウント、ユーザー指定のコストセンター、そのコストセンターへの税配分や1セント単位の按分といった機能が次々に追加された。そのたびに新たなひだや例外が生まれ、2つの画面/方式の数字が一致しなくなった
課金責任者が自分だったので、毎月数日かけてすべての請求書を手作業で確認し、クレジットカード決済や紙の請求書発送の前に、最終チェックとして数字が合っているかを確かめていた。いつも、あるいはしばしば、1人または少数の顧客に影響する新たな問題を見つけ、実際の課金前にコードを修正していた。すべてを手作業で再確認せずに手放すのは、常に不安だった
不一致と手動のクロスチェックをなくすため、課金ロジックを1か所にリファクタリングしようかと考えたが、長く考えた末に、単一コードベースは不都合で、むしろ2つのコードベースのほうが自分のミスを見つける助けになると気づいた。その後は、2つの実装のあいだで自動実行とクロスチェックを徐々に容易にしていった
課金コードは自慢できるほどきれいではなかったが、課金精度、不満の少なさ、そして何年にもわたって避けられたぞっとするようなミスは大いに誇らしかった。後任たちに残した複雑さには少し罪悪感があるが、今でも大きく後悔はしていない
その経験以来、複式簿記の動機はいつもよく理解できていた。顧客に被害が及ばないよう自分のミスを防ぐために、雑なやり方で二重ロジックの課金コードを再発明していたようなものだった
前職で率いることになったデータチームには、お金を「失くす」という不運な癖があった。実際のお金がどこかへ移動する途中で消えたわけではなく、顧客に請求すべき記録が消える、という形だった
売上を失わないときは二重請求をしていて、こうしたことが続いていた。経営陣の信頼を取り戻すのに3年の厳しい作業がかかった
テストもなかったのか? 「5ドルを購入するたびに取引ログには4.98ドルが残った」という例を挙げられるほど、取引のたびに金を失っていたのなら、問題は複式簿記の不在よりはるかに大きい
そんな金融システムを作って正常だと思う人がいるのか? 補償も問題だが、そんなサービスならできるだけ早く逃げるべきだ
「踊るセント」みたいな冗談を言っていて、意味のある結果を負わずに済むと分かっていたからそうしたのだ。素早く動き、何かを――金に関わるものを――壊し、笑って済ませた
今では、自分たちが意図的にそういう決定をしたという理由で、道徳的・技術的権威でもあるかのように人に説教しようとしている。驚くほど傲慢なスタートアップVC文化的なたわごとだ
もちろんバグを見つける手がかりにはなり得るが、基本的なテストを書いていても同じだったはずだ
筆者がなぜ「make it work, make it right, make it fast」という格言を否定的に持ち出しているのか分からない。たぶん「make it fast」がどこに入るか誤解しているのだろう
「make it right」が第2段階であり、正しく動くようになるまではそこで作業を止めるべきだ。システムは健全に動作しなければならない。「make it fast」、つまり最適化は、正確性と健全性の問題が完全に解決された後で初めて始まる
これは納品速度や素早く働くこととは関係なく、最適化を最後の段階に回せという意味だ
ただし筆者が言いたかったのが、何かが大まかに「動く」としても「正しさ」から離れすぎていて、後から巻き戻して直せず、ようやく動き始める前から最初から「正しく」作らなければならない、という点なら理解できる
私も同意するし、フィンテックシステムの監査に参加したことがある。監査人たちは帳簿を承認する前に、すべてをExcelスプレッドシートに落として数字を突き合わせなければならなかった。時間も金もかかり、3年後の流動化イベントで少なくとも0.1ユニコーン分の差を生んだだろうと推測している
速く動くスタートアップでは、できるだけ早く実質的なMVPを出す。顧客基盤や財務などを作らなければならないので、「make it work」の段階で止まってしまう
もっと適切な格言はFacebookの「move fast and break things」だったはずだ。ただしそれは後で直せる場合にしか通用しない。たとえば航空機を作るなら、そんなことはしないだろう
その文脈を見ると、最初の文で述べた誤解がいちばんありそうだ。すぐ後で、スタートアップが受ける時間的圧力の話をしているからだ
ここのコメントの大半が、記事が批判していることをそのまま繰り返している。単式簿記を擁護する長い議論がいくつも見える
単式簿記のほうが簡単で、より一般化されているのかもしれないが、何世紀にもわたって発展してきた仕組みと抽象化にただ従うのが良い考えであることもある
必ず別のものが必要なのでなければ、複式簿記を使うほうがよい。プログラマーの本能にはしっくりこないかもしれないが、不一致を整理するために実際の会計士を呼ばなければならない瞬間が来たら、ありがたみが分かるはずだ
これに関連して、決済やその周辺分野のプログラマー向けによい資料を知っている人はいるだろうか? 「プログラマーのための会計」みたいなものだ
https://martin.kleppmann.com/2011/03/07/accounting-for-compu...
https://www.moderntreasury.com/journal/accounting-for-develo...
[0]: https://www.winstoncooke.com/blog/a-basic-introduction-to-ac...
10回取引するごとにデータの1%が消えるデータベースシステムを使っていると言われたら、こんなエンジニアリングブログの助言を真剣に受け取れるだろうか? こういう文章は概念紹介というより、特定の個人や集団の宣伝広告を落ち着いた調子で包んだもののように感じる
金を動かすソフトウェアに対するこうした杜撰な態度が実際の人間にどんな結果をもたらすかを見たければ、Post Officeスキャンダルを見ればよい
https://en.wikipedia.org/wiki/British_Post_Office_scandal
金を動かすすべてのものは、可能な限り真剣に扱うべきであり、できるだけ多くの歴史的な失敗を知っておくべきだ
https://en.wikipedia.org/wiki/Mr_Bates_vs_The_Post_Office