ソリューションアーキテクト、17年の経験。2026年3月頃から7月頃まで、湾岸諸国の国家港湾コミュニティシステムのアイデンティティおよびシングルサインオン層で唯一の実務担当開発者でした — 同システムは港湾および物流セクターのフロントエンドです。すでに本番環境で稼働中です。
「単独」という表現を正確にしておきたい理由があります:他の人も両リポジトリにコミットしています — CI、自動テスト、いくつかの修正、下流統合作業などです。私がバックエンドコミットの約80%、フロントエンドの約70%を執筆しました。コアシステムに対する実装レベルの機能開発は他に誰も行っていません。それが事実です。
git log --numstat から得られたボリューム:
- 602コミット、1,423ファイルで約184,000行の変更
- バックエンド:383のJavaファイル / 約21k LOC、19コントローラー、83 RESTエンドポイント
- フロントエンド:152のTS/HTMLファイル / 約15.3k LOC、62 Angularコンポーネント
- 6つの外部統合、それぞれに本番用とモックアダプターを用意
- 4環境、2回の独立したペネトレーションテストを修正済み
論争になる前、4月にリーダーシップへ提出した私の見積もり:AIツールなしでは3〜4人のエンジニアが3ヶ月必要。
アーキテクチャ
Hexagonal(ポートとアダプター)+CQRS。ドメインパッケージにはSpringやJPAのimportが一切ありません — 検索可能で、何も返しません。これが、hexagonalが設計判断かバズワードかを測る本当のテストであり、ほとんどのコードベースがこのチェックに失敗します。
domain/ 純粋なドメイン — モデル、値オブジェクト、イベント、ポート。フレームワークのimportなし。
application/ ユースケース — コマンド(書き込み)とクエリ(読み取り)、DTO、アセンブラー。
dataprovider/ アダプター — JPAエンティティ、Spring Dataリポジトリ、SOAPクライアント。
web/ インバウンドアダプター — コントローラー、DTO、マッパー、JWTフィルター、SAMLハンドラー。
全画面表示にする 全画面表示を終了
すべての機能は、トランザクション書き込み用のXxxCommand/XxxCommandImplと読み取り専用のXxxQuery/XxxQueryImplのペアになっています。すべての外部依存はポートの背後に配置され、Springプロファイルで選択される本番用およびモックアダプターを持ちます — これにより、政府システムが稼働していなくてもQAとUATで完全なエンドツーエンドフローを実行できました。実際、稼働していないことが頻繁にありました。
盗用に値するJPAの詳細:@Versionを使用した楽観的ロックとfind-then-update保存パターン。これはSpring Dataの「バージョンがnull=新規エンティティ」という振る舞いによる重複キーエラーを避けるためのものです。間違えやすく、デバッグも苦痛です。
ユーザーライフサイクルは実際のステートマシン(PENDING_VERIFICATION → ACTIVE → LOCKED/INACTIVE)でStateパターンを使用しているため、無効な遷移はUIに散らばったif文ではなく、ドメインレイヤーで失敗します。
読む価値のある4つの問題
1. JavaのX.509パーサーが政府証明書を拒否した
AVA not a sequence — JDKの厳格なパーサーが完全に拒否する、奇形だが実世界では一般的なエンコーディングです。BouncyCastleのX.509ファクトリで手動のメタデータパーサーを書きました。
その後、省庁が本番環境で署名証明書をローテーションしたところ、システムがダウンしました。そこで証明書を障害時リフレッシュでホットリロード可能にしました。20日後にまたローテーションされましたが、今度は誰にもページングせずに自己修復しました。
2. ネットワークのせいにされた断続的な500エラー
テスターはインド、サーバーはオマーンにあり、「レイテンシが原因」というのが即座のコンセンサスでした。
代わりに私が調べました。設定されたタイムアウトは60秒でした。実際のインド〜オマーン間のRTTは100〜250ms — 約240分の1です。深刻な劣化でも60秒の遅延は発生しません。また、500または503はサーバーが接続を受け入れた後に生成されるため、定義上ネットワーク経路の問題ではありません。
政府のAPIが単に不安定だったのです。この結論により、幻のネットワーク問題を何週間も追いかける代わりに、リトライとバックオフを導入できました。
同じ統合に埋もれていた問題:同APIは有効な労働許可証に対して"WORKING"を返し、"Active"ではありませんでした。この文字列の不一致により、すべての実ユーザーの登録がサイレントにブロックされました。
3. 決済で競合する可能性のある3つのクロック
10分の請求書有効期限、約2分のフロントエンドポーリングウィンドウ、60秒のバックエンド照合ジョブ。遅い3-D Secure確認は、決済が成功した場合でもフロントエンドの忍耐時間を超える可能性がありました。
修正は、ブラウザが開いているかどうかに依存しない安全網として2つのバックエンドスケジューラを導入し、2つの独立した最終化パス — 銀行のサーバー間コールバック(権威的で、ユーザーがタブを閉じても動作する)とSPAのポーリング(フォールバック) — を単一のべき等性ガードに集約し、どのパスが勝っても下流同期がちょうど1回だけ発火するようにしました。
関連する、より単純な問題:ブラウザから銀行の決済エンドポイントへ直接GETすると、POSTボディがサイレントに破棄されました。修正は、ブラウザがそのエンドポイントを一切GETしないように、バックエンドでレンダリングした自動送信フォームを使用することでした。
4. ChromeがXHRのTLS再ネゴシエーションをブロックし、ナビゲーションではブロックしない
すべてのAPIコールがnet::ERR_FAILEDで失敗する一方、SAMLリダイレクトは完全に動作していました。
この非対称性が問題を高価にした理由です — 認証リダイレクトは成功し、その後のすべてのAPIコールが失敗するシステムは、バックエンドの認証バグとまったく同じに見えます。実際はインフラ設定の問題でした。
誰も警告してくれない手戻り
両リポジトリで16回の異なるrevert-and-rebuildサイクルがありました。特にお気に入りの2つ:
- 下流のトークン交換ハンドシェイクは、ある午後の間に4つの異なるプロトコルを経由しました — two-step、service-JWT bearer、app_secret bearer、no auth header — その後数日かけてさらに2回revertされ、最終的にsingle-step exchangeに落ち着きました。
- ビジネスライン正規化ルール:4つのPRで4つの戦略を1日で構築し、すべて同日にrevert。相手方がAPIが保存されている値をそのまま欲しいことを確認した後、
mainから完全に削除し、2週間後に3つ目の方法で再構築しました。
これらはAIのせいでも私のせいでもありません。要件がまだ固まっていないチームと統合すると起こることです。
Claudeが実際に役立った点と役立たなかった点
コードの大半を書いたのはClaudeです。足場だけでなく、アダプターペア、数十の機能にわたるCQRSコマンドとクエリの実装、JPAマッピング規約、Angularコンポーネント、統合クライアントの大部分などです。この圧縮こそが、1人で3〜4人分の範囲をカバーできた理由です。
上記の4つの問題はどれも発見しませんでした。どれも「何を疑うべきか」を知ることから始まりました。証明書の失敗は一般的なSAMLエラーとして現れました。500エラーは部屋中の全員が自信を持って誤診しました。クロック競合はテストに一切現れませんでした。TLSバグは認証問題として提示されました。
正しく特定していない問題の修正をプロンプトで導き出すことはできません — すでに解釈した症状を記述するだけで、解釈こそが仕事なのです。
誰も言及しないもう一つの側面:5ヶ月間にわたってモデルの使用制限に何度もヒットしました。レートリミットは実際のスケジュール制約であり、脚注ではありませんでした。
違ったやり方をするだろうこと
テストカバレッジはバックエンドで約23%の命令カバレッジです。これは低く、取り繕うつもりはありません。2人目のエンジニアがいれば、自動カバレッジへの投資をもっと早く行い、下流統合プロトコルを実装前に誰かにレビューしてもらっていたでしょう。4回のライブ手戻りを通じて不整合を発見するのではなく。
詳細についてさらに深く掘り下げることも可能です。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.