はじめに — 首を痛めた火曜の夜から始まった
最初に
きっかけは
それが SparMate になった。
作ってから 2 週間後、
そこから
アプリが違っても、痒みの形は同じだった
3 ジャンルで
| ジャンル | 悩み | 足りない "1 人" |
|---|---|---|
| 格闘技 | 体重・ |
同じ階級の |
| お笑い | 笑いの |
ネタ |
| ボドゲ | 4 人必要な |
あと 1 人 |
違うのは「条件の
マッチングの
本質は アルゴリズムじゃない。 "条件で 絞れる" × "メッセージを 送れる" が 揃えば、 あとは ユーザーが 勝手に 見つける。
この
1 つの env var で 3 アプリに分岐する
コードのほぼ全部は共通。違うのは genre.ts だけ
// src/config/genre.ts(実値を抜粋)
export const GENRE_CONFIGS = {
martial: {
name: 'SparMate',
primaryColor: '#dc2626',
defaultLanguage: 'en', // 海外 BJJ コミュニティが主戦場
featureFlags: {
locationRequired: true, // ジムに通える距離じゃないと意味がない
superLikeEnabled: false, // 出会い系っぽくしない
ageFilterEnabled: false, // 年齢より体重・レベル
},
},
comedy: {
name: 'NetaPair',
primaryColor: '#f97316',
defaultLanguage: 'ja',
featureFlags: {
locationRequired: false, // 脚本作業はリモート可
superLikeEnabled: true, // 相方探しは熱量勝負
ageFilterEnabled: true,
},
},
boardgame: {
name: 'BoardLink',
primaryColor: '#7c3aed',
defaultLanguage: 'ja',
featureFlags: {
locationRequired: true, // 対面プレイ前提
superLikeEnabled: false,
ageFilterEnabled: false,
},
},
}
これを getGenreConfig() でNEXT_PUBLIC_GENRE を
デプロイは Vercel の 4 プロジェクト並列
同じ GitHub リポジトリを 4 つの Vercel プロジェクトに
| プロジェクト | 環境変数 | ドメイン |
|---|---|---|
| martial-matching | NEXT_PUBLIC_GENRE=martial | sparmate.cosoado-lab.com |
| comedy-matching | NEXT_PUBLIC_GENRE=comedy | netapair.cosoado-lab.com |
| boardgame-matching | NEXT_PUBLIC_GENRE=boardgame | boardlink.cosoado-lab.com |
| matching-app-template | — | (staging) |
git push 1 回で 3 本番 + 1 staging が並列
DB は 1 Supabase プロジェクト × スキーマ分離
ジャンルごとに DB をnetapair / sparmate / boardlink)で
理由は
スキーマ
3 つ同時に回して初めて見えたこと
ここからが、
気づき 1:マッチングアプリの 9 割は "検索 + DM" で足りる
作る
ユーザーが
レコメンドエンジンに
工数を 吸われる 前に 気づくべきだった。 マッチング アプリの 満足度を 決めるのは「検索 フィルターの 数」と「DM の 1 クリック 距離」で、 機械学習は ほぼ関係ない。
気づき 2:ジャンルによって "距離" の重みが丸ごと違う
これは
- 格闘技: ジムに
通える 距離じゃないと成立しない(最重要) - ボドゲ: カフェ
集合が 基本(重要) - お笑い: 脚本は
リモートで 書ける、 合流は ライブで(任意)
同じ "エリアfeatureFlags.locationRequired で
気づき 3:「年齢フィルター」は出会い系のメンタルモデル
最初、
格闘技
確かに、
SparMate だけ
気づき 4:言語デフォルトの選択で体感で数倍変わる
SparMate を
英語
デフォルト UI にしただけで、 BJJ 系 X 投稿の インプレッションが 体感で 5 倍。 defaultLanguageの 1 行の差が、 そのまま市場規模の 差に なる。 グローバル 狙いの ジャンルを 日本語で 出すのは、 自ら市場を 1/20 に 絞る 行為。
気づき 5:「使ってる自分」が何より強いデータソース
NetaPair と BoardLink は、
だから機能の
SparMate は「柔術やってる
ユーザーから見ると、完全に別アプリ
3 アプリは、
- SparMate は
海外の BJJ コミュニティで 紹介されている - NetaPair は NSC 界隈で「相方募集」の
ハッシュタグと 並んで 流れる - BoardLink は
ボドゲカフェで チラシが 置かれる
それぞれ別
これは
数字の話
ベータ
- 開発時間: SparMate ≒ 2 週間(ほぼゼロから)、
NetaPair と BoardLink 合わせて 3 日(テンプレ 化後) - インフラ: Vercel Hobby + Supabase Free で 3 アプリ
運用中(トラフィック 次第で 有料) - 発信運用: X / Bluesky / Threads に 32 本の
投稿を cron で 12 時間ごとに 自動配信 - ユーザー
層 : 3 ジャンル間でばらつきあり、 初期は 想定外の 層が 登録してくることが 多い
個人開発で 1 アプリが
※ テンプレ
自体は マルチジャンル 対応で、 本稿で 扱う 3 アプリの 後に 4 本目として 釣り仲間 マッチング TsuriMate を β 稼働中。 本記事は「最初に 立ち上げた 3 つ」に 焦点を 絞っている。
いまやっていること
- リファラル
機能 : 招待コード 経由の 登録で お互いの 表示優先度が 上が る 仕組み。 バイラル 係数を 見える 化したい - X / Bluesky / Threads への
同時配信 : 同じコンテンツを 3 プラットフォームに自動投稿する cron bot - 個別
ジャンルの : ジム・オフライン 導線 ボドゲカフェ・ ライブハウスへ の 物理 チラシ
3 つのアプリ、使ってみたい人へ
全部無料の
ジャンルは
同じ "あと 1 人" の
使ってみた
最後に — "レバレッジ" の再定義
個人開発の
1 つの
もし自分が「趣味 X のgenre.ts に 1 エントリ
興味が