PMがコミュニティSNSを1人で作った話|Pulse Commons 開発記
- 個人開発に興味があるエンジニア・PM
- Next.js + Supabase でプロダクトを作りたい人
- 「SNSの会話が流れて消える」ことにモヤモヤしている人
- AIを活用した開発フローに関心がある人
「SNSって、なんで会話が流れて消えるんだろう」。ふとした違和感から始めて、2週間後にはコミュニティSNSをリリースしていた。
自分の肩書きはプロダクトマネージャーだ。エンジニアではない。それでも「作りたい」と思ったものを形にできる時代になった。この記事は、構想から設計、技術選定、実装、リリースまでの記録であり、個人開発のひとつのリアルだ。
なぜSNSを作ろうと思ったのか
きっかけは、Xのタイムラインを眺めているときの違和感だった。
良い議論が生まれても、数時間後には流れて消える。ブックマークしても、文脈ごと保存されるわけではない。Discordのサーバーに入っても、チャンネルが多すぎて追いきれない。Slackは仕事の延長線上で、気軽に「最近読んだ本」の話はしづらい。
「目的のある場所」で「文脈を保ったまま」会話できるSNSがほしい。
それが Pulse Commons の出発点だ。
Pulse Commons の設計思想
既存SNSの「流れる問題」に対して、3つの仕組みで答えることにした。
Room(場):目的のある空間
タイムラインの代わりに、テーマごとの Room を用意する。「今読んでいる本」「AIツール実験室」「個人開発の進捗」。目的が明確な場所なら、何を投稿すればいいか迷わない。
作法(ルール):場の空気をつくる
各Roomには 作法 を設定できる。「感想を添えて共有しよう」「ネタバレは警告をつけよう」。シンプルな約束事が、場の文化になる。モデレーションを人力でやるのではなく、仕組みとして場の空気を守る設計だ。
投げかけ(テーマ):会話のきっかけ
「最近読んで一番印象に残った一節は?」。Room内で 投げかけ を設定すると、メンバーは何を投稿すればいいか迷わなくなる。白紙の恐怖をなくすことが、投稿のハードルを下げる鍵だった。
いいねの代わりに4種のリアクション
「いいね」は便利だが、何に対して「いいね」なのかわからない。Pulse Commons では、4種類のリアクションで反応する。
- 学び — 新しい知識を得た
- なるほど — 視点に共感した
- 試したい — 実践してみたくなった
- がんばれ! — 応援したい
受け取る側にとって、「いいね」より「学びになった」のほうが嬉しい。質のあるフィードバックが、質のある投稿を生むサイクルを目指している。
Sessions:オンラインで集まる理由
Room内で勉強会やもくもく会を開催できる Sessions 機能も実装した。日時とミーティングURL(Google Meet、Zoom等)を設定して告知、参加ボタンで人数を事前に把握、開催中はリアルタイムで表示される。
「なんとなく集まる」ではなく「目的を持って集まる」場をつくりたかった。
技術選定:Next.js + Supabase + Vercel
個人開発で最も重要なのは「速度」だ。完璧な技術選定よりも、素早くリリースして反応を見ることを優先した。
フロントエンド:Next.js(App Router)
React Server Componentsの恩恵が大きい。認証状態の確認、初期データのフェッチをサーバーサイドで完結させ、クライアントには必要最小限のJSだけを送る。App Routerの layout.tsx でサイドバーやナビゲーションを共通化し、ルーティングは直感的に管理できている。
バックエンド:Supabase
Supabaseを選んだ理由はシンプルだ。1人で運用できるから。
- PostgreSQL: RLS(Row Level Security)でデータアクセス制御をDB層で完結
- Auth: メール認証をほぼゼロコードで実装
- Realtime: 通知バッジのリアルタイム更新に活用
- Storage: 画像アップロード(プロフィール、投稿画像)を管理
Firebase も候補だったが、SQLの柔軟性とRLSの設計思想がしっくりきた。複雑なクエリ(リアクションの集計、フィード構築)をSQL一発で書けるのは、リレーショナルDBの強みだ。
CSS:Tailwind CSS v4
Tailwind v4にはひとつ罠がある。@import "tailwindcss" が生成するCascade Layers(@layer base/utilities)と、レイヤー外のCSSの優先度問題だ。body > * のような要素セレクタがTailwindの fixed や z-* を上書きしてしまう。解決策は、要素セレクタを @layer base に入れること。これに気づくまで半日溶かした。
レイヤー外(unlayered)のCSSは全レイヤーより優先度が高い。要素セレクタやワイルドカードセレクタは必ず @layer base 内に配置すること。
ホスティング:Vercel
Next.jsとの親和性は言うまでもない。git push でデプロイが走り、プレビュー環境も自動生成される。個人開発にはこの手軽さが不可欠だ。
DB設計で考えたこと
SNSのDB設計は、一見シンプルに見えて奥が深い。いくつか判断が必要だったポイントを記録しておく。
リアクションのカウント:非正規化の判断
リアクションは reactions テーブルに (user_id, post_id, type) の複合主キーで格納している。1ユーザーが1つの投稿に対して複数タイプのリアクションを付けられる設計だ。
カウントの取得方法には2つの選択肢があった。
- 毎回COUNT集計 — 正規化としては正しいが、フィード表示のたびにJOIN+GROUP BYが走る
- 投稿テーブルに非正規化カウントを持つ —
learn_count,insight_count,try_count,cheer_countカラムを追加
後者を選んだ。PostgreSQLのトリガーで INSERT/DELETE 時にカウントを自動更新する。フィードのクエリがシンプルになり、パフォーマンスも良い。「正規化原理主義」より「ユーザー体験」を優先した判断だ。
通知のスパム防止
リアクションが4種類あるということは、1人が1投稿に4回リアクションできるということだ。そのたびに通知が飛んだらスパムになる。
解決策として、通知トリガー内で SELECT を挟み、同じ (actor_id, post_id) ペアの通知が既にあれば新規作成しない設計にした。シンプルだが効果的だ。
RLSでのレート制限
Supabaseの RLS ポリシー内で、一定時間内のリアクション数を制限している。1分間に120件を超えるリアクションは弾く。これにより、botや悪意あるスクリプトからの大量リクエストをDB層で防げる。
Claude Code との開発フロー
この記事で正直に書いておきたいことがある。Pulse Commons の実装の大部分は、Claude Code と一緒に書いた。
自分の役割は「何を作るか」「なぜ作るか」「どう設計するか」を決めること。実装の詳細はClaude Codeとペアプログラミングのように進めた。マイグレーションSQL、コンポーネント実装、リファクタリング。PMとしての設計力と、AIの実装力の組み合わせだ。
ただし、丸投げではない。RLSポリシー、トリガーの挙動、パフォーマンスの考慮点。こうした判断はすべて自分で行っている。AIに「何を作るか」を任せるのではなく、「どう実装するか」を一緒に考えるスタイルだ。
AIに「全部お任せ」ではなく、設計判断は自分で行い、実装をAIと協働するのが現時点でのベストバランスだと感じている。「PMがコードを書く」のではなく「PMがプロダクトを作る」という感覚に近い。
リリースして思うこと
正直に言えば、いまPulse Commonsにはまだほとんど人がいない。
でも、それは恥ずかしいことではないと思っている。最初の10人が場の空気をつくる。最初の100投稿が文化になる。プロダクトは、使う人がいて初めて完成する。
個人開発のプロダクトが「完成」するタイミングは、コードを書き終えた瞬間ではない。誰かが使い始めて、想定していなかった使い方をしてくれた瞬間だ。
まとめ:個人開発の現在地
2026年の個人開発は、こんな景色になっている。
- アイデアからリリースまで2週間 — フレームワークとBaaSの進化
- PM1人でSNSを作れる — コードを書く能力より、設計する能力が価値を持つ
- AIは「相棒」になった — 丸投げではなく協働するスタイルが最も生産的
技術的な壁はどんどん低くなっている。残る壁は「何を作るか」と「なぜ作るか」だけだ。
Pulse Commons はまだ始まったばかりだ。興味があれば、最初の住人になってほしい。
Pulse Commons を見てみる → pulse.and-and.dev
本記事に記載の情報は2026年2月時点のものです。サービス内容・機能は今後変更される場合があります。