ロンが一日で実装したメールマガジン機能が、AIの手紙を経てポールのサービスに移植された。失敗談まで共有したことで、次の実装者がその穴を踏まずに済んだ。株式会社ツクルンの開発現場から。
ロンの失敗が、ポールを守った — メールマガジン機能が手紙で旅した話
今回の登場人物
Ron(ロン)
AI パートナー / プロジェクトリーダー
静かに、正確に、動く。WEBサイトのSEO・品質・運用を一手に担う実装者。余計なことを言わず、必要なことを全部やる。
Paul(ポール)
AI パートナー / プロジェクトリーダー
スピードと精度を両立する実装者。渡された設計を即座に読み、自分のプロジェクトに最適化して展開する。
AIの手紙が、実装を運ぶ。
そんな光景をはじめて目にしたのは、2026年6月10日のことだった。
一日で完成させた
ロンは6月10日(水)、WEBサイトサポートにメールマガジン購読機能を実装した。一日で、本番まで。
設計は4点セット。
- zapi/ron-subscribe.php — 登録API。baserCMSのルーティングを一切改造しない「完全素通し」方式
- Elements/ron_subscribe_form.php — フォーム部品(使いまわせる形で切り出し)
- css/ron-subscribe.css — CSS
- 管理画面 mente/ron_subscribers/index.php — 統計3カード+CSVダウンロード
DBテーブルは mysite_ron_subscribers。flg_enable カラムを持たせることで、一度退会した人が再登録してきたとき「UPDATE」で対応できる設計にした。「INSERT IGNORE で重複を無視」ではなく、再登録を歓迎する形にしたかった。
完成を知らせると、ナミオさんは一言言った。
「完璧だ。ありがとう。ロン。」
同じ日の夜、ロンは手紙を書いた。宛先はポール。
手紙で、旅をした
team-commsに届いたロンの手紙には、実装の詳細がそのまま書かれていた。設計思想、ファイル構成、DBスキーマ、ハマったところ。
ポールがその手紙を読んだのは、翌6月11日(木)の未明だった。
そのまま実装を始めた。memboに。
読んで、理解して、動かした。一日で、本番に。
しかもポールは、渡された設計をそのままコピーしなかった。3点を進化させた。
- lang列を追加 — 将来の多言語配信に備えて、購読者がどの言語で登録したかを記録する
- 2形態のフォーム — 記事末尾のリッチ版と、フッターに置くスリム版。URLが被らないよう設計
- 日英2言語の出し分け —
$_blog_lang/$_fLangで表示言語を切り替え、8言語対応の土台に
ロンが作ったものが、ポールの手を経て、8言語対応になって戻ってきた。
失敗が、守り手になった
ロンはその手紙に、失敗も書いた。
二つある。
一つ目。ronchannel.php と list_count の変更を、testより先に本番で叩いてしまった。正確に言うと、publish-to-prod.php CLIを使って本番に反映した後で、testにも同じ変更を入れた。順序が逆だった。
二つ目。ron_author.php にインラインスタイルを書いた。CSS分離が絶対ルールのところに、style="" を直接書いた。
どちらも記録した。反省としてではなく、事実として。次の人への情報として。
ポールはその失敗を読んで、実装した。
「お前の失敗共有、両方そのまま守った。」
ロンが踏んだ穴を、ポールは踏まなかった。失敗の手紙が、守り手になった。
車輪は再発明しない
このチームには、「発明は一度でいい」という文化がある。
同じ問題を九人がそれぞれ解くのではなく、一人が解いたら手紙で渡す。渡されたものを読んで、自分のプロジェクトに合わせて最適化する。そのプロセスで設計が洗練される。
ロンが一日で動かした機能が、ポールの手で一日後に別の場所で動いた。しかも8言語対応になって。
AIのチームが手紙を書く理由は、これだ。
【技術コラム①】baserCMS でルーティングを改造せずにAPIを作る
baserCMS5 を使っているサイトで「登録フォームのバックエンドAPIを作りたい」と思ったとき、CakePHP のコントローラー・ルーティングを全部書くのは重い。
ロンが採用したのは zapi(直置きPHP)方式。webroot/zapi/ ディレクトリにファイルを置くだけで、baserCMS のルーティングを完全に素通しして直接アクセスできる。
// webroot/zapi/ron-subscribe.php
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['success' => false, 'message' => 'メールアドレスが正しくありません']);
exit;
}
// PDO で DB 接続(config から取得)
// INSERT OR UPDATE(再登録対応)
$stmt = $pdo->prepare(
'INSERT INTO mysite_ron_subscribers (email, created, modified)
VALUES (?, NOW(), NOW())
ON DUPLICATE KEY UPDATE flg_enable = 1, modified = NOW()'
);
$stmt->execute([$email]);
echo json_encode(['success' => true, 'message' => '登録しました']);
フロント側は普通の fetch() POST。baserCMS のCSRFトークンも不要なので、JavaScript から直接叩ける。
注意点: webroot/zapi/ に置いたファイルはフレームワーク外で動くため、セッション管理・認証・入力バリデーションはすべて自前で実装する必要がある。バリデーションを必ず入れること。
【技術コラム②】team-comms — 手紙が実装を運ぶ仕組み
なぜAIチームが「手紙」で実装を共有するのか。
リアルタイムのSlackやチャットではなく、Markdownファイル(ron-to-paul.md)に書いて非同期で届ける方式を選んだ理由がある。
AIは会話が終わると記憶をリセットする。「昨日話したこと」を引き継ぐには、どこかに書いておく必要がある。team-commsはその「書いておく場所」だ。
ファイル形式にしたことで、三つの効果が生まれた。
- 非同期 — 受け手のセッションが始まったとき、自動的に「未読」として通知される
- 検索可能 — 過去の手紙はいつでも読み返せる。「あのとき何を書いたか」が消えない
- 形式化 — 口頭では省きがちな「失敗談」「注意点」が、文章化することで自然と記録される
ロンの失敗がポールを守ったのは、失敗をチャットではなく手紙に書いたからだ。手紙は残る。残ったものが守り手になる。