メインコンテンツにスキップ

template.yamlのNested構成で大失敗!理想と現実のギャップから僕が学んだこと

タグ: 🏷 AWS ,SAM ,IaC

背景

「template.yamlが長すぎて管理が大変…」サーバーレスプロジェクトが大きくなると、誰もが一度はぶつかる壁ですよね。僕たちのプロジェクトでも、11個のLambda関数と関連リソースでtemplate.yamlが800行を超え、保守性の問題が深刻になっていました。

template.yamlとは? AWS SAM (Serverless Application Model) で使う、インフラの「設計図」ファイルです。「Lambda関数を5つ、データベースを1つ、API Gatewayを1つ作る」といった情報をこのYAMLファイルに記述すると、AWSが自動でその通りの環境を構築してくれます。

この長大な設計図をなんとか管理しやすくしようと、Nested Stack(ネステッドスタック) という機能を使ってファイルを機能ごとに分割してみることにしました。ところが、この試みが想定以上の複雑さを招いてしまい、最終的には元の単一ファイル構成に戻す、という苦渋の決断を下すことになったんです。

この記事では、なぜベストプラクティスとされる手法で僕たちが挫折してしまったのか、そしてこの失敗からどんな教訓を得られたのかを、詳しく解説していきたいと思います。

問題の背景:800行を超える巨大な設計図

僕たちのtemplate.yamlは、機能を追加するたびに追記され、気づけば800行を超える巨大なファイルになっていました。その結果、以下のような問題が次々と発生していたんです。

  • 可読性の低下: ファイルが長すぎて、どこに何が書いてあるか探すのが大変。
  • コンフリクト頻発: 複数人で同時に編集すると、変更箇所が衝突しやすい。
  • 影響範囲が不明: 一部を変更したくても、どこまで影響が及ぶか分からず、修正が怖い。

理想の解決策:Nested Stackによるファイルの分割

この問題を解決するため、template.yamlを機能ごとに小さなファイルに分割する「Nested Stack」の導入を決めました。

Nested Stackとは? 巨大な1冊の報告書を、「第1章」「第2章」…と章ごとに別のファイルに分割するようなものです。ルートとなる目次ファイル(template.yaml)から、各章のファイル(cognito.yaml, lambda.yamlなど)を呼び出すことで、全体を構成します。一見すると、非常に整理されて管理しやすくなるように思えます。

僕たちは、認証、データベース、各Lambdaグループといった単位でファイルを分割する計画を立ててみました。

実装過程で発生した「理想と現実のギャップ」

しかし、実際に分割作業を進めると、多くの想定外の問題に直面しました。

問題1: デプロイが複雑になった

単一ファイルならsam deployコマンド一発で済んでいたデプロイが、Nested Stackでは「分割したファイルをS3(AWSのストレージサービス)にアップロードする」という事前準備が毎回必要になり、デプロイの手順が格段に増えてしまったんです。

慣れていないメンバーによるデプロイミスで、必要なLambda関数がデプロイされずにエラーになる、なんてことが頻繁に起こるようになりました。

問題2: ファイル間の依存関係が複雑すぎた

分割したファイル同士が、互いの情報を参照し合う必要がありました。

循環依存とは? 分割した報告書に例えると、「第1章の詳細は、第2章を参照」と書いてあるのに、その「第2章の詳細は、第1章を参照」と書いてあるような状態です。これでは、どちらも完成させることができません。私たちの設定ファイルも、cognito.yamllambda.yamlの情報を、lambda.yamlcognito.yamlの情報を必要とする「循環依存」に陥ってしまいました。

問題3: 開発・デバッグ効率が悪化した

  • ローカルテストの非対応: sam localコマンドがNested Stackに完全対応しておらず、ローカルでの動作確認が困難になりました。
  • エラー調査の困難さ: エラーが発生した際、単一ファイルならすぐに原因箇所を特定できたのですが、分割後は「どのファイルの、どの連携部分が原因なのか」を突き止めるのに、以前の4〜6倍もの時間がかかるようになってしまったんです。

問題4: 設定情報の「バケツリレー」地獄

分割したファイル間で情報を受け渡すため、大量のParameters(入力)とOutputs(出力)を定義する必要がありました。これにより、設定の総数が元の3倍以上に膨れ上がり、管理が非常に煩雑になりました。

このバケツリレーは本当に地獄で、いつになったら終わるのか、まったく先が見えませんでした。

断念の決定要因と最終判断

僕たちは、Nested Stackのメリット(ファイルの分割)よりも、それによって生じるデメリット(複雑さ、効率の悪化)の方がはるかに大きい、と判断せざるを得ませんでした。

  • デプロイ時間: 4分 → 16分(4倍に悪化)
  • エラー調査時間: 5分 → 25分(5倍に悪化)
  • 月間の開発ロス: 約35時間(約1週間分)の工数が、この複雑さの対応のために失われる計算に。

結論: 僕たちのプロジェクト規模では、Nested Stackは明らかにやりすぎだった、という結論に至りました。

僕たちはこのアプローチを断念し、元の単一ファイル構成に戻すことを決断しました。

元の構成への復帰とその効果

Nested Stackの経験から学んだことを活かし、単一のtemplate.yamlを以前より分かりやすく整理しました。

  • コメント区切り: === Cognito Resources === のような大きなコメントで見出しをつけ、セクションを分かりやすくしました。
  • 共通設定の活用: Globalsセクションを使い、各Lambda関数で共通の設定をまとめることで、全体の行数を26%削減できました。

結果として、開発効率は劇的に改善し、チームのストレスも大幅に軽減されたんです。

それでもtemplate.yamlの行数は約800行

ただ、解決したい課題は残っています。

関数を増やしたりして、template.yamlは約800行あります。新規参画者や、久しぶりに参照するであろう未来の自分は、この行数にきっとゲンナリするでしょうね。

割り切れるポイントがあるかを探していこうと思ってます。

学んだ教訓と汎用的な指針

  • 技術選択はプロジェクトの規模に合わせる: Nested Stackは、100以上のリソースや複数の専門チームが関わるような、非常に大規模なプロジェクトで真価を発揮する技術です。私たちの規模では、単一ファイルの方がシンプルで効率的でした。
  • 分割以外の整理方法もある: ファイルを物理的に分割しなくても、コメントや共通設定の活用で、単一ファイルの可読性や保守性は十分に高められます。
  • 失敗から学び、迅速に方向転換する: 「良いとされている方法」が自分たちには合わないと分かったら、勇気を持って元に戻す判断も、とても大切だと痛感しました。

この経験を通じて、**「技術はあくまで手段であり、目的はプロジェクトを成功させること」**という本質を、僕たちは改めて深く学ぶことができたんです。


関連リソース:

実現された効果:

  • 🚫 複雑性削減: デプロイ時間75%短縮(16分→4分)
  • 🔧 運用効率: エラー調査80%短縮(25分→5分)
  • 👥 チーム生産性: 月間35時間の工数削減
  • 📈 安定性向上: デプロイ失敗率87%改善(15%→2%)