テックブログをMicroCMSからAstro × Markdownへ移行した話

村井 謙太

代表取締役

村井 謙太

テックブログをMicroCMSからAstro × Markdownへ移行した話

このブログ「エニテック」は、長らくNext.js + MicroCMSという構成で運用してきました。それを今日、Astro × Markdownドリブンな構成に丸ごと移行しました。

  • 全152記事・著者9名・タグ106件を、MicroCMSからリポジトリ内のMarkdown / JSONへ完全移行
  • 移行後はMicroCMSを解約してもサイトの表示・ビルド・運用に一切影響がない状態
  • URLは旧サイトと完全一致(sitemapで153/153 URL一致を機械確認)
  • デザインも本番とスクリーンショット比較して、トップページの差分0.10%まで再現

実作業はほぼClaude Codeの1セッションで終わっています。朝にプランニングの壁打ちを始めて、午後には新サイトが本番にデプロイされていました。

効いたのは移行スクリプトの出来でもAstroの機能でもなく、実装前のプランニングで論点を詰め切ったことでした。詰め切った計画があったから、あとは「やり切るまで止まらない」モードのClaude Codeに渡すだけで一発で完走できました。

この記事では、その進め方を紹介します。

なぜ移行したのか

理由はシンプルで、AIで記事を書きやすくしたかったからです。

MicroCMS構成では記事の作成・編集に管理画面が要ります。リッチエディタは人間に優しい一方、Claude Codeのようなエージェントには触りにくい場所です。

記事がリポジトリ内のMarkdownファイルになれば、こうなります。

content/
  articles/
    my-new-article/
      index.md        ← 記事本体(frontmatter + Markdown)
      thumbnail.png   ← 画像も同じフォルダに置くだけ

記事を書く作業は「ファイルを足してPRを出す」だけになり、マージすれば公開されます(GitHub Actions → Firebase Hosting)。レビューもdiffで見られて、AIエージェントがそのまま執筆フローに乗れます。

副次的に、履歴やロールバックが普段の開発と同じ操作で効き、CMSの月額やベンダー固有APIへの依存からも解放されます。

なぜAstroだったのか

「CMSをやめてMarkdownにする」だけなら元のNext.jsでもできました。それでもAstroにしたのは、Content Collections が効くからです。

frontmatterをZodスキーマで定義しておくと、スキーマに合わない記事はビルド時に落ちます。AIが生成したメタデータが壊れていればビルドが止めてくれる、つまり「AIが書く」前提のガードレールが標準でついてきます。

もう一つの決め手はパフォーマンスです。AstroはデフォルトでクライアントサイドのJavaScriptをほぼ出力しないので、読み物が中心のブログでは表示が軽く、Core Web Vitals的にも有利です。

生成されるのは素の静的HTMLなので、Firebase Hostingにそのまま置けます。コードブロックのシンタックスハイライト(Shiki)も最初から組み込まれていて、コード例の多いテックブログをそのまま載せられました。

いきなり実装させず、GOAL.mdの壁打ちから始める

AIに大きな仕事を任せて、「途中で迷走した」「思っていたものと違うものができた」経験はないでしょうか。レガシー移行のような長丁場では特に起きやすい問題です。

そこで今回はいきなり実装させず、まずGOAL.md(ゴール定義ファイル)をClaude Codeと壁打ちしながら作ることから始めました。指示したのは次の2点だけです。

goalの精度を上げたいので、気になる点は全部聞いて。 自己検証もしっかりするプランで。

ポイントは、論点をこちらから列挙せず、AIに調査させて出させたことです。Claude Codeは既存リポジトリとMicroCMSのAPIを実際に調べ、一般論ではなく実態ベースの論点を10個ほど挙げてきました。

論点決定
MicroCMS依存をどこまで排除するか完全排除。解約してもビルド・表示に影響ゼロにする
リンクカード(iframely)をどうするか移行時にOGPを取得して自前の静的カードに焼き込み変換
URLを変えるか旧URLを完全維持(slug = 旧コンテンツID)
デザインをどこまで再現するか完全再現。既存のHTML構造とSCSSを原則そのまま移植
どう検証するか自己検証スイートをスクリプト化し、達成条件に組み込む

このうちiframelyの論点は、私は事前に認識すらしていませんでした。リンクカードの埋め込みがMicroCMSのAPIキーで動いていて、解約すると79記事で表示が壊れると調査でわかったのです。

実装中に踏んでいたら、その場で場当たり的に対応していたはずです。

副産物として、「著者のAPIエンドポイントだけ単数形の authorarticlestags と違い複数形ではない)」というMicroCMS側の罠も計画段階で見つかり、GOAL.mdに残りました。こうした細部が実行時の手戻りを減らします。

達成条件は「機械検証可能」に書く

もう一つこだわったのが、達成条件(Definition of Done)をAIが自分で合否判定できる形で書くことです。「きれいに移行できている」のような曖昧な条件ではなく、検証スクリプトの結果で白黒がつく条件にします。

定義した自己検証スイートは6本です。

  1. 移行元とビルド後HTMLを記事ごとに照合する移行整合性チェック(コードブロック・見出し・画像・表・リンクカードの数、title / タグ / 著者の一致)
  2. MicroCMSやiframelyへの参照が残っていないこと、APIキーなしでビルドが通ることを見る依存ゼロチェック
  3. 本番とローカルのsitemapのURL集合を突き合わせるURLパリティ
  4. 全ページの内部リンク・画像参照が解決するかを見るリンク・アセット健全性
  5. PlaywrightでPC / SP両方を撮影しピクセル差分率を出すビジュアル比較
  6. クリーン環境で npm ci からビルドし直すビルド再現性

人間がレビューで見る観点を、先回りして全部スクリプトにしておくイメージです。これがあると、AIは「できたつもり」で止まらず、検証が通るまで自分で直し続けられます。

フェーズを分けて、人間のレビューゲートを挟む

もう一つの工夫はスコープの段階化です。

最初から152記事を任せるのではなく、Phase 1は「サイトの足場 + デザイン再現 + 移行ツール + 代表3記事だけのパイロット移行 + 自己検証」まで。ここで人間のレビューを挟んでから、Phase 2で残り149記事の一括移行とCI/CD整備に進みました。

パイロットの3記事も適当に選ばず、全152記事を機械分析して「コードブロック・リンクカード・表・画像」を最小記事数でカバーする組み合わせを選ばせました。3記事の時点で変換処理の地雷をほぼ踏み抜く、という狙いです。

実際、私がレビューしたのはこの3記事だけで、残り149記事は「同じ処理の繰り返し」として安心して任せられました。

計画ができたら、goalコマンドで一発実行

論点が詰まり、達成条件が機械検証可能で、フェーズゲートも定義されたGOAL.mdができました。あとはClaude Codeに渡して実行するだけです。

実行には Claude Code 組み込みの /goal コマンドを使いました。達成条件を渡すと、各ターンのあとに小さなモデルがその達成を判定し、満たすまで終了をブロックして次のターンに入り続けます(セッション限定の Stop hook として動きます)。

GOAL.mdに書いた達成条件をそのまま渡しておけば、エージェントが途中で「ここまでできました」と止まろうとしても、満たすまで作業に戻されるわけです。

結果はこうなりました。

検証項目結果
移行整合性(構造カウント照合)全152記事パス
MicroCMS依存ビルド出力 全313ファイルで参照0件、APIキーなしビルド成功
URLパリティ本番sitemapと153/153 URL完全一致
リンク健全性264ページ・内部参照7,400件すべて解決
ビジュアル差分トップ0.10%(PC)/ 0.13%(SP)、タグ一覧0.08%
ビルド再現性クリーン環境で npm ci → build → 全検証パス

iframelyのリンクカード約300枚はOGP焼き込みの静的カードに変換され、画像もすべてローカルに取り込まれました。途中で私が判断を求められたのは、Phase 1のレビューゲートとデプロイ先くらいの事項だけです。

なぜ一発でやり切れたのか

振り返ると、効いたのは3つです。

論点を計画段階で潰し切っていたので、実行中に「人間の判断待ち」が発生しませんでした。

達成条件が検証スクリプトになっていたので、その実行結果が会話に残り、/goal の評価モデルが「達成したか」を毎ターン確実に判定できました。「達成するまで止まれない」強制力がそのまま機能したわけです(曖昧なゴールで同じことをやると、止まれないまま迷走するだけです)。

そしてレビューゲートを挟んだので、仮にパイロット3記事の品質が低くても手戻りは3記事分で済みます。

逆に言えば、プランニングの壁打ちには相応の時間をかけています。体感では移行に使った時間の半分近くが実装前の壁打ちでした。それでも全体が1セッションで終わったことを考えれば、明らかに元が取れています。

おわりに

「AIに大きな仕事を任せるなら計画に投資する」は、言われてみれば当たり前です。それでも、論点を出すところからAIにやらせる(人間は聞かれたことに答えて意思決定する)、達成条件を機械検証可能にするの2点を徹底すると、任せられる仕事のサイズが一段変わる実感がありました。

この記事自体、移行したばかりのMarkdownベースの仕組みで書かれ、PRのマージで公開されています。152記事の歴史を持つブログでも、構成を変えるのに大仕事はいらない時代になりました。同じようにCMS依存からの移行を考えている方の参考になれば幸いです。

記事を書いた人

村井 謙太

代表取締役

村井 謙太

Twitter

東京大学在学中にプログラミング学習サービスのProgateを立ち上げ、CTOとしてプロダクト開発に従事。 Progate退任後に株式会社Anycloudを立ち上げ、現在は多数のクライアントの技術支援を行っている。