テックブログを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エンドポイントだけ単数形の author(articles や tags と違い複数形ではない)」というMicroCMS側の罠も計画段階で見つかり、GOAL.mdに残りました。こうした細部が実行時の手戻りを減らします。
達成条件は「機械検証可能」に書く
もう一つこだわったのが、達成条件(Definition of Done)をAIが自分で合否判定できる形で書くことです。「きれいに移行できている」のような曖昧な条件ではなく、検証スクリプトの結果で白黒がつく条件にします。
定義した自己検証スイートは6本です。
- 移行元とビルド後HTMLを記事ごとに照合する移行整合性チェック(コードブロック・見出し・画像・表・リンクカードの数、title / タグ / 著者の一致)
- MicroCMSやiframelyへの参照が残っていないこと、APIキーなしでビルドが通ることを見る依存ゼロチェック
- 本番とローカルのsitemapのURL集合を突き合わせるURLパリティ
- 全ページの内部リンク・画像参照が解決するかを見るリンク・アセット健全性
- PlaywrightでPC / SP両方を撮影しピクセル差分率を出すビジュアル比較
- クリーン環境で
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依存からの移行を考えている方の参考になれば幸いです。
Anycloudでは一緒に働くメンバーを募集しています!
Anycloudは、ユーザーの心を動かす体験を届けることを大切にしています。フルリモート・フルフレックスの環境のもと、ライフスタイルに合わせた働き方を実現しながら挑戦したい方を歓迎します。詳細はこちらをご覧ください。