
KiroとAI-DLCでリバースエンジニアリング、Vibe実装のLambdaを改善してみた
はじめに
Vibeコーディングとは、AIにざっくり意図を伝え、生成されたコードを細かく精査せずに受け入れながら進める開発スタイルです。Andrej Karpathy氏がXで言及したことで広まりました。このスタイルで作ったコードには共通の課題があります。動いてはいるが、仕様書もテストもなく、変更に不安がある。今回扱うのはまさにそういうLambda関数でした。
対象システムはS3に出力されるログファイルをイベントで検知し、内容を社内Slackに通知する仕組みです。実体はCloudTrailとFirehoseでログ集約された結果がS3に出力される構成ですが、業務上クリティカルではない社内通知用途のため、今回の記事ではアーキテクチャ詳細には踏み込みません。
S3イベント → EventBridge → Lambda → SNS → Chatbot → Slack
^^^^^^^^
今回の改善対象
今回、Kiro CLIでAI-DLC逆解析ワークフローを実行し、CloudFormation(CFn)テンプレートとLambdaコードを入力として現状仕様のドキュメント化を行いました。日本語で指示したため、ドキュメントも日本語で生成されています。コンポーネント一覧・依存関係・品質評価など10種類のドキュメントが生成されました。
逆解析の結果を踏まえた改善内容を以下の表にまとめました。
| 項目 | 変更前 | 変更後 |
|---|---|---|
| テスト | なし | pytest 14ケース |
| アーキテクチャ | x86_64 | arm64(料金単価が約20%安い) |
| ランタイム | Python 3.11 | Python 3.13 |
| デプロイ方式 | CFn ZipFile埋め込み | S3バケット参照 |
| 保守用ドキュメント | なし | AI-DLCによる現状仕様ドキュメント |
変更の不安解消のため、テスト導入を起点にテスト可能な構成へ寄せていった結果がこの表です。
AI-DLC逆解析で何が見えたか
今回の実行で生成された10種類のドキュメントから代表的なものを紹介します。
inception/reverse-engineering/s3-lambda-notifier/
├── components.md # コンポーネント一覧
├── component-methods.md # メソッド・関数の詳細
├── component-dependencies.md # 依存関係・データフロー
├── services.md # E2Eワークフロー
├── cross-cutting.md # 横断的関心事(エラーハンドリング、ロギング等)
├── event-catalog.md # イベント定義
├── external-dependencies.md # 外部依存(他サービス・バケット等)
├── technology-stack.md # 技術スタック
├── code-structure.md # コード構造・命名規約
└── code-quality-assessment.md # 品質評価・技術的負債リスト
たとえば components.md では、各コンポーネントが以下の粒度で整理されます(Lambda部分の抜粋)。
| 項目 | 内容 |
|---|---|
| 名前 | LogProcessor |
| 目的 | Firehoseが配信したCloudTrailログ(gzip JSONL)をパースし、S3オブジェクト操作をグルーピングしてSNS経由でSlack通知する |
| 責務 | gzip解凍、JSONL解析、バケット名/イベント名/IAM発行者によるグルーピング、SNSメッセージフォーマット、SNS送信 |
| 状態 | Stateless |
| ソースファイル | cfn/s3-lambda-notifier.yaml, lambda/index.py |
component-dependencies.md では、コンポーネント間のデータフローが以下のように可視化されます。
監視対象S3バケット
│ (S3データイベント: WriteOnly)
▼
CloudTrail
│ (AWS API Call via CloudTrail)
▼
EventBridge Rule
│ InputTransformer: 12フィールドに圧縮
▼
Kinesis Data Firehose
│ バッファ: 60秒/64MB, GZIP圧縮
│ 動的パーティション
▼
S3 (ログバケット)
│ (Object Created, suffix: .gz)
▼
EventBridge Rule
│
▼
Lambda (LogProcessor)
│ S3 GetObject → parse → group → format
▼
SNS Topic → AWS Chatbot → Slack
今回の構成では、CFnテンプレートとLambdaコードを入力するだけで、ここまでのフロー図が生成されました。約170行のLambdaでも、目的・責務・データフローの全体像が明文化されることで、「このシステムは何をしているか」が即座に把握できます。
特に品質評価ドキュメント(code-quality-assessment.md)が有用でした。「現状の制約」として以下が指摘されています(抜粋)。
- テスト不在: ユニットテスト・統合テストが存在しない。parse_and_group_logの複雑なロジックが未検証
- ZipFileコードの二重管理: lambda/index.py と CFnテンプレート内のZipFileが同一内容だが同期メカニズムなし
- トランケーション位置: 文字数カットが行境界を考慮しない(オブジェクトキーの途中で切断される可能性)
漠然と「テストがないから怖い」と感じていたものが、具体的なリスクとして言語化されました。AI-DLCが出したのは「現状把握・品質評価」であり、何をどう直すかは人間が判断します。
生成されたドキュメントを見て、今回はすべてを直すのではなく、変更時の不安を減らすために「テスト導入」を起点に改善することにしました。
改善のストーリー
改善項目は個別のチェックリストではなく、一本の因果の連鎖で繋がっています。
変更に不安がある(仕様もテストもない)
→ まずテストを入れたい
→ 継続的にテストしやすくするには、Lambdaコードを通常のソースファイルとして管理したい
→ ZipFile(CFn埋め込み)を卒業してS3デプロイへ
→ テストが揃ったのでPython 3.13化を安全に実施
+ ついでにarm64化も実施(開発環境がARM / 料金単価が安い / テストで動作確認済み)
ZipFile埋め込みには「短いコードならデプロイが楽」という利点があります。ただし、pytestで継続的に検証するにはLambdaコードを独立したファイルとして管理する方が扱いやすいため、テスト導入を動機にS3デプロイへ移行しました。arm64化は、料金単価が約20%安いことと、開発環境と実行環境のアーキテクチャを揃えたかったことから同時に実施しています。
改善箇所: エラーを握り潰さない
品質評価で指摘された「エラーハンドリングの粒度」に対応した改善例を示します。
改善前は parse_and_group_log 内でS3 GetObject失敗時にtry-exceptでキャッチし、空の結果を返していました。
# 改善前: エラーを握り潰し、空の結果で正常終了
def parse_and_group_log(log_bucket, object_key, target_app_bucket):
grouped_events = defaultdict(list)
processed_event_count = 0
try:
response = s3_client.get_object(Bucket=log_bucket, Key=object_key)
# ... 解析処理 ...
except Exception as e:
logger.error(f"Failed to get object: {e}")
return grouped_events, processed_event_count
改善後はtry-exceptを外し、例外をそのままraiseさせています。Lambdaの失敗として表面化し、EventBridgeのリトライに委ねる設計です。Slack通知の遅延は許容できるため、一時的失敗はリトライで吸収する方針で十分と判断しました。
# 改善後: S3取得失敗時は例外をraise(一時的エラー扱い)
def parse_and_group_log(log_bucket, object_key, target_app_bucket):
"""S3からgzip JSONLを取得・解析しグルーピング。S3取得失敗時は例外をraise(一時的エラー)"""
grouped_events = defaultdict(list)
processed_event_count = 0
response = s3_client.get_object(Bucket=log_bucket, Key=object_key)
with gzip.GzipFile(fileobj=io.BytesIO(response['Body'].read())) as f:
content = f.read().decode('utf-8')
# ... 解析処理 ...
この設計判断が正しく機能することを確認するテストを対で書きました。
@patch('index.s3_client')
def test_s3_error_raises(self, mock_s3):
from botocore.exceptions import ClientError
mock_s3.get_object.side_effect = ClientError(
{'Error': {'Code': '500', 'Message': 'Internal'}}, 'GetObject')
with pytest.raises(ClientError):
parse_and_group_log("log-bucket", "key.gz", "my-bucket")
テストがあることで「S3取得エラーを握り潰さず、Lambdaの失敗として上位に伝播する」振る舞いが保証されます。これにより、非同期実行のリトライ対象にできます。
14ケースの内訳は、パス抽出・ログ解析のグルーピング・フィルタリング・不正JSON行のスキップといった正常系・異常系が中心です。加えて、通知メッセージのトランケーション境界やLambdaハンドラのエラー伝播も検証しています。テストコードはKiro CLIにindex.pyを渡して生成し、内容を確認・調整しました。
結果
所要時間は約40分でした。内訳は逆解析に約20分、改善設計・実装・テスト・デプロイに約20分です。対象コードが約170行と小さく、テスト生成もAI支援で並行実施したことが短時間で済んだ要因です。比較表に挙げた変更はすべて完了し、現状仕様ドキュメント一式も手元に残りました。
今後の変更時に参照できる現状仕様とテストが揃い、「仕様を先に更新→差分を実装」という流れに移行しやすい状態になりました。
まとめ
Vibeコーディングで作ったコードの最大の負債は「仕様がないこと」です。コードは動いていても、なぜそう書いたのか・どこまでの入力を想定しているのかが不明なため、変更のたびに全体を読み直す必要があります。AI-DLCの逆解析で現状仕様を可視化すれば、安全に改善するための土台ができます。約40分という所要時間は環境依存(Kiro CLI + claude-opus-4.6)ですが、「逆解析で現状を把握→テスト導入を起点に改善」というプロセス自体は他のVibe実装にも適用できると考えています。
参考リンク









