出力を契約する ― 配ったあとも中身を変えられるようにする

シリーズ・第 2 回 — 「個人で出版ツールを作って配るまで」(全 5 回予定)。前回は全体像と前提を共有しました。各回は単独でも読めます。一覧は シリーズの記事一覧 から。今回のテーマは「配ったあとも、中身を変えられるようにしておく」です。

自分だけで使う道具なら、いつでも自由に作り変えられます。問題は、公開して他の人が使い始めたあとです。

たとえば設定キーをひとつ改名しただけで、その古いキーを使っていた利用者の環境は動かなくなります。配ったあとは、利用者が頼っている部分を気軽に変えられない。「外から見えるものは、いつか誰かが当てにする」——これは Hyrum の法則として知られる、ごく当たり前の現象です。

そこで配る前に決めておきます ― 何を約束として固定し、どこは自由に変えてよいことにするか。crofty はその約束を「出力」に置きました。

入力か、出力か

ここでの「入力」と「出力」は、道具(crofty)が受け取るもの(あなたが書く記事や設定)と、生み出すものdist)です。約束をどちらに置くか、二択になります。

入力を契約する 出力を契約する
固定するもの 設定キー・ファイル構成・テーマ内部 生成 HTML に必ず入る項目
内部の変更 利用者の環境が壊れやすい 契約さえ守れば自由
つらくなる例 キー名の改名、部品の作り替え 契約を破ったときだけ

入力を契約すると内部実装が約束になり、出力を契約すると作り方が自由になります。crofty が選んだのは後者です。

「契約」の正体 ― 実物を見る

言葉だけだと抽象的なので、実物を見ます。crofty が生成する記事ページの <head> には、必ず次が入っています。

<!-- 生成された HTML(抜粋) -->
<html lang="ja">
  <head>
    <title>記事タイトル · サイト名</title>
    <link rel="canonical" href="https://example.com/posts/hello/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- + フィード(/feed.xml)があり、外部へ勝手に通信するタグは無い -->
  </head>

この「必ず入っている項目」こそが契約です。langcanonical が欠けたページを、crofty は出力しません。逆に、ここに無いもの(テーマの作り方、HTML の組み立て方)は、自由に変えてよい部分です。

crofty の内部実装は自由に変えられ、その下の出力契約(dist の保証)は固定され、その先の公開サイトは契約が同じなら壊れない、という三層の図
固定するのは真ん中の一本(出力契約)だけ。上の実装は動かしてよい

文書ではなく、機械で守る

契約は文章で書くだけだと、いつか実装とずれて腐ります。そこで crofty doctor が、ビルドした dist が契約を満たしているか毎回チェックします。

$ crofty doctor
✓ output contract: all good

Checks: canonical link, feed, <html lang>/<title>/viewport, no phone-home.

これで契約は「努力目標」ではなく「機械が守る線」になります。内部をどれだけ作り替えても、doctor が通る限り、利用者のサイトは約束どおりに保たれます。

それでも、入力はゼロにはできない

出力を契約しても、入力面(記事の frontmatter や設定)は残ります。大事なのは、その線引きです。

場所 扱い
crofty が所有するファイル crofty が書く
利用者のもの(hugo.yaml など) 書き換えず、案内にとどめる
見た目のカスタマイズ あとから勝つ層(crofty theme ejectcustom.css)で上書き

上書きしても doctor が契約を点検するので、「自由に変えていい場所」と「守る場所」は混ざりません。

どこにでも効く考え方

これは crofty に限りません。「公開 API を、内部の作りではなく、外から見える振る舞いで定義する」という一般的な作法です。

約束する面=外から見える面を、意識して小さく選ぶ。何を凍らせ、何を動かせるようにしておくか。配る道具の設計は、その線引きから始まります。


← 前の記事:書いたものを自分の手元に置く | 次の記事:多言語サイトの設計