金融システム構築のためのエンジニアリング原則
(substack.wasteman.codes)- 会計はここ数百年の間、大きく変わっていない
- それにもかかわらず、金融システム向けソフトウェアをどう構築するのが正しいのかについては多くの混乱がある
- この記事では、大企業で金融システムを構築した経験に基づく教訓を共有する
- 会計システムの構築に焦点を当てるが、これらの原則はより一般的な金融システムにも適用できる
基本的な金融用語の定義
- 総勘定元帳 (General Ledger, GL): 特定期間中のすべての金融取引を要約した、企業の主要な会計記録。関連する補助元帳の集計と考えることができる
- 補助元帳 (Sub-ledger): 特定のGLに関連するすべての個別取引の詳細情報を含む。補助元帳のレコードには、総勘定元帳よりもはるかに細かい粒度のデータが含まれる(例: 特定の顧客、注文内の特定項目など)。補助元帳とGLのデータ差分は、事業の種類と扱うデータ量によって異なる。小規模企業の中には補助元帳なしで運用できるところもあるが、規模が小さい場合はカスタムソフトウェアが必要になる可能性はあまり高くない
- 財務記録 (Financial Record): 総勘定元帳と補助元帳を総称したもの
- 重要性 (Material): 財務諸表上の情報の歪みが、合理的な利害関係者の意思決定に影響を与えるかどうかを示す。この定義は意図的にやや曖昧であり、企業ごとに重要性の基準が異なるためである。たとえば、年間売上25万ドルの企業にとって重要なことが、年間売上10億ドルの企業にとっては重要でない場合がある。設計の観点では、この概念の主な価値は、さまざまなカテゴリの財務データを分類することにある
High Level Data Flow
Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger
会計システムの3つの主要目標
- 正確性 (Accurate): 財務記録はビジネスの既知の状態を反映していなければならない
- 例: $9.99の商品を10個販売した場合、その財務記録の合計は$99.90であるべき
- これは一見明白に思えるが、数千件、数百万件の取引を集計すると、システム間の単純な合算や丸め誤差によって重大な不正確さが生じることがある
-
Wastemanのノート
- 命名がコンピュータサイエンスで最も難しい問題だと言われるが、加算はその次に難しいと思う
- ここ数年、大規模な金融システムに携わる中で、ごく小さなバグがデータに大きな差異を生んだ例を数えきれないほど見てきた
- floatの合計の話はしないでほしい。常に整数を使うべき理由を痛いほど学んだ
- 財務記録は 完全 (complete) でなければならない
- より具体的には、補助元帳と総勘定元帳は、ある時点で発生したすべての事業活動を完全に表現していなければならない
- 発生したにもかかわらず財務記録に存在しないイベントがあるなら、そのシステムは完全ではない
- これは結果整合性 (eventual consistency) が許容されないことを意味するわけではない
- データがいつ完全になるのかを把握しておくことで、利害関係者に対してデータが確定したことを伝えられる
-
Wastemanのノート
- 完全性を保証することも驚くほど難しい問題だ
- システムが拡張されるにつれて、データが複数のシステムを通る過程で誤って変形されたり欠落したりすることがある
- 監査可能性 (Auditable): 利害関係者が誤りを検出し、ビジネス実績を正確に測定できるよう、財務記録は容易に監査できなければならない
- 適時性 (Timely): 会計システムはビジネス固有の要件を満たさなければならない
- 小規模企業では月末にすべての数値をダンプするだけで十分な場合もあるが、大企業は通常、ほぼリアルタイムのシステムを求める
- これにより、月を通じて財務状況を監視し、財務データに基づく意思決定をより迅速に行い、月初や四半期初の締め作業の切迫感を減らせる
- 必要性が何であれ、会計システムはビジネスの要件を満たし、彼らにとっての 適時 の意味そのものを実現しなければならない
-
Wastemanのノート
- 適時性に関して、人はバッチ対ストリーミングシステムの議論で道を見失いがちだ
- 私の見解では、ほとんどのシステムにおいてこれは重要な区別ではない
- 数秒から数分といった非常に短いレイテンシを気にするなら重要になる
- しかし、利用者が1日に数回以上更新を見る必要がないにもかかわらず、何を採用すべきかで議論しているのを驚くほど多く見かける
- 要求されたからといって、本当に必要とは限らない
会計システムが守るべき3つの主要なエンジニアリング原則
- データの不変性 (Immutability) と永続性 (Durability)
- 監査可能性を担保し、デバッグと正確性の向上に役立つ
- データが不変であれば、いつでもシステムの状態を記録できる
- これにより、過去の状態から世界を再計算することが非常に容易になる。どの状態も失われないからだ
- 一度財務記録に記載されたデータは削除できない
- システムへのあらゆる修正は、新たな金融取引として表現されるべきである
- 例: システムのバグにより、本来$900であるべきサービスが誤って$1000で販売されたと記録された場合
- この誤りを正すには、まず誤りに対応する会計仕訳を取り消し、その後、正しい金額で会計仕訳を再計上する必要がある
- データは最小粒度で記録されるべき (Data recorded at the smallest grain)
- 上記の原則と同様に、これは明確な監査証跡を可能にするうえで非常に重要である
- 財務報告書と総勘定元帳は集計されたものだとしても、それらはより細粒度のイベントから計算される
- データが理解できないとき、何が問題だったのかをデバッグするには最も細かい粒度のデータが必要になる
- 最小レベルの粒度でデータを保存しておけば、そのデータセットから派生したデータを修正することも非常に容易になる
- 単一の不変データセットが、そのデータのあらゆるビューに対する中核的な真実のソースであるなら、
- ビューを修正するには、データを修正したうえで、そのビューを生成するパイプラインを再実行するだけでよい
- 同様に、会計担当者が帳簿を締める準備をするとき、
- 彼らは帳簿が正確であることを確認するために、発生したすべての取引と勘定残高を突合する
- 不一致が見つかれば、原因となっている正確な取引まで掘り下げることができる
- 冪等性 (Idempotency) を持つべき
- すべての財務イベントは一度だけ処理されなければならず、財務記録の重複は明らかな不正確さを生む
- このため、財務記録を生成するすべてのコードは冪等であるべきだ
- 冪等とは、ある操作を何度適用しても結果が変わらない性質を意味する
- つまり、財務イベントを複数回処理しても、結果は最初に処理したときと同じでなければならない
ベストプラクティス
- 金融金額の表現には整数を優先すること: 算術演算がはるかに容易になる。floatは避けること
- 通貨換算時の精度損失を最小化できるよう、金融金額の粒度をサポートすること
- ドルだけを扱うなら、セント単位で表現するだけで十分な場合がある
- グローバルビジネスでは、マイクロ単位や
DECIMAL(19, 4)のような小数を優先する - 金融システムでは小数の採用が一般的だが、広告金融システムではマイクロ単位が標準である
- 一貫した丸め方法を使うこと: 丸め方によって、期待値と重要な差が生じることがある
- すべての値について、5以上は次の有効桁へ切り上げ、4以下は切り捨てる方法
- 常に切り上げる方法など
- 重要なのは全体として一貫性を保つこと(取引ごとに1セントの差でも、1,000万件なら$100kの差になる)
- 可能な限り通貨換算を遅らせること: 事前に通貨換算すると精度損失が発生する可能性がある
- 現地通貨での集計が行われた後まで通貨換算を遅らせる
- 時間は整数表現を使うこと: やや議論の余地はあるが、強く推奨する
- タイムスタンプをオブジェクトとしてパースするさまざまなライブラリが、それぞれ異なる扱いをする
- こうした厄介さを避けるためにも、整数を使うのがよい
- UnixタイムスタンプやUTCベースの整数datetimeでもまったく問題ない
- システム間でのデータ変換は少ないほどよい
-
Wastemanのノート
- サマータイム関連のバグについては、まだ触れてもいない。単調増加する整数を使えば、これを完全に回避できる
- datetimeにこだわるなら、少なくともUTCを使うこと。驚くほど多くの大企業がUTCではないタイムスタンプを使っている
2件のコメント
これは本当に役立ちますね。何も考えずに型変換(decimal、float、double)や丸めを、実務での議論もなくそのままやってしまうと大変なことになります。
Hacker Newsの意見
一貫した丸め手法を使う重要性を強調
時間を整数で表現する方法を推奨
会計システムではリレーショナルデータベースの使用を推奨
会計システムの主な目標は、正確性、監査可能性、適時性である
会計システムの完全性に関する意見
グローバルビジネスでは最低8桁の小数点使用を推奨
ユーザーインターフェース(UI)の重要性に言及
バッチ処理とストリーミング処理の違いを説明
TypeScriptを使った請求書システム構築の経験を共有
標準ライブラリのクラス使用を推奨
丸めとデータ共有の難しさを説明
米国の上位10大銀行のAPI作業経験を共有
Martin Fowlerの"Accounting Patterns"を推奨