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

> このブログ自体をMicroCMS + Next.jsからAstro × Markdownに移行しました。152記事の完全移行をClaude Codeがほぼ1セッションでやり切れた鍵は、実装前に論点を詰め切るGOAL.mdドリブンな計画づくりでした。

- 公開日: 2026-06-12
- 著者: 村井 謙太
- タグ: Claude Code, Astro, AIエージェント
- URL: https://tech.anycloud.co.jp/articles/microcms-to-astro-migration

---

このブログ「エニテック」は、長らく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本です。

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依存からの移行を考えている方の参考になれば幸いです。
