個人ブログ基盤を AI にゼロから作らせたら何に躓いたか ― Astro × DDD × TDD 検証ノート
外部発信を再開するにあたり、最初に突き当たった問いが「ブログ基盤をどこまで AI に任せられるのか」だった。Astro 公式の starter を clone してテーマを当てれば数時間で公開できる、という前提を一度脇に置き、Claude Code に要件定義から作らせて「AI が自律的に開発・運用する前提で設計された個人サイト」を手元で育ててみたかった。長期的なコスト構造として未知の領域だったためである。
進めた作業自体は成功と失敗が混ざった。本稿はその失敗も含めて、時系列で何が起きたかを残す。先行記事 Cursor Agent を利用したリファクタリングの試み(失敗編) が「AI に既存コードを触らせると何が壊れるか」の検証だったのに対し、こちらは「AI に要件から作らせると何が甘くなるか」の検証にあたる。
作業環境は MacBook Pro m4 と Claude Code(モデルは Claude Opus 最新)。併用ツールとして codex-ask で設計レビューを回す。成果物は public な astro-blog と private な astro-blog-content の 2 リポで、ホスティングは Cloudflare Pages を想定している。
最初に投げたプロンプト
astro https://github.com/withastro/astro を用いて、個人事業主(swe)としての情報発信を行いたい。
ブログ基盤 と コンテンツ を分離した モノレポ で構築する。
ブログ基盤は astro, ts, react で構築し、feature-first の ddd, tdd アーキテクチャで構成する。
コンテンツは フロントマター付きの markdown で管理し、 github markdown 規格をサポートしたい。
最新バージョンを利用する。
まずは要件を詳細に固めて、フィジビリティ検証をして。
astro開発に便利なmcpやスキルがあれば調べて。
aiが自律的に開発できるように、claude rulesで詳細なルールを定めておきたい。
この時点では 2 つの内心があった。期待としては「Astro のような比較的新しい領域でも、公式知識を持つ AI なら設計の叩き台を即出せるはず」、不安としては「『個人事業主レベル』『一人保守』といった非機能要件を明示しないと、企業向けの過剰設計が返ってくるのでは」という懸念。結論を先取りすれば、不安はそのまま的中した。
AI に要件定義を投げたら、エンタープライズ設計が返ってきた
1 回目の返答は技術的には網羅的で、Content Collections のスキーマには著者管理・タグのタクソノミー・関連記事算出まで組み込まれていた。機能として間違いではないが、記事 20 本規模の個人ブログで維持する価値があるかは別問題である。
要件ドラフトには「想定規模」「運用者数」「更新頻度」「失敗時のコスト」といった運用パラメータの仮定が暗黙のうちに織り込まれる。その仮定を先に明示しないと、ドラフトはエンタープライズ前提に寄る。この一発目の齟齬を見て、「叩き台を 1 往復で完成させる」発想を早々に捨てた。
以降は、AI に粗い叩き台を出させ、「運用者の時間コスト」「規模感」「非機能制約」を追補プロンプトで差し込みながら絞り込むやり方に切り替えた。実際に投げた追補プロンプトの一部を残す。
codex ask で、設計レビューさせて。個人事業主レベルなので、その辺りは意識してこのアーキテクチャは、単なるブログにとどまらず、自己紹介などのコンテンツも配置できるよう拡張可能かsite を public リポジトリ、 content を private リポジトリにすることは可能か機能一覧を教えてシェアボタンは後で良い。その他はほしい
「個人事業主レベル」という制約語をトピックが変わるたびに差し込み直したのが、振り返るとコツだった。一度言えば AI が記憶するように見えても、次の話に移ると制約は往々にして忘れられる。リマインドを躊躇わない方が結果は良い。
興味深かった弱点
AI が自発的に言及しなかった設計要素で、印象に残ったのは「書き手本人の時間コスト」である。CI の整備時間、ビルド時間、将来の Astro バージョンアップ対応コスト ― 聞けば答えてくれるが、ドラフト段階では出てこない。AI は技術的な完成度には敏感だが、運用者の稼働時間には鈍感だ、という肌感覚が得られた。
ここに Codex を噛ませて逆を取った。codex ask で設計をレビューさせると、「個人事業主レベルでは DDD の Aggregate まで切るのは冗長」のような踏み込んだ反論が返ってくる。Claude と Codex は学習分布の重心が微妙に違うはずで、片方が踏み込み不足のときにもう片方が補完しやすい。同じモデルの多数決より、異なるモデル間のピアレビューの方がレビューとしての信号が強い、という経験的な感触を得た。この観察は別記事 Codex CLI は 1 Skill にまとめるな で、Skill 分離設計の発想として再利用している。
一方で AI ピアレビューが弱い論点もあった。「将来の執筆継続コスト」― この frontmatter スキーマで 300 本の記事を書いたあと後悔しないか ― のような時系列で効いてくる非機能要件への感度は、Claude も Codex も両方低い。これは人間側が踏ん張るしかない、というのが今のところの結論である。
DDD と TDD はブログで空回りした
依頼文で「feature-first の DDD + TDD」を指定したが、書き進めてすぐにブログ基盤に DDD はほぼ過剰だと実感した。
DDD が効くのは「ビジネスルールがコードを支配する」領域である。ブログは記事の表示・フィルタ・公開制御が本体で、データ変換と表示ロジックがほぼすべて。Aggregate や Entity の概念を持ち込んでも層構造が空回りし、ユースケース層が「Content Collection から引いてきて View に渡す」だけの薄いラッパーに成り果てた。DDD の用語だけ使ったディレクトリ構成、中身は CRUD ベースの MVC 相当、という残念な状態になりかけた。
完全に捨てるのも極端だと思い、「境界を切る」部分だけ残すことにした。feature-first のディレクトリ分割は維持して posts / tags / authors / rss の関心ごとに閉じる。ドメインレイヤには薄いエンティティ(Post, Tag)だけ定義し、Aggregate Root や Entity のライフサイクルは持ち込まない。Repository は Astro Content Collections に丸投げして、自前の Repository クラスは書かない。
こうすると DDD の語彙と境界の切り方だけ残り、階層の重さは捨てられる。「形だけ輸入、重量は捨てる」は個人事業主スケールでは素直な落とし所だった。
TDD は DDD よりだいぶ素直に役立った。frontmatter のスキーマ検証・タグの正規化・RSS 生成のようなピュアな変換ロジックは、テストを先に書いて Claude Code にグリーンにさせる手順がそのまま効く。ただし UI コンポーネントの振る舞いテストはコスパが悪く、書かないと割り切った。ここも「TDD を全適用しない、変換ロジックだけに絞る」という部分輸入である。設計手法を丸ごと鵜呑みにしなかった判断は、今のところ後悔していない。
2 リポジトリ分離は「心理の問題」だった
次の迷いは、全部を public な単一リポジトリに置くか、content を private に切り分けるかだった。
単一リポジトリで下書きを drafts/ のようなディレクトリに隠す案も検討した。シンプルだが、push 前に「これ公開しても問題ないか」を毎回気にすることになる。公開スケジュールや読者分析用の設定を置く場所にも困る。コードとコンテンツが同居すると、ポートフォリオとして見せる public repo のノイズが増える。この 3 点で却下した。
採用したのは 2 リポ + submodule 構成。astro-blog (public) に Astro 本体・テーマ・コンポーネント・ビルド設定を置き、astro-blog-content (private) に記事・固定ページ・サイト設定を置く。astro-blog 側の content/ を astro-blog-content の submodule としてマウントする。
切り替えた直後の感覚として、下書きを書くハードルが明らかに下がったのが面白かった。「未公開のものは人目に触れる可能性がゼロ」という状態は、想像以上に執筆継続性に効く。計測できない非機能要件だが、ここを軽視すると個人事業主の発信は続かない、というのが実感である。
submodule には罠もある。GitHub Actions の actions/checkout@v4 では submodules: true または submodules: recursive を明示しないと content が取れずビルドが謎に失敗する。ローカル作業で content 側のブランチが detached HEAD になりやすく、意識しないとコミットがどのブランチにも乗らず消える。そして一番厄介なのが、site 側の commit が指す content 側の commit で本番反映が決まるため、content だけ更新して site 側を更新しないと本番に何も出ないサイレントな失敗モードである。この最後の点は、後述の notify-deploy ワークフローで一応解消したが、検出策としては CI 側で git submodule status --recursive のログを取って期待 SHA と比較するのが素直である。回避策の自動化(content push を契機に site 側へ submodule SHA 更新 PR を自動作成する)は今のところ手動運用のままで、次の宿題として残している。
repository_dispatch はプレビュー環境にしか届かなかった
デプロイ経路の最初の仮説は「content 側の main push を契機に、site 側を repository_dispatch で呼べば済む」というものだった。実装して動かしたところ、site 側の Deploy がプレビュー環境にしか到達しない現象に遭遇する。
原因は repository_dispatch の仕様である。受信側のデフォルトブランチ上のワークフロー定義で処理される、という性質により、site 側のデフォルトブランチを develop にしていた構成では develop 相当のプレビュー経路に寄ってしまう。
workflow_dispatch --ref main に切り替えると production 対象ブランチを明示できる。実装した workflow はこれだけの分量である。
.github/workflows/notify-deploy.yml の抜粋
name: Notify site deploy
on:
push:
branches:
- main
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Trigger site Deploy workflow on main
env:
GH_TOKEN: ${{ secrets.DEPLOY_DISPATCH_PAT }}
run: |
gh workflow run Deploy \
--repo <owner>/<site-repo> \
--ref mainこの差し替えで production は素直に動くようになったが、前提が 2 つある。site 側の Deploy workflow が workflow_dispatch を受け付ける定義であること、そしてworkflow ファイル自体が受信リポジトリの default branch に存在していること。この 2 つ目を忘れると、また別のサイレントスキップに遭遇する。
もう一つ未解決として残しているのが、DEPLOY_DISPATCH_PAT(fine-grained PAT、site repo の Actions Read and write 権限のみ)の失効管理である。期限が切れても content 側の push は成功するため、notify-deploy workflow だけが静かに失敗し続ける未検出リスクが存在する。現状は手動で失効日をカレンダー管理しているが、監視の自動化は積み残しになっている。
手応えとしっくりこなかったところ
AI に要件から任せた体験は、一言でいうと「往復前提なら、ペイはする」という感触で終わった。
粗い叩き台を即日出せる心理的負担の軽さは確かに効いた。AI 同士のピアレビューで単一モデルの自己整合バイアスを緩和できたのも収穫で、同じ発想を Skill 分離設計にも横展開できた。一方で「時系列で効く非機能要件」への AI の感度は明確に低く、DDD のような設計手法の適用判断を AI に委ねるより、人間側が「形だけ残す/中身は捨てる」と割り切る方がずっと早い。PAT 失効のような運用細部も、AI に言語化を促さないと設計から落ちる。
まとめると、要件定義を AI に 1 発で求めるな、3〜5 往復の追補プロンプトで詰めろ、が一番の持ち帰りだった。次点が Claude と Codex の異モデルピアレビューは多数決より信号が強い、それから DDD / TDD は「形だけ輸入、重量は捨てる」が個人事業主スケールの落とし所、public / private 分離は心理的コストの問題であって技術的問題ではない、repository_dispatch で躓いたら workflow_dispatch --ref と default branch の罠を疑え、といったところ。
このあと詰めたいこと
- AI ピアレビューの定量評価。同じ要件を Claude 単独と Claude + Codex で並行設計させ、レビュー指摘の質・量を比較したい
- DDD の「形だけ輸入」がどこでブレイクするか。記事本数・機能数がどこまで増えると薄いエンティティが窮屈になるかを観察したい
- PAT 失効の早期検出。
notify-deployの失敗通知を Slack なり GitHub Discussions なりに飛ばす仕組みを組み込みたい
「AI に要件から任せる」実験は現時点で暫定的にペイしている、とは言える。ただ時系列で効く非機能要件に対しては AI の感度が明らかに弱いので、長期運用フェーズで何が噴き出すかは未知である。この記録はその時点での観察としてのみ残す。