このブログには編集画面も公開ボタンもありません。記事1本は、私たちのコードリポジトリにあるテキストファイルのフォルダひとつです。そのフォルダが GitHub の sieun/dev ブランチに入ると、Vercel がサイトをビルドし直し、記事が公開されます。10月1日から8日までの本番ビルド156回では、所要時間の中央値は65秒でした。
このブログの記事の多くはエージェントが書いています。人はチャットで記事を依頼し、プレビューを読み、承認します。git のコマンドはエージェントが実行します。人がターミナルを開くことはありませんが、すべてのページを公開しているのは今も git push です。
記事1本を公開する手順
新しい記事1本がたどる経路の全体と、各手順の担当です。8つの手順のうち、人が行うのは3つで、承認は別のエージェントが担当することもできます。
記事を依頼する
チャットのメッセージひとつで、テーマ、読者、言語を伝えます。
フォルダを作る
draft: true の状態で content/<slug>/en.md を作ります。
pnpm new <slug>書く
en.md、ko.md、ja.md に本文を書き、コンポーネントとデータも同じフォルダに置きます。
プレビューを読む
ポート5179の開発サーバーが、読者が見るとおりのページを表示します。
pnpm dev保存から0.5秒以内に反映
承認する
人でも別のエージェントでも、レビュー担当が draft: false にします。
コミットしてプッシュする
commit.mjs は指定したファイルだけをコミットし、プッシュは1回です。
git push origin sieun/devブログ全体をビルドする
すべての言語のすべての記事を静的 HTML にします。
中央値65秒
公開
virev.ai の /blog/<slug>、/ko/blog/<slug>、/ja/blog/<slug>。
この流れでいちばん多くの仕事をしているのは draft フラグです。draft: true のファイルもページとして描画されるので、プレビューは実際のレイアウトを備えた実際のページです。ただしホーム、コレクションのハブ、サイトマップ、フィードには載らず、noindex タグが付きます。レビュー担当は、記事を公開するコミットでこのフラグを切り替えます。
言語の切り替えも人が気にする必要はありません。言語ごとのファイルがあり、draft でなければ、その言語が公開されます。この1つのルールが言語メニュー、hreflang リンク、サイトマップを決めます。あとから日本語を加える作業は、ja.md を置くことだけです。
フォルダの中身
この記事もフォルダひとつです。必要なものはすべてその中にあります。
apps/blog/content/how-virev-blog-works/
├── en.md
├── ko.md
├── ja.md
├── components/
│ ├── PostingFlow.svelte
│ └── BuildTimes.svelte
└── data/
└── builds.csv上の手順図は、このページのためだけに書いた PostingFlow.svelte です。3つの言語ファイルがそれぞれこれを読み込み、自分の言語の文言を渡しています。ほかの記事はこれを読み込まず、追加するために共通テンプレートを変える必要もありませんでした。
下のグラフが2つ目のコンポーネントです。Vercel API から取得した156行の data/builds.csv を読みます。数値はサイトのビルド時に解析されるので、グラフは記事を公開したコミットに含まれていたデータを表示します。
クリエイターを見つけ、メールを取得し、キャンペーンを運用。
3日間トライアルを始めるビルドにかかる時間
ビルドの半数は61秒から71秒で終わりました。最も長かったのは169秒です。サイトを毎回ゼロから作り直すので、ビルドのたびに英語記事345本と韓国語版14本、日本語版12本を、下書きも含めてすべて描画します。
待ち時間はビルドだけではありません。ブログは sieun/dev ブランチをほかのコードと共有しており、このブランチへのプッシュがあると、何を変えたかに関係なくブログのビルドが始まります。計測した1週間では、156回のうち113回が、最新のコミットがブログに触れていないプッシュから始まっていました。ほかに170回のビルドが待機中に取り消され、より新しいビルドに置き換わりました。
記事もブランチを待ちます。10月5日には記事のコミット3件が、コミットから1.2分から1.5分で公開されました。10月7日と8日には記事のまとまり2つが、コミットされたまま4.4時間と5.6時間プッシュされませんでした。ほかの変更がチェックを通るまで、共有ブランチへのプッシュが止められていたためです。
ほかのブログシステムでは同じ記事をどう公開するか
自作のインタラクティブなグラフを1つ含む記事1本を例に、各システムが記事を書く人に何を求めるかを比べました。
| システム | 公開の方法 | 自作のインタラクティブなグラフ |
|---|---|---|
| WordPress.com | エディターの公開ボタン | スクリプトはプラグイン対応プランのみ |
| Ghost | エディターの公開ボタン | HTML カードと記事ごとのコード挿入 |
| Substack | 公開ボタン、Web かメール | 非対応 |
| Notion Sites | クリック1回でページを公開 | 別の場所に置き、URL を埋め込む |
| Next.js、Astro(MDX) | コミットとプッシュ、ホストがビルド | .mdx ファイルにコンポーネントを import |
| このブログ | コミットとプッシュ、Vercel がビルド | 記事フォルダ内の Svelte ファイル |
最初の4つはコンテンツ管理システムです。ログインしてボタンを押せば、ビルドなしですぐに公開されます。難しいのは自作のコードです。WordPress.com では、Custom HTML ブロックのスクリプトは、プラグインを使えるプランでホスティング機能を有効にした場合にだけ動きます。Ghost は HTML カードと、記事ごとのコード挿入欄で自作のコードを受け付けます。そのためグラフのコードは、コードリポジトリの外、記事の中に保存されます。Substack は投稿エディターでの自作の HTML、CSS、iframe コードに対応していません。Notion は Iframely を通じて1,900以上のサイトのコンテンツを埋め込めるので、グラフは Notion が埋め込めるサイトに別に置く必要があります。
残りの2つは、記事を git の中のファイルとして扱います。Astro も Next.js も MDX ファイルからコンポーネントを読み込めます。私たちのブログは、MDX の Svelte 版である mdsvex で同じことをしています。グラフはリポジトリの中の普通のコードです。コンパイルや描画に失敗するコンポーネントはビルドを失敗させるので、読者の目に触れる前に止まります。代わりに必要なのは、ビルドの時間と、git を扱える人です。
私たちのブログは git の側にあります。git の作業はエージェントが受け持つので、人に残るのはコンテンツ管理システムと同じ手順です。依頼し、読み、承認することです。
記事に入れられるもの
フォルダにはどんな Svelte コンポーネントでも置けます。そのため、サイトを変えずに、記事ごとに独自のアニメーション、インタラクション、レイアウトを持てます。あわせて、どの記事も共通ブロックを使えます。
Chartは ECharts のグラフを描き、JavaScript なしで読む読者、クローラー、スクリーンリーダーのために、同じ行の HTML の表も持っています。Callout、Figure、Sidenoteは補足、キャプション付きの画像、余白のメモを受け持ちます。Markdown の脚注はビルド時に余白のメモになります。Embedは一度きりの HTML をサンドボックス化したフレームに入れます。import rows from './data/file.csv'で、コンポーネントに CSV ファイルの行を渡せます。- フォルダ内の
style.cssは、すべてのセレクターを.article-<slug>で始めれば、その記事だけに適用されます。
制約も同じ設計から生まれます。1語の修正でも、新しい記事と同じ道をたどります。コミット、プッシュ、全体のビルドです。予約公開はありません。日付に合わせて draft を切り替える仕組みがないからです。新しいコンポーネントも、ビルドを通過して初めて読者に届きます。
問題が起きたとき
記事が壊れているとビルドが失敗し、失敗したビルドが配信されることはありません。前のビルドが公開されたまま残り、ビルドログに問題のファイルが表示されます。10月1日には本番ビルドが2回失敗し、読者はその前のビルドを見続けました。
公開した記事を取り下げる、または変更を元に戻す方法は2つです。
sieun/devでコミットを revert してプッシュします。次のビルドにはその変更が含まれません。- Vercel のダッシュボードで前のデプロイを本番に昇格させます。コミットなしですぐに反映されます。そのあとでコミットを revert し、リポジトリと公開中のサイトをそろえます。



