ohgs.dev
← Journal

メディアをGitHubで管理し始めたら、CMSができてしまった話 — UMPの優位性とこれからの展望

プロダクト

最初からCMSを作ろうとしたわけではない。むしろ逆で、CMSを持ちたくなかった。

関わるメディアが増えるたびに、別々の管理画面、別々のログイン、別々の入稿ルールが増えていく。WordPressがあり、Notion + Super.soがあり、手書きのHTMLがあり、それぞれにプラグインとアカウントと「この媒体はこうやって出す」という暗黙知がぶら下がっていた。サイトが1つ増えるたびに、覚えることが増える。これを止めたかった。

そこで素朴なことを1つ決めた。記事も固定ページも、全部GitHubのリポジトリで管理する。本文はMarkdownのテキストファイル、出力先はAstroのサイト、公開はコミットしてビルドするだけ。データベースに記事を閉じ込めるのをやめて、コンテンツをただのファイルに戻した。

ここまでは「管理方法を変えた」だけの話だ。問題はその次に起きた。

WebUIを作ったら、CMSになっていた

ファイルで管理するのは正しかったが、毎回エディタを開いてfrontmatterを手で書いて、画像をアップロードしてパスを通して、git addしてcommitしてpushする、というのは編集作業ではない。エンジニアの作業だ。一緒にやる人にこれを覚えてもらうのは無理がある。

だから入稿用のWebUIを作った。タイトルと本文を書く欄、サムネを上げるボタン、公開ボタン。押すと裏でGitHubにコミットが飛ぶ。最初はそれだけのつもりだった。

ところが、現場で回し始めると「あれもいる」「これもいる」が止まらなくなる。書いてすぐ世に出るのは怖いから承認のステップがいる。タグ付けと校閲が毎回だるいからAIに下ごしらえさせたい。媒体ごとに出力先のパスもファイルの形も違うから、それを吸収する層がいる。誰が何を公開していいかの権限がいる。トップページの編成を変える画面がいる。

気がつくと、入稿・校閲・承認・公開・権限管理・デプロイまでが1つの管理画面に揃っていた。これはもうCMSだった。UMP(UTAR Media Platform)と名前を付けたのは、そうやって機能が出揃ってからの話だ。設計書を書いて作ったCMSではなく、運用の不便を1つずつ潰した残りがCMSの形をしていた、という順番になっている。

なぜこの作り方が効くのか

偶然できたものではあるけれど、この成り立ちそのものが、既存のCMSに対する優位性になっている。要点を整理しておく。

コンテンツがGitHubにある、という一点

UMPの一番の特徴は、記事の正本がUMPの中ではなくGitHubにあることだ。本文はプレーンなMarkdownファイルで、変更はすべてコミット履歴に残る。

これが効く理由はいくつもある。まず、ロックインがない。UMPが気に入らなくなっても、コンテンツはMarkdownのまま手元のリポジトリに残る。引っ越し先で困らない。次に、すべての変更に履歴が付く。誰がいつ何をどう変えたかがgitのログとして残るので、「公開済み記事を勝手に書き換えない」といった運用ルールを、人の記憶ではなく差分で担保できる。そして、人間と機械の両方から触れる。WebUIから書いてもいいし、緊急ならエンジニアが直接リポジトリを直してもいい。どちらの操作も同じファイルに対する同じコミットになる。

データベース型のCMSは、コンテンツを自分の中に抱え込むことで成立している。UMPは逆に、コンテンツを外(GitHub)に置いて、自分は操作と判断の層に徹している。この割り切りが、後述する強みの土台になっている。

違いはアダプタで裏に吸収する

メディアは1つずつ事情が違う。記事だけのサイトもあれば、固定ページとお知らせだけの店舗HPもある。出力先のリポジトリも、ファイルを置くパスも、frontmatterに要る項目も、URLの規則も、媒体ごとにバラバラだ。公開の仕組みすら、GitHubにコミットしてからビルドを叩くもの、デプロイフックを撃つもの、とまちまちだった。

UMPはこの差分を「アダプタ」という形で裏側に閉じ込めた。媒体ごとに、種類(記事 / 固定ページ / お知らせ)・出力先・必須項目・URL規則・公開のしかた・その媒体固有のチェック(house lint)を定義しておく。書く人は管理画面で同じ操作をするだけで、あとはプラットフォームが、その媒体に合った場所へ、合った形で届ける。

たとえば、ある媒体では特定の人物名を本文に入れてはいけない、という対外ルールがある。これはhouse lintとして定義してあり、混入していると公開がブロックされる。媒体ごとの「やってはいけない」も、運用者の注意力ではなく仕組みで守る。

最初に道北ネット(dohoku.net)で土台を固めて、そこで通った形を他へ広げた。いまは地方紙のweb展開から、地域メディア、リサーチハブ、店舗HP、会社HPまで、十数のメディアを1つの管理画面で横断して動かしている。

「書く」と「世に出す」を分ける

運用していて一番学びがあったのは、トップページの編成のところだった。編集するたびにサイト全体のビルドが走ると、記事数の多いサイトでは再ビルドの行列ができて何分も待たされる。

そこで操作とビルドを切り離した。記事の公開もトップの編成も、リポジトリには即コミットするが、ビルドはしない。本番への反映は管理画面の「サイトに反映」を押したときだけ走る。複数の変更を溜めて1回でまとめて出せるし、未反映の変更が何件あるかも画面で見える。同じ場所を何度直しても、最後の状態だけがビルドに乗る。

この「溜める」と「世に出す」の分離は、権限にも持ち込んだ。UMPの権限は役職の階層(編集長 > 編集者 > 投稿者)ではなく、投稿・編集・ビルド・管理という独立した能力(capability)の組み合わせで付与する。だから「記事は編集できるが、本番に出すのは別の人」という体制を、そのまま権限として表現できる。書く人と、世に出す責任を持つ人を、別の人格に分けられる。これは複数人・複数媒体を回すときに地味に効く。

IDはUTARと共有する

UMPはUTARとIDを共有している。巨大なデータベースを1つ共有しているのではなく、メールから導いた同じ鍵を使うことで、別々のアプリでも「同じ人」として扱える形にした。誰がどのメディアを運営できるかという権限も、このIDの上に乗っている。プロダクトをまたいでも、ログインし直す必要も、別アカウントを作る必要もない。

下ごしらえはAI、判断は人

入稿のたびに発生する地味な作業 — 校閲、タグ付け、要約、取材メモからのたたき台づくり — はAIに下ごしらえさせている。人がゼロから書くのではなく、AIにたたき台を出させて、人は判断と仕上げに回る。記事数が増えても運用が重くならないように、ここは早めに入れた。

大事なのは、AIを「自動で公開する仕組み」ではなく「下ごしらえの道具」に留めていることだ。最後に世に出す判断は必ず人が承認のステップで通す。速くしたいのは下準備で、雑にしたいわけではない。

これからの展望

UMPはまだ途中だ。これから向かいたい方向を3つ書いておく。

検証付き公開

UMPは「書いて出す」層を担当しているが、その手前に「出す前に質を検証する」層を置きたい。これは別に作っているFactSpotの役割で、記事の品質をコードで実測しAIで多角診断する道具だ。UMPの公開フローからFactSpotを呼んで、検証をくぐった記事だけが世に出る、という形を作りたい。

UMP(書く・出す)とFactSpot(出す前に検証する)を別の層として分けてあるのは、この接続を前提にしているからだ。将来的には「検証付きで公開できる仕組み」そのものを、社外の発行者にも開ける。

公開前プレビューと、書く面との接続

いまは公開してからサイトで確認する流れだが、公開前にブランチのプレビューで見られるようにしたい。それと、FactSpot側で書いた原稿をUMPの入稿画面にそのまま引き継いで、下書きとして管理・編集できるようにしたい。書く面と出す面を、人の手作業でつながずに地続きにする。

発行者のためのプラットフォームへ

もっと先の話として、UMPを自分たちが使うだけの道具で終わらせたくない。地域の発行者が、会員管理・課金・サイト・配信までを1つのスタックで持てる「発行者のための情報OS」にしていきたい。GhostやSubstackが個人の発信者向けにやっていることを、複数媒体を抱える地域の発行者向けに、IDとデータを共有した形で提供する。

その土台はもうできている。コンテンツはGitHubにあってロックインがなく、媒体差はアダプタで吸収でき、IDは共有され、書くと出すは分離されている。あとはこれを、自分以外の発行者が使える形まで磨くだけだ。

結局のところ

「メディアを増やすほど、運用が重くなる」を「増やすほど、運用が軽くなる」に反転させたい。これがUMPで一貫してやろうとしていることだ。

CMSを作ろうとして作ったのではなく、運用の摩擦を1つずつ削った結果がたまたまCMSだった、という成り立ちは、弱みではなく強みだと思っている。使ってみないと気づけない不便だけを潰してきたから、機能の1つ1つに現場の理由がある。発信する人が、書くことそのものに時間を使える状態を、これからも裏側から作っていく。

事業・メディア・データ・地域・AI まわりのご相談を受け付けています。 お気軽にどうぞ。

お問い合わせはこちら