どうも、朝七時を回りました。二〇二六年十月二日、金曜日の朝です。おはようございます、「zenncast」パーソナリティのマイクです。

さてこの番組では、エンジニアのみなさんの朝のお供に、Zennで話題になっているトレンド記事をゆるっと、でも中身はしっかりめにご紹介していきます。通勤・通学の支度をしながら、コーヒーを飲みながら、ゆったり聞いていってください。

今日はお便りの紹介はお休みで、そのぶんしっかり記事を追いかけていこうと思います。

今日ご紹介する記事は、全部で五本です。AIっぽい日本語を直すライティング術から、Terraform と Cloud Run のちょっと怖いロールバック話、AIエージェントの評価の考え方、テスト要求仕様の話、そしてヘキサゴナルアーキテクチャをAWSの公式サンプルで確認する記事まで、かなりバラエティ豊かなラインナップになっています。

それでは一本目からいきましょう。

。。


まず最初は、AIが書いたような不自然な日本語を直すスキル、「yomiyasu」という考え方とツールの紹介記事です。

テーマは「AI臭さをどうやって抜くか」という話なんですが、単に「この単語は禁止」「この表現は使うな」という表面的なNGワード集ではなくて、「誰が・何を・どうしたか」という、文の骨組みをもう一度ちゃんと復元することにフォーカスしています。さらに、「静かに壊れる」とか「〜に効く」みたいな、ふわっとした比喩的な動詞を、具体的な行動に言い換えていく、というのがポイントになっています。

筆者が面白いのは、Qiitaの七万件もの記事を分析して、「AI以後の文章」がどう変わったかを定量的に見に行っているところなんですね。その分析によると、太字とか箇条書きがどんどん増えている一方で、抽象的な比喩表現が多用されるようになっていて、しかも主語や動詞が抜けちゃって、名詞だけがずらっと並んだ文章が増えている、という傾向が見えたそうです。

これを筆者はある種の「病気」とみなしていて、「非生物主語プラス比喩動詞」とか、「名詞の詰め込み」、「『〜ではなく〜』という、でも中身がない対比」といったパターンを検出して、そこをきちんと直していく、というのが yomiyasu のアプローチです。

具体的な仕組みとしては、正規表現をうまく使いながら、前後の文脈も見つつ問題になりやすい比喩動詞だけを拾い出して、それを「人間が実際にやる作業」や「物理的な出来事」に言い換えるパイプラインを設計していると。つまり、一括置換ではなく、構造を見たうえでの変換ですね。

さらに面白いのが、文章のための静的チェックツールも同梱していて、それで文章を減点方式で採点できるようになっている点です。しかも、「AIに書かせるときのプロンプト」自体も、このチェックの対象にして、「お手本となる日本語」を保つことで、AI臭さを構造レベルから取り除こうとしている。プロンプトがAIくさかったら、出てくる文章もAIくさくなるよね、という発想です。

AIに頼る頻度が上がっているからこそ、「ちゃんと読める日本語に仕立て直す」スキルセットとして、yomiyasu みたいな考え方を取り入れていくと、技術記事を書くときにもかなり役立ちそうだな、という内容でした。

。。


続いて二本目は、TerraformでCloud Runを管理していたら、たった一つ、不要な環境変数を消して `terraform apply` しただけなのに、「一か月半前のコンテナイメージが本番にロールバックされてしまった」という、なかなか怖い事例の紹介です。

原因のキモになっているのは、Terraform の `ignore_changes` です。この記事のケースだと、Cloud Run の `template.containers[〇].image` に対して `ignore_changes` を設定していたんですね。これを「今動いているイメージをそのまま保ってくれるオプション」だとなんとなく思いがちなんですが、実際には「差分としては扱わない」だけであって、更新リクエストを送るときには、Terraform の state に残っている古いイメージの値が、`template` 一式と一緒に Cloud Run に送られてしまうという構造になっています。

その結果どうなるかというと、`template` の中に含まれる環境変数とかメモリ設定とか、そのどれか一個でも変えて `terraform apply` した瞬間に、state に残っている古いイメージを含んだ新しいリビジョンが、意図せず作られてしまう可能性があるんですね。しかもこれが厄介なのは、`ignore_changes` されている項目なので、`terraform plan` にはその差分が出てこない。レビューしていても、「危ない変更が入ってる」と気づきづらいという構造的な落とし穴になっています。

さらに記事では、Next.js のような、「先にポートを開けちゃうアプリケーション」を例にしていて、壊れたイメージでも起動チェックをなんとか通ってしまう可能性がある、と指摘しています。そうなると、本番が静かに古いコードで動き続けてしまって、「あれ、なんかバグ直したはずなのに直ってない?」みたいな状況が、じわっと発生しうるわけですね。

じゃあどうするか。筆者は一度、「もう Cloud Run は Terraform 管理から外してしまおうか」とも考えたそうなんですが、やっぱり宣言的な管理を完全に捨てたくはないということで、落としどころとして、「イメージタグをTerraformの変数として渡す」という方向を検討しています。具体的には、コミットハッシュなどのタグを Terraform の変数にして、GitHub Actions から `terraform apply -var="image_tag=..."` と渡してあげる。

こうすると、毎回のデプロイで Terraform の側も「このタグが本番で動くんだな」と最新の状態を認識できるので、state に古いイメージが残り続ける、という問題がだいぶ抑えられます。Terraform とデプロイをバラバラにせず、「一体として運用する」方が、「今本番でどのコードが動いているのか分からない」という状態を避けられるんじゃないか、という問題提起ですね。

Cloud Run や Terraform を使っている方はもちろん、「ignore_changes ってこういう動き方なんだ」という理解としても、かなり学びになる記事だと思います。

。。


三本目は、AIエージェントの評価の考え方を整理した、オピニオン寄りの解説記事です。特に、オブザーバビリティ、いわゆるメトリクスとかログとかを扱う系の AI エージェントをどう評価するか、という話にフォーカスしています。

ポイントは、「最終的な応答テキストだけを見ていても、『もっともらしいけど実は間違っている回答』を見抜くのは難しい」というところです。例えばダッシュボードから原因を推測するとき、途中でどんなツールを呼び出したのか、とか、どんなクエリを投げたのか、そのプロセス全体を見て評価しないと、本当にちゃんと原因にたどり着けているのか分からないよね、という問題意識があります。

そこで記事では、「シナリオ」と「グレーダー」を分けて設計しよう、という提案がされています。シナリオは、「現実のユーザーの仕事」と「成功条件」を書き下した仕様書のようなもの。たとえば、「このアラートが鳴ったときに、根本原因を特定して、解決策を提案してほしい」とか、そういう形です。一方でグレーダーは、保存してあるダッシュボードや、Prometheus のクエリ結果といった実データを使って、合否を判定する仕組みのことを指します。これをコードとして分離しておくことで、テストのメンテナンスもしやすくなる。

評価の中身もレイヤーを分けていて、まず「事実」、つまり正しいツールを使っているか、適切なクエリを投げているか、システムの状態の認識に間違いがないか、といった部分は、機械的なチェックで済ませる。ログの中にこのクエリがあるか、とかですね。一方で、「原因特定の説明が妥当か」とか、「提案された対応策が文脈として適切か」といった意味的なところは、ルーブリック付きの LLM 採点で評価していく、としています。

そして、この評価を一回ポンとやって終わりではなくて、「品質」「コスト」「レイテンシ」といった軸ごとに、複数回実行した結果から能力を比較していくことが重要だ、という話も出てきます。AI は一回ごとにばらつきがあるので、何度か回して統計的に見る、という発想ですね。

さらに記事で面白いのは、「AIが改善案を提案し、ベンチマークがそれを検証し、最終的に人間が採用するか決める」というループをぐるぐる回すべきだ、という主張です。その一方で、シナリオとグレーダーそのものも時間とともに劣化する、現実とズレていく、という前提を持っておいて、プロダクト本体と同じように継続的に保守していく必要があると。

記事では、この考え方を実際に形にした例として、「o一一y-bench」という仕組みが挙げられています。YAML でシナリオを書いて、その採点コードを用意しておくことで、さっきの「シナリオ+グレーダー」の世界を具体的に体験できるようになっている、という紹介ですね。

AIエージェントを本番で使うとき、「なんかいい感じにやってくれてるけど、本当に信用していいの?」というモヤモヤに対して、評価の枠組みからちゃんと考え直そう、という記事でした。

。。


四本目は、テストの話です。オピニオン記事なんですが、「テスト設計の前に、テスト要求仕様、略してTRSという文書を一枚挟むと、テスト設計がめちゃくちゃ楽になった」という体験談になっています。

従来のやり方だと、「確認項目」をテストを書く人の感覚でひたすら列挙していく、というスタイルになりがちで、対象の機能が大きくなればなるほど、「これって全体として何をカバーできてるんだっけ?」とか、「ここ、漏れてないかな?」というのが見えづらくなっていました。

そこで登場するのが TRS です。ここでは、ユーザーストーリーとユースケースを起点にして、「正常」「準正常」「例外」という三つの分類で振る舞いを整理していきます。そのうえで、受け入れ条件、いわゆる AC を Gherkin で書いていく。いっぽう、具体的な値、たとえば「このパラメータはAとBとCの三パターンがある」とか、そういうものは「因子」と「水準」の一覧にまとめて、別の表に集約しておくんですね。

こうやって構造化しておくことで、いざテストケースに落とすときに「どこで切るか」「どこまで組み合わせるか」で迷いにくくなりますし、「どの振る舞いを検討したのか」「あえてまだ検討していないのはどこか」が、文書上はっきり見えるようになります。また、仕様が固まっていないところは Open Issues として TRS の中で管理できるので、「この件まだ決まってないんですよ」と可視化しておけるのも利点です。

さらにこの記事では、このTRSの構造はそのまま AI エージェントへの入力に使える、と指摘しています。人間は AC、つまり「こういう振る舞いが期待される」という部分だけをきちんと書いておき、具体的なテストパターンの大量生成は AI に任せる。人間はそれをレビューする側に回ることで、テスト設計の生産性を上げていこう、というイメージですね。

ただし、そのときの大事なルールとして、「AI が実装コードを根拠に期待値を決めないようにする」ということも強く強調しています。期待値はあくまで仕様書とデザインだけを根拠にして、未定義なところは「未定義」として扱う。コードを見て「こう動いてるから、これが正なんだな」としてしまうと、仕様からのズレがあっても検知できなくなってしまうので、そこはラインを引こうという話です。

テストがカオスになりがちなプロジェクトほど、この「テスト要求仕様」という中間レイヤーを作る価値は大きそうだな、と感じさせる内容でした。

。。


そして最後、五本目は AWS Lambda と DynamoDB の公式サンプルを題材にして、ヘキサゴナルアーキテクチャを「概念」ではなく、実際のコード構造として確認していこう、という Tips 系の記事です。

ヘキサゴナルアーキテクチャって、言葉としてはよく聞くんだけど、「結局コードではどう分ければいいの?」というところがふわっとしがちですよね。この記事では、具体的なサンプルアプリを通して、その分け方を丁寧に見ていきます。

まず、ドメイン、つまり業務ルールは `Recipient` みたいな素のクラスで表現して、インポートも同じドメイン層の中だけにとどめます。ここには AWS SDK とか HTTP とか、外側の技術要素はいっさい登場しません。とにかく「業務のルールだけが書いてある世界」を作る。

次にポート。これは抽象クラスとして定義しておいて、その引数や戻り値には `Recipient` や `Status` のような業務で使う型だけを登場させます。DynamoDB のアイテムを表す辞書型だとか、API Gateway の event 型みたいな、お作法的な型はポートには出さない。つまり、「技術の都合を中に持ち込まない」ようにする設計ですね。

ユースケースの層は、「データを取得して → ドメインの業務ルールを呼び出して → 保存する」という手順に徹します。どこに保存するかはコンストラクタで受け取るインターフェース、つまりポートの実装に隠しておく。業務ルールそのものはあくまでドメイン側に任せる、という役割の分担になっています。

被駆動アダプター、たとえば DynamoDB のリポジトリクラスは、このポートを実装して、裏側で `boto三` を使いながら、DynamoDB のアイテムとドメインオブジェクトを双方向に変換する「翻訳役」として完結させます。ここは外の世界と中の世界をつなぐトランスレーター、という感じですね。

一方で、駆動アダプター、Lambda のハンドラーなんかは、HTTP のリクエストとレスポンスを `make_reservation` のようなポートの呼び出しに変換するだけの薄いコードにします。ここには業務ロジックは一切書かない。リクエストをパースして、ポートを呼んで、結果を HTTP っぽいレスポンスにラップし直すだけ、という役割に絞ります。

そして、内側のドメイン・ユースケースと、外側のアダプター群を具体的なクラスで結びつける「組み立て処理」、いわゆる Composition Root を一か所にまとめます。ここだけが「どのDBを使うか」「どのアダプターでつなぐか」を知っている場所。DynamoDB から RDB に変えたくなったら、この部分だけを書き換えればよい、というのが狙いですね。

テストの話も出てきます。テストでは、DynamoDB の代わりにポートを実装したダミークラスを差し込むことで、ネットワークなしでユースケースやドメインをテストできます。つまり、テストもまた一種のアダプターとして扱える、ということをコードレベルで示してくれています。

この記事の前半では、ここまでの構造を実際に SAM を使ってデプロイして、動かして確認するところまでをやっています。そして後半の記事では、「保存先のDBを変える」「入口を追加する」「同じ枠の二重予約を防ぐ」といった、現実にありがちな仕様変更が、この構造だとどれくらい楽になるのか、という検証をやる予告で締めくくられています。

ヘキサゴナルアーキテクチャ気になっていたけど、コードでイメージがついてないな、という方には、すごく実践的なガイドになりそうな内容でした。

。。


というわけで、今日の「zenncast」、駆け足でおさらいしていきます。

まず一つ目は、AIっぽい不自然な日本語を直すスキル「yomiyasu」。主語と動詞を取り戻して、比喩的な動詞を具体的な行動に言い換えつつ、静的チェックでプロンプトまで含めてAI臭さを取っていく、というお話でした。

二つ目は、Terraform で Cloud Run を管理していたら、`ignore_changes` が原因で一か月半前のコンテナイメージに静かにロールバックされてしまった事例。イメージタグを変数で渡して、Terraform とデプロイを一体運用するほうが安全では、という問題提起でした。

三つ目は、AIエージェント評価の考え方。シナリオとグレーダーを分けて、ツール呼び出しのプロセスを含めて評価しつつ、事実と意味的な妥当性を別レイヤーでチェックしていこう、という話。o一一y-bench という実例も紹介されていました。

四つ目は、テスト設計の前にテスト要求仕様、TRS を挟むことで、US/UCと振る舞い、AC、因子と水準を整理し、網羅性と未決事項を見える化するという話。TRSはAIエージェントへの入力にもなりうるが、期待値は仕様とデザインだけを根拠にする、というルールが大事だという内容でした。

そして五つ目は、AWS Lambda と DynamoDB の公式サンプルを使って、ヘキサゴナルアーキテクチャをコードで確認する記事。ドメイン、ポート、ユースケース、アダプター、Composition Root、そしてテストをアダプターとして扱う、というきれいな分離を、実際の構造で学べる内容でした。

気になった記事があれば、詳しいリンクやキーワードはショーノートにまとめてありますので、そちらからぜひ元記事をチェックしてみてください。

この番組「zenncast」では、みなさんからの感想や、「こういうテーマも取り上げてほしい」といったリクエストもお待ちしています。番組の感想やフィードバックは、ぜひフォームやコメント欄から送ってください。マイクがありがたく読ませていただきます。

それでは、そろそろお時間です。今日も一日、いいコードといい文章に出会えますように。お相手はマイクでした。また次回の「zenncast」でお会いしましょう。いってらっしゃい。

Related episodes

内容の近いエピソードを推薦しています