Upgrade to Pro — share decks privately, control downloads, hide ads and more …

400超のデータポイントを型で制す — UPSIDERの与信審査エンジンを支えるTypeScr...

400超のデータポイントを型で制す — UPSIDERの与信審査エンジンを支えるTypeScriptの「柔軟性」と「結合力」_泉雄介

TSKaigiでVPoE泉が登壇した資料です。
https://2026.tskaigi.org/talks/39

More Decks by UPSIDER, Inc. Tech&Product div.

Transcript

  1. 自己紹介 アメリカ 音楽大学を卒業後、メディア制作会社で作曲家とし て勤務した ち、システム開発で起業。 モルガン・スタンレー証券会社で 債券取引 システム開発 や、ディー・エヌ・エーにおけるゲームプラットフォーム事業や ヘルスケアサービス開発

    リードエンジニア、ラクスル取締 役CTOを経て、株式会社UPSIDERに入社しVP of Engineeringを務める。 言語: Go / TS / Ruby / Perl / C# / JS / Java / PHP CFA協会認定証券アナリスト (2011~) 日本CTO協会理事 (2024~) グロース市場企業社外取締役(2025~)
  2. UPSIDER 審査規模・体制 1 取扱企業 数万社規模 日々更新 2 特徴量 1審査あたり 400

    超 データポイント 3 ロジック 4系統 x 系統毎 20以上 チューニング パラメータ 4 運用体制 少人数で 高機能運用 5 開発体制 PdM x 1, App x 1 Data Eng x 1, DS x 1
  3. 業界 常識 vs 現場 要件 VS 業界 常識 (金融ドメイン )

    Java / Kotlin / Scala / Go 等による堅牢な実装 安定したデータモデル 一度決めた構 慎重に変更する レイヤーごと 厳密な分割 各層で専用 DTOを持ち同期する UPSIDER 現場要件 TypeScript + Zod 堅牢性とアジリティを両立 変化する審査ロジック 市場変動に合わせて動的に変えたい システム全域 貫通 1つ スキーマでDBからUIまで結合する 技術選定 変更頻度 アーキテクチャ
  4. スキーマ宣言を「 1回」で済ませる export const zFeatures = z.object({ banks_avg_balance: z.number(), dpd_over_30:

    z.boolean(), banks_burn_slope: z.number().nullish(), banks_concentration_inflow_1m: z.number().nullish(), // ... 400 lines ... current_credit_line: z.number().nullish(), dpd_over_30_within_36_months: z.boolean().nullish(), }) export type IFeatures = z.infer<typeof zFeatures> 1. コンパイル時 型定義 (IFeatures) 2. 境界 ランタイム検証 (z.parse) 3. 部分型へ 派生 (zFeatures.partial()) 4. JSONシリアライズ対応
  5. もしJavaで同じことを書いたら? HTTP Domain Persistence Wiring / Test FeaturesRequest Bean Validation付き

    API用DTO Features Record Immutableなドメインモデル FeaturesEntity & Converter JPAとJSONBカラム 変換 FeaturesMapper & Builders MapStruct等で 型変換・テスト 1つ 概念に 8つ以上 ファイル が飛び散る摩擦
  6. 「What-if分析」 要件 動的マージにより、本番と全く同じロジックでパラメータや特徴量を変えたテスト・シミュレーションが実行可能 PostProcess Model Parameter 生成 特徴量 抽出 計算

    結果 { 決定与信枠、自動承認 判定 } Parameter Injection ・X以上 スコアで自動承認 ・最低X円以上 与信枠 など 閾値や既定値を上書き Feature Injection ・個社ごと 固有 特殊事情 ・特定 属性値 一時的な変更 など 特徴量を上書き ビジネス影響 シミュレーション ポリシー(パラメータ)変更によって、 全 体 与信枠金額にど ような影響が あるかを計算可能。 個別 特徴量変更による個社へ 影 響 テスト・試算も本番と同じプロセス で検証できる。
  7. コードで見る動的マージ 実装 「自動で動く本流」 特徴量と「手動介入(DB設定値)」 オーバーライドをマージする実際 処理 // DB設定値 (JSON列) 用

    スキーマ export const zServiceProvisionConfig = z.object({ featureOverrides: zFeatures.nullish(), predictionOverrides: zPredictionModelResponse .partial().nullish(), }).strict() // → 完全に型付けされた overrides が手に入る // マージ実行 (predictProcedure.ts) const mergedFeatures = { // 本流 入力 ...input.features, // DBから オーバーライドをマージ ...(serviceProvision.configuration?.featureOverrides || {}), } 「JS スプレッド構文 気軽さ」を保ちつつ、「静的型推論」と「 Zod 境界検証」で守る
  8. 動的マージと静的型 両立 1. モデル全体 デフォルト DB 2. 今回 審査だけ 上書き

    DB 3. Zodによる境界検証 (.parse) 完全に型付けされ、制約を満たした値として下流へ
  9. 実行時 想定外をシャットアウト Java 限界 Optional<Builder> 連鎖 何層にも重なるマージ ビジネスサイドに 説明できないコードに陥る 「上書きしたつもりが

    出来ていない」バグ 温床 Pure JS 危険 スプレッド構文 {...a, ...b} タイポや未定義オブジェクト によるマージ失敗を 静的に弾けない 値が文字列 ままマージされ 計算時に NaN を誘発 TS  Zod 解 マージ 柔軟性 JS まま 境界に来た瞬間に型と制約を検証 数値 範囲制約も実行時に保証 想定外 キー .strict() で拒否可能
  10. なぜ Strategy Pattern を用いる か? UPSIDERで 複数ブランドや特殊案件に応じた多様な審査パターンが存在。 「入出力 型 同じまま、内部

    計算ロジックだけを柔軟に切り替える」設計が必要。 審査プロセス開始 INPUT 共通 型) ML予測審査パターン 1 UPSIDERカード等 ) ML予測審査パターン 2 PRESIDENTカード等 ) 上場企業・特殊案件用審査 (各種特化ロジック ) 後続プロセス OUTPUT 共通 型) 入出力 型制約 担保 したまま、中身 計算ロジックだけを動的に Switchできる
  11. 具体 実装 switch (method.postAppraisalProcess) { case PostAppraisalProcess.DEFAULT: return new DefaultPostProcessor().run()

    case PostAppraisalProcess.RUNWAY: return new RunwayPostProcessor().run() default: // 型による網羅性 強制 const _exhaustive: never = method.postAppraisalProcess throw new Error(`Unsupported`) } ▪ Exhaustiveness Check enumに新しい値が追加されたとき、 switch文で処理が漏れているとコンパイル エラーとして検知。 ▪ 安全なStrategy分岐 ロジック追加時 「処理漏れ」を人間で なくコンパイラが確実に防ぐ。 ▪ 堅牢性へ アプローチ Java 21 sealed interface + pattern matching と同等 安全性をシンプル な文法で実現。
  12. 新しい審査パターン 追加コスト Interface実装 追加 IAppraisalPostProcessor を満たすクラスを作成 1 Switch文へ 追加 コンパイラが漏れを検知。

    既存処理 破壊も防ぐ。 2 構 的型付けと Interface 恩恵により、低コストで安全に ロジックを拡張可能。 テスト審査 テストモードで新しい審査 パターンで結果を確認して適用 3
  13. OpenAPI生成モデルと 決別 従来 Java+JS開発 DB Entity DTO / Response Model

    OpenAPI 仕様 生成 Generated TS Model tRPC  Zod バックエンド Zod スキーマ フロント Mutation 型 コンパイル時 推論 みで直結
  14. 開発現場に見られる兆候 1Pizza Team チームサイズを 2-Pizza (7〜8人) から 1-Pizza (2〜3人) へ縮小。

    エンジニアがAIを駆使して広範囲をカバーする ため、コミュニケーションコストが劇的に下が る。 巨大なPull Request 人間が介在せず、AIに自律的にコーディング させるため、PRが肥大化する。 しかし、型 境界が守られているため 「それ で良し」とする新しい開発スタイル。 人間 役割シフト 不要になるタスク: コーディング、単体テスト 集中すべきタスク: 要件定義、アーキテクチャレビュー、結合テ ストなど上流工程へシフト。