コンテンツにスキップ

BFFの実装・移行

職務経歴書の8エピソード一覧から、今回は一つだけ選ぶ。同じページの他の経験には広げず、関連知識の補強もこのエピソードに沿って行う。

セッション:開発手順を共通化

Section titled “セッション:開発手順を共通化”

Dockerマルチステージビルド、ホットリロード、タスクランナー、lint、Protocol Buffersからのコード生成を整備し、他チームの後続Go案件でも採用された経験。

ChatGPT開始文:開発手順を共通化
https://github.com/MFQWKMR4/tech2026 のAGENTS.md、CHATGPT.md、
src/content/docs/projects/bff-migration.md、reviews/experience-bff-migration.yaml、
src/content/docs/career/interview-tips.md、.github/ISSUE_TEMPLATE/session-sync.md、reviewが参照する必要な過去sessionを読んでください。
読めた資料とcommit SHAを示し、読めない場合は読んだとせずsession packを求めてください。
今回はtopic: experience-bff-migration、session_type: experienceです。
選択エピソード:開発手順を共通化。1エピソード1セッションとし、他の開始文は実行しないでください。
対象:Dockerマルチステージビルド、ホットリロード、タスクランナー、lint、Protocol Buffersからのコード生成を整備し、他チームの後続Go案件でも採用された経験。
既存needs_revisitのうち今回の話に関係するものを優先し、無関係な項目は次回に残してください。
なければ最初の一問は「共通化する前に、チームの開発手順にどんな困りごとがありましたか。」。一度に一問だけ尋ね、回答を待ってください。
当時の課題・観測→自分の担当と行動→判断理由と代案→検証・結果を、回答に応じて思い出す手助けをしてください。
覚えていない事実はunknownで止め、今知りたい仕組みを一つ選んで学び直してください。
技術補強の候補:開発用・実行用イメージの違い、依存と生成コードの再現性、ローカルとCIの手順、共通化の範囲と保守負担。
前提知識を短く説明し、一次資料のURLと対象バージョンを示してから、条件を一つ変えた問いで理解を確かめてください。
注意:今回は互換性比較とカナリア移行へ広げないでください。OpenAPIとProtocol Buffersの生成対象は記録から同一視せず、私が整備した範囲を確認してください。
当時の経験、今の学び、AIの補足・演習条件を分け、経験・成果・不足を推測で補完しないでください。
会社名・顧客情報・非公開情報・職務経歴書の連絡先は教材や同期Issueへ転載しないでください。
最後に私自身がこのエピソードを短く説明し、根拠のある担当・判断・結果と、まだ確認が必要な点を整理します。模範回答を先に出さないでください。
開始時刻が分かれば記録し、40分付近で区切りを提案。時刻不明なら経過を推測しないでください。
終了時は既存形式の[Session Sync](YAML+Session narrative)を作成し、summaryとnarrativeに選択エピソード名、実際の回答・ヒント、当時の経験と今の学びを残してください。
フィードバックではtipsから今回に関係する観点を選び、実際の回答を根拠に伝わった点・説明を補う点・未確認を分け、narrativeに残してください。全問の消化は目標にしません。
repository_requestsにはこのprojectページへ戻す想起内容・技術解説を指定し、別エピソードの記録を上書きしないでください。
Issueを作成できなければ未作成と明示し、コピーできるタイトルと本文を返してください。

セッション:互換性検証と段階的なリリース

Section titled “セッション:互換性検証と段階的なリリース”

新旧APIのJSON比較ツールを提案・実装し、約200パターンでシリアライズ差異を検出・修正。Kubernetes・Argo CD環境でカナリアリリースを実施した経験。

ChatGPT開始文:互換性検証と段階的なリリース
https://github.com/MFQWKMR4/tech2026 のAGENTS.md、CHATGPT.md、
src/content/docs/projects/bff-migration.md、reviews/experience-bff-migration.yaml、
src/content/docs/career/interview-tips.md、.github/ISSUE_TEMPLATE/session-sync.md、reviewが参照する必要な過去sessionを読んでください。
読めた資料とcommit SHAを示し、読めない場合は読んだとせずsession packを求めてください。
今回はtopic: experience-bff-migration、session_type: experienceです。
選択エピソード:互換性検証と段階的なリリース。1エピソード1セッションとし、他の開始文は実行しないでください。
対象:新旧APIのJSON比較ツールを提案・実装し、約200パターンでシリアライズ差異を検出・修正。Kubernetes・Argo CD環境でカナリアリリースを実施した経験。
既存needs_revisitのうち今回の話に関係するものを優先し、無関係な項目は次回に残してください。
なければ最初の一問は「検出したシリアライズ差異を一つ挙げると、利用者にはどんな違いが生じ得ましたか。」。一度に一問だけ尋ね、回答を待ってください。
当時の課題・観測→自分の担当と行動→判断理由と代案→検証・結果を、回答に応じて思い出す手助けをしてください。
覚えていない事実はunknownで止め、今知りたい仕組みを一つ選んで学び直してください。
技術補強の候補:API互換性の比較範囲と見逃し、テストケースの偏り、段階的なトラフィック移行、継続・中止判断とロールバック。
前提知識を短く説明し、一次資料のURLと対象バージョンを示してから、条件を一つ変えた問いで理解を確かめてください。
注意:今回は開発環境共通化を別セッションに残してください。既存記録の他者によるヘッダー問題の修正・ロールバックと、私が実施したカナリア作業を区別してください。
当時の経験、今の学び、AIの補足・演習条件を分け、経験・成果・不足を推測で補完しないでください。
会社名・顧客情報・非公開情報・職務経歴書の連絡先は教材や同期Issueへ転載しないでください。
最後に私自身がこのエピソードを短く説明し、根拠のある担当・判断・結果と、まだ確認が必要な点を整理します。模範回答を先に出さないでください。
開始時刻が分かれば記録し、40分付近で区切りを提案。時刻不明なら経過を推測しないでください。
終了時は既存形式の[Session Sync](YAML+Session narrative)を作成し、summaryとnarrativeに選択エピソード名、実際の回答・ヒント、当時の経験と今の学びを残してください。
フィードバックではtipsから今回に関係する観点を選び、実際の回答を根拠に伝わった点・説明を補う点・未確認を分け、narrativeに残してください。全問の消化は目標にしません。
repository_requestsにはこのprojectページへ戻す想起内容・技術解説を指定し、別エピソードの記録を上書きしないでください。
Issueを作成できなければ未作成と明示し、コピーできるタイトルと本文を返してください。

GitHubを読めない場合は npm run session:pack -- experience-bff-migration の出力を渡す。パック内に複数の開始文があっても、選択した一件だけを扱う。

過去プロジェクト一覧へ戻る。

解説図:AI補足:応答の比較範囲を図で見る

職務経歴書で確認した追加情報

Section titled “職務経歴書で確認した追加情報”

2026-09-16の本人確認では、開発手順の共通化にDockerのマルチステージビルド・ホットリロード・タスクランナー・lint・Protocol Buffersからのコード生成を含み、設定は他チームの後続Goプロジェクトでも採用された。また、JSON比較・差異修正に加え、Kubernetes・Argo CD環境でカナリアリリースを実施した。

以下の過去の回答記録は保持する。OpenAPIとProtocol Buffersの生成対象、カナリアで本人が操作・判断した範囲は次の対話で具体化する。他者が行ったロールバックを本人の担当へ置き換えない。追加情報の保存だけでreviewや習得状態は更新しない。

2026-09-07の初回口述メモを、同日の深掘りセッション(Issue #4)で更新した。以下は本人の回答の要約であり、コードや実測ログを独立に検証した記録ではない。会社名は掲載せず、共有可能な技術内容だけを残す。

3人チームの一メンバーとして、BFFの全APIをScalaからGoへ移行した。主に複数のマイクロサービスAPIを集約するBFFで、実行基盤はKubernetesだった。本人はAPI移行に加え、開発環境整備と互換性検証を主導した。

内部構成を簡素化する方針はtech leadの判断。検索の並列化と片側失敗時の返却方針も既存Scala版から継承したものであり、本人発案の設計とは扱わない。Scala版の詳細な層・型変換経路はunknown。adapter / use caseという名前の記憶だけでHexagonal/Clean Architecture採用と確定しない。

本人の説明した依存方向は、controllerからmodelとinfrastructureへ、modelからinfrastructureへ。infrastructureから上位層への依存は置かなかった。各層は意味に応じた型を持っていた。当時の「model」は呼称であり、domain modelと同義とは断定しない。

backendのresponseだけでフロントエンド要求を満たす場合はmodelを作らず、controllerからinfrastructureを直接呼んでいた。追加処理がなければmodelを通す必要はない、という本人の判断だった。

図1:単一backendで完結する経路JSON正本)。矢印は実行時の呼び出し・返却であり、import関係の図ではない。具体的なAPI名・型名はunknown。

検索ではmodelが処理を組み立てた。Query Handling APIで入力を検索クエリへ変換し、その結果を使って広告求人検索と通常求人検索を並列に呼び、結果を統合した。前段の結果に依存する箇所は直列、独立した二つの検索はレイテンシを抑えるため並列だった。片方が失敗した場合は成功した側だけを返す既存挙動をGoでも維持した。

図2:Query Handling後の検索fan-outJSON正本)。二つの検索矢印は並列分岐を表す。縦の描画順は開始・完了の実測順序を意味しない。両方失敗時、Query Handling失敗時、具体的なtimeout時の処理はunknown。

初回メモではmodel/infrastructureのパッケージを他チームから利用可能にしていたと説明した。社内利用の文脈であり、OSS公開や具体的な可視性設定、interfaceの所有場所は未確認。interface経由でテスト実装へ差し替えた記憶もあるが、正確な対象・コード配置はunknown。

本人発案の互換性検証と見逃し

Section titled “本人発案の互換性検証と見逃し”

QAの目視だけでは全データ項目を確認しにくいと考え、Scala版とGo版へ同一リクエストを送りJSON responseを比較するツールを提案・実装した。時刻等の無視対象フィールドを指定でき、負荷試験等のため整理済みだった約200パターンを利用した。これは約200パターンを本人が新規作成したという意味でも、全入力を網羅したという意味でもない。

比較でシリアライズ差異を検出した。Scalaの独自処理に合わせてGo側にも独自処理を実装・差し替えた。小数表現付近だった記憶はあるが、正確なフィールド・差異はunknown。JSON比較、unit test、QA確認が揃い、本人はリリース前には安心していた。

しかし本番ではresponse headerに含まれる値の引き回し差異を見逃し、事業側の指標変化から問題が発覚してrollbackされた。具体的なheader・指標名はunknown。本番固有の広告効果計測等に関係していたという記憶に留まる。該当実装は本人担当ではなかったが、コードレビューで見落とした点を本人の反省としている。rollbackと障害修正は本人に対応が振られる前に進んでおり、本人の対応実績とはしない。

約10 Podを1→3 Podのように段階的にGo版へ置き換え、記憶では3 Pod程度で問題を発見し全量切替前に止めた。Pod数の比率が実トラフィック比率と等しかったかはunknown。本人はDatadog APMのレイテンシ・503率等を確認しており、性能面は改善していた。正確な数値はunknown。カナリアは被害を限定したが、これだけではheaderの同等性を確認できなかった。

現在なら、移行の最初に既存コードの設計意図を理解し、削る抽象化の理由を明示して、受け入れ条件と検証方法をチームで決めると説明した。本番との差異を整理し、1 Podで何を確認して次へ進むかも事前に決める。

ChatGPTが事業指標をpromotion criteriaの中心に整理した際、本人はその整理を修正した。互換移行の主眼は技術的な同等性の直接確認であり、事業指標だけでは外部要因の影響を切り分けられない。一方、関連指標を異常検知として見ることと、プロダクトの収益構造やABテストの目的を理解して重要な技術箇所を見抜くことは有用、と振り返った。

Go経験の浅い3人が同じコマンド・同じ手順で開発できることを目的に、Dockerfile/Compose、ホットリロード、タスクランナー、lintを整備した。BFF側でOpenAPI定義を作成してフロントエンドのレビューを受け、request/response型とserver/controller interface相当を生成していた。移行中には未使用プロパティをフロントエンドと確認して整理したケースもある。

確認できた開発フローは「API定義を作成・レビュー → 型/interface相当を生成 → 実装」であり、起動・反復開発・lintの手順を共通コマンドへ揃えた。ツール名・実行順の細部はunknown。後続Goプロジェクトで本人が導入したタスク設定等の採用を確認し、雛形として参照されたと認識した。正式な全社標準や定量的な生産性向上は主張しない。

Goのcontextと並列API呼び出しのtimeout/cancelの関係を学んだと説明した。具体的な実装やerrgroup利用はunknown。KubernetesとArgo CD/GitOps系の運用も初めて経験したが、設定・運用の担当範囲は未確認。

2026-09-08反映。Issue #6の実施日・ヒント有無はunknownのため、既存session/reviewの再評価には使わない。以下は本人申告の追加メモ。

  • 移行の背景は、会社としてScalaエンジニアの採用が難しく、Goへ技術スタックを寄せる方針だった。
  • 自ら導入したタスクランナーはPRで使い方を共有し、Taskfile内にも用途を記載。他メンバーも利用・追加し、最終的に10個超程度のタスクになったという。コード生成等の複雑な操作の共通化に役立った。
  • 規約は非同期で案とコメントを出し、定期ミーティングで合意した。命名で本人は構造体/シングルトン的な値のメソッドを名前空間のように使う案を出した。チームは構造を増やさず、明示的で多少長いpackage-level function名で衝突を避ける案を選んだ。本人は、本来の課題である名前衝突を解決できるため合意した。
  • QAの確認範囲からログ基盤との結合が漏れていると気づき、JSON Schemaによるログ構造検証と、同じ入力に対する旧Scala版・新Go版のログ比較を提案した。本人によればログ関連の不具合2件をリリース前に検知した。各方法の実装担当、2件の内訳、既存のresponse比較ツールとの関係はunknown。ログ比較とresponse body比較を同一の検証と決めつけない。
  • 「開発完了が約2週間早まったという記録」と述べているが、元の計画・比較基準・本人の施策との因果は未確認。個人の施策で2週間短縮したとは断定しない。

これらの成果と、Issue #4で確認した本番header問題・rollbackは併存する。ログ不具合の事前検出を「移行で本番障害がなかった」と言い換えない。

Scala版の設計名・型変換経路、header名と低下指標、障害後の再検証手順、シリアライズ差分の詳細、並列処理APIは、今回の問いでも思い出せなかった。知識の誤りとは判定しない。担当期間、言語・ツールのバージョン、具体的な性能数値もunknownのまま残す。

次回はreviewのneeds_revisitから一問ずつ、まず「この移行であなたが主導した判断と、既存仕様を引き継いだ部分を分けて説明してください」と尋ねる。以降、アーキテクチャの依存方向と境界、互換性の受け入れ条件を再確認する。以下のAI解説や回答例を先に読み上げず、自力説明と説明後の回答を分ける。

以下は記憶との照合に使う一般設計例。Scala版の採用事実や、本人が実施済みの検証を示すものではない。

コードと図で一般論を深める場合はGoコードで理解するHexagonal・Clean Architectureへ。架空の見積APIを使い、以下の概要から依存方向・テスト・採用判断へ進める。一般教材のtopicはarchitecture-boundaries。そのtopicのsession packには全文が含まれる。

Hexagonal・Clean・典型的な3層構成

Section titled “Hexagonal・Clean・典型的な3層構成”

まず小さなBFFでは、HTTPの入出力、集約処理、backend通信の責務を分けるところから始める。層の名前を増やす前に「何が変わるとどこを直すか」「どこを単独テストしたいか」を考える。

観点 典型的な3層構成 Hexagonal / Ports and Adapters Clean Architecture
中心の考え 表示・アプリ処理・データアクセス等の責務分担 アプリの内側を外部技術から切り離す 業務ルールを外側の詳細から独立させる
ソースの依存 上位から下位への依存が典型。3層という名前だけでは確定しない 内側に必要な対話をPortとして定め、外側Adapterで接続する ソース依存は内側へ向ける。制御が外へ出る箇所もinterface等で逆転する
テスト 下位への依存を差し替えられるかは実装による UIや実DBなしでもアプリを駆動できる use caseや業務ルールをframeworkから分離して確認する
コスト 薄い層なら単純だが責務が漏れ得る Port・Adapterの設計と保守が増える 境界・変換が増え、単純な処理では負担になり得る

三つは完全に排他的な分類ではない。層が三つでも内側にinterfaceを所有させられるし、adapterという名前だけでは依存方向は分からない。Cockburnの原論文(2005、v0.9)MartinのClean Architecture原文(2012-08-13)が比較の基準となる。

Portはアプリにとって必要な対話の契約、AdapterはHTTP・DB等の具体技術との変換役。Use Caseは一つの目的に沿った処理の組み立てを担う。ControllerはHTTP等の入力をアプリへの呼び出しへ変換する入口になり得る。Infrastructureは外部API client等の実装をまとめる呼称だが、名前そのものが依存規則を保証しない。

例えば内側の検索Use Caseが内側で定義した検索Portを使い、外側のHTTP検索Adapterがそれを実装する。実行時にはUse CaseからAdapterの処理が動くが、ソース上はAdapterが内側の契約へ依存する。今回説明されたGo版のmodel→infrastructure依存を、そのままこの依存性逆転と同一視しない。

一般例として、backendのDTOは外部APIの項目名・欠損表現を持ち、内部の型は集約処理が必要とする意味を表し、view modelはフロントエンドの表示契約を持つ。境界で変換すれば、外部schema変更が内部全体に広がることや、不要な項目が応答へ漏れることを抑えられる。

代わりにmappingコード、変換テスト、欠損・丸め・順序の扱いを保守する必要がある。同じ構造を理由なく三回コピーしても利益は小さい。単純なBFFでは型の共用や直接経路が合理的な場合もあるが、認可・入力検証・共通error変換をmodelだけに置いたなら、直接経路で迂回しない設計が必要になる。これは今回その不具合があったという意味ではない。

Productionで値が変わったら、backendの生応答→内部変換→外部responseのどの境界で差が生じたかを調べる。個人情報を無差別にログへ残さず、再現用の合成データと差分テストで絞る。層名より依存先・interface所有者・変換箇所をコードで確認するのが、記憶を照合する手順になる。

HTTPの応答はbodyだけではない。statusとfieldsも意味を担う(RFC 9110、2022年6月、§6・§8・§15)。下表の右列は現在の本人の方針を具体化したAI一般例で、当時の実施実績ではない。

境界 今回確認できた実施・観測 今なら検証計画に含めるもの
Request / response body 同一request約200パターンのJSON比較、無視field指定、シリアライズ差分修正 入力境界値、欠損/null、数値、配列順序、合意した契約変更。無視条件と理由を明示
Status / headers 機能的なresponse header差分を見逃した status、必要headerの値・伝播・複数値、cookie等の契約。全headerの文字列一致を無条件に要求しない
Error / timeout 片側失敗時の成功結果返却を継承。比較試験範囲はunknown 片側/両側失敗、前段失敗、deadline、cancel、error body、下流副作用
非機能 負荷試験、APMのレイテンシ・503率等を確認 代表負荷で旧版と比較し、許容レイテンシ・error率・resource使用の閾値を事前合意
外部依存・環境 本番固有条件に関わるheaderだった記憶 backend版、認証、proxy、flag、計測設定の環境差と、再現不能部分の確認場所

JSONが同じでも全挙動が同じとは証明できない。比較の対象と除外条件を契約として合意し、同じbackendデータや時刻条件を使えるかも整理する。旧版の既知不具合まで維持するか、意図的変更として分けるかは利用側と決める。実際に行った未使用field整理も「完全な無差分」と一括りにしない。

bodyが一致しても、応答の契約は一致したとは限らない

Section titled “bodyが一致しても、応答の契約は一致したとは限らない”

以下は上の検証範囲を可視化するAI一般例・演習上の追加条件X-Example-Context、値、JSONは全て架空で、当時のheader名・用途・生レスポンスを再現していない。

架空の旧新応答でstatusとbodyは一致するが新側のheaderが欠落。body比較の枠はその差を含まない。

図を拡大する

緑の範囲をどれだけ多く比較しても、枠の外の差は検出できない。ケース数を増やす判断と、比較する契約の範囲を増やす判断は別である。必要headerの値・伝播、status、異常系の契約を先に決め、その範囲に対応する比較を用意する。日時など変動するheaderまで全て文字列一致させるという意味ではない。HTTP Semantics / RFC 9110(2022年6月、2026-09-09再確認)。

既存の単一backend・fan-out図は、応答がどの経路を通るかの確認に使う。この図はその応答の何を比較したか、次のカナリア図は比較結果をいつ昇格・停止の判断へ使うかを担当する。事業指標はこの直接比較の代わりにせず、異常検知の補助として扱う。

事前検証からカナリアの判断へ

Section titled “事前検証からカナリアの判断へ”

図3:事前検証 → 1 Pod → 昇格/停止・rollbackJSON正本)。AI一般設計例であり、当時のリリース手順の再現ではない。図中のPASSとFAILは別の条件分岐で、連続して両方実行する意味ではない。

事前検証で契約・ケース・許容差・rollback手順を合意し、本番でしか検証できない条件を残件として明示する。1 Podでは対象requestが実際に新版へ到達し、必要headerが利用先まで届く等の直接証拠を取る。旧版の同時期の観測と比較し、SLI・resource使用も確認する。件数や観測時間が足りず判断できない場合は昇格を保留する。1 Podだから安全、または10 Pod中1 Podだから必ず10%のトラフィック、とは限らない。

各段階で条件を満たせば徐々に拡大し、契約違反や許容範囲を超える劣化があれば停止・rollbackする。戻した後も回復を確認する。副作用がある変更は旧版へ戻すだけで復旧できるかも事前に調べる。事業指標は補助的な異常シグナルとして併せて見るが、外部要因でも動くため、それだけで移行の因果や同等性を判定しない。Google SRE Workbook「Canarying Releases」(2018)は、比較対象・指標・代表性・判定に十分な観測を考える一次資料となる。

面接での説明例(AI作成、本人の確認済み事実に限定)

Section titled “面接での説明例(AI作成、本人の確認済み事実に限定)”

3人チームの一メンバーとしてBFF全APIをScalaからGoへ移行し、特に開発環境整備と互換性検証を主導しました。QAの目視だけでは全データ項目の差分を拾いにくいと考え、両実装に同じリクエストを送りJSONを比較するツールを発案・実装しました。既存の約200パターンを使い、時刻等を除外しながら比較して、シリアライズ差異を検出・修正できました。

一方でbody比較の対象外だったheaderの引き回し差異を見逃し、本番のカナリア中に問題が発覚してrollbackされました。私は該当実装やrollbackの担当ではありませんでしたが、レビューで確認を漏らした点を反省しています。

今なら最初に受け入れ条件と検証方法を合意し、bodyに加えてheader、status、異常系、timeout、性能、本番との差異まで整理します。カナリアでも何を確認したら次へ進むかを先に決めます。この経験を通じて重視しているのは、正しさをどう確かめるかを実装前に設計することです。

この回答案を読んだことは自力説明の証拠にしない。簡素化方針・並列化・partial failureの採用や障害対応を本人主導へ言い換えない。

補強元:2026-09-07-experience-bff-migration-01。一般化した問いは「境界を分ける理由は何か」「互換性をどこまで比較し、何を根拠に昇格するか」。上記一次資料は2026-09-07確認。設計記事は記載年版、HTTPはRFC 9110を対象とする。当時のScala・Go・Kubernetes・Argo CD・OpenAPIのバージョンはunknownであり、現在の特定バージョンの機能を経験へ補わない。

図表改善(2026-09-09):説明用SVGはSVG自体が正本。前提と判断は本文、状態・比較は図、通信順序・依存関係は既存図、実測は元の記録を参照する。教材編集を新しい学習実績にはしない。