- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. 技術(ぎじゅつ)スタック哲学(てつがく)
- 2. Stage 0: MVP(月額(げつがく)0〜50ドル)
- 3. Stage 1: Product-Market Fit(月額(げつがく)200〜500ドル)
- 4. Stage 2: Growth(月額(げつがく)2K〜10Kドル)
- 5. Stage 3: Scale(月額(げつがく)10Kドル以上(いじょう))
- 6. フロントエンドスタック
- 7. バックエンドスタック
- 8. データベース
- 9. インフラストラクチャ
- 10. 採用(さいよう)と技術(ぎじゅつ)スタック
- 11. よくある失敗(しっぱい)
- 12. 実例(じつれい)
- 13. クイズ
- 参考(さんこう)資料(しりょう)
1. 技術(ぎじゅつ)スタック哲学(てつがく)
1.1 Boring Technology原則(げんそく)
Dan McKinleyの「Choose Boring Technology」エッセイは、スタートアップの技術(ぎじゅつ)選択(せんたく)のバイブルです。核心的(かくしんてき)な主張(しゅちょう)はこうです:
イノベーショントークン: すべてのチームには限(かぎ)られた数(かず)の「イノベーショントークン」があります。新(あたら)しく検証(けんしょう)されていない技術(ぎじゅつ)を採用(さいよう)するたびにトークンを消費(しょうひ)します。ビジネス自体(じたい)がすでに1つのイノベーションなので、技術(ぎじゅつ)スタックでまで冒険(ぼうけん)する必要(ひつよう)はありません。
┌─────────────────────────────────────────────────────┐
│ イノベーショントークン配分例 │
├─────────────────────────────────────────────────────┤
│ │
│ 保有イノベーショントークン: 3個 │
│ │
│ 良い例: │
│ ├── ビジネスモデル (革新) ......... トークン1個使用 │
│ ├── PostgreSQL (退屈な技術) ....... トークン0個 │
│ ├── Next.js (実績あり) ........... トークン0個 │
│ ├── 独自MLパイプライン (革新) ..... トークン1個使用 │
│ └── 残りトークン: 1個(予備) │
│ │
│ 悪い例: │
│ ├── ビジネスモデル (革新) ......... トークン1個使用 │
│ ├── CockroachDB (新しい) ......... トークン1個使用 │
│ ├── Bun Runtime (新しい) ......... トークン1個使用 │
│ ├── 独自MLパイプライン (革新) ..... トークン不足! │
│ └── 残りトークン: 0個(危険) │
│ │
└─────────────────────────────────────────────────────┘
1.2 Monolith First
Martin Fowlerが主張(しゅちょう)した「Monolith First」原則(げんそく)。マイクロサービスで始(はじ)めず、モノリスで始(はじ)めて必要(ひつよう)な時(とき)に分離(ぶんり)しましょう。
なぜモノリスが先(さき)なのか:
- サービス境界(きょうかい)を事前(じぜん)に正(ただ)しく分(わ)けることはほぼ不可能(ふかのう)
- 開発(かいはつ)速度(そくど)が速(はや)い(サービス間(かん)通信(つうしん)のオーバーヘッドなし)
- デバッグが容易(ようい)(単一(たんいつ)プロセス)
- トランザクション管理(かんり)が簡単(かんたん)(分散(ぶんさん)トランザクション不要(ふよう))
- デプロイがシンプル(1つのアーティファクト)
┌──────────────────────────────────────────────────┐
│ アーキテクチャ進化パス │
│ │
│ Stage 0: モノリス (MVP) │
│ ├── 単一のNext.jsアプリ │
│ ├── 単一のDB (Supabase) │
│ └── 単一のデプロイ (Vercel) │
│ │ │
│ ▼ (PMF達成) │
│ Stage 1: モジュラーモノリス │
│ ├── ドメイン別モジュール分離 │
│ ├── 内部API境界の定義 │
│ └── DBスキーマ分離開始 │
│ │ │
│ ▼ (チーム10名+, トラフィック急増) │
│ Stage 2: 選択的サービス分離 │
│ ├── ボトルネックサービスのみ分離 │
│ ├── イベント駆動の非同期処理導入 │
│ └── キャッシュレイヤー追加 │
│ │ │
│ ▼ (チーム30名+, グローバル展開) │
│ Stage 3: MSA (必要な場合のみ) │
│ ├── 独立デプロイ可能なサービス群 │
│ ├── サービスメッシュ │
│ └── 集中型オブザーバビリティ │
└──────────────────────────────────────────────────┘
1.3 PaaS-First戦略(せんりゃく)
インフラ管理(かんり)に時間(じかん)を使(つか)わないでください。PaaS(Platform as a Service)で始(はじ)めて、必要(ひつよう)な時(とき)にIaaSに移行(いこう)しましょう。
| アプローチ | インフラ管理時間 | 柔軟性 | コスト(初期) | コスト(成長期) |
|---|---|---|---|---|
| PaaS (Vercel, Railway) | 最小 | 低 | 無料〜50ドル | 高くなる可能性 |
| Managed (AWS ECS, GKE) | 中 | 中 | 100〜500ドル | 中 |
| Self-managed (K8s on EC2) | 高 | 高 | 200〜1000ドル | 最適化可能 |
核心(かくしん)原則(げんそく): 初期(しょき)は開発(かいはつ)速度(そくど)がコスト最適化(さいてきか)より重要(じゅうよう)。PMFを見(み)つける前(まえ)にインフラ最適化(さいてきか)に時間(じかん)を費(つい)やすのは時間(じかん)の無駄(むだ)です。
2. Stage 0: MVP(月額(げつがく)0〜50ドル)
2.1 推奨(すいしょう)スタック
┌───────────────────────────────────────────────┐
│ MVP技術スタック (2025) │
├───────────────────────────────────────────────┤
│ │
│ Frontend: Next.js 15 (App Router) │
│ Styling: Tailwind CSS + shadcn/ui │
│ Backend: Next.js API Routes / Server Actions│
│ Database: Supabase (PostgreSQL) │
│ Auth: Supabase Auth / NextAuth.js │
│ Storage: Supabase Storage / Cloudflare R2 │
│ Deploy: Vercel │
│ Analytics: PostHog (セルフホスト) / Plausible │
│ Payments: Stripe │
│ Email: Resend │
│ Monitoring: Vercel Analytics + Sentry │
│ │
│ 月額合計コスト: 0 ~ 50 USD │
│ (Vercel Free + Supabase Free Tier) │
│ │
└───────────────────────────────────────────────┘
2.2 Next.js + Supabaseプロジェクトセットアップ
# プロジェクト作成
npx create-next-app@latest my-startup --typescript --tailwind --app --src-dir
cd my-startup
# コア依存関係のインストール
npm install @supabase/supabase-js @supabase/ssr
npm install stripe @stripe/stripe-js
npm install resend
npm install zod react-hook-form @hookform/resolvers
# UIライブラリ
npx shadcn@latest init
npx shadcn@latest add button card input form dialog toast
# 開発ツール
npm install -D prettier eslint-config-prettier
npm install -D @types/node
2.3 Supabase設定(せってい)
// src/lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {
// Server Componentではset不可 - 無視
}
},
},
}
)
}
// src/lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
2.4 認証(にんしょう)の実装(じっそう)
// src/app/auth/login/page.tsx
'use client'
import { createClient } from '@/lib/supabase/client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
export default function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const router = useRouter()
const supabase = createClient()
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
alert(error.message)
} else {
router.push('/dashboard')
router.refresh()
}
setLoading(false)
}
const handleGoogleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
})
}
return (
<div className="flex min-h-screen items-center justify-center">
<form onSubmit={handleLogin} className="w-full max-w-md space-y-4 p-8">
<h1 className="text-2xl font-bold">ログイン</h1>
<input
type="email"
placeholder="メールアドレス"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full rounded border p-2"
required
/>
<input
type="password"
placeholder="パスワード"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full rounded border p-2"
required
/>
<button
type="submit"
disabled={loading}
className="w-full rounded bg-blue-600 p-2 text-white"
>
{loading ? 'ログイン中...' : 'ログイン'}
</button>
<button
type="button"
onClick={handleGoogleLogin}
className="w-full rounded border p-2"
>
Googleでログイン
</button>
</form>
</div>
)
}
2.5 Stripe決済(けっさい)連携(れんけい)
// src/app/api/stripe/checkout/route.ts
import { NextRequest, NextResponse } from 'next/server'
import Stripe from 'stripe'
import { createClient } from '@/lib/supabase/server'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: NextRequest) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { priceId } = await req.json()
const session = await stripe.checkout.sessions.create({
customer_email: user.email,
line_items: [{ price: priceId, quantity: 1 }],
mode: 'subscription',
success_url: `${req.nextUrl.origin}/dashboard?success=true`,
cancel_url: `${req.nextUrl.origin}/pricing?canceled=true`,
metadata: {
userId: user.id,
},
})
return NextResponse.json({ url: session.url })
}
2.6 MVPコスト分析(ぶんせき)
| サービス | 無料ティア | 有料切替時点 | コスト |
|---|---|---|---|
| Vercel | 100GB帯域幅、無制限デプロイ | トラフィック増加時 | Pro: 20ドル/月 |
| Supabase | 500MB DB、1GBストレージ | 使用量増加時 | Pro: 25ドル/月 |
| Stripe | 使用量ベース | 売上発生時 | 2.9% + 30セント/件 |
| Resend | 100通/日 | コンバージョンテスト時 | 20ドル/月 |
| Sentry | 5Kイベント/月 | エラー増加時 | 26ドル/月 |
| PostHog | 1Mイベント/月 | 分析高度化時 | 0ドル(セルフホスト) |
MVP合計(ごうけい)コスト: 月額(げつがく)0〜50ドル(有料(ゆうりょう)切替(きりかえ)前(まえ)までほぼ無料(むりょう))
3. Stage 1: Product-Market Fit(月額(げつがく)200〜500ドル)
3.1 PMF段階(だんかい)の技術(ぎじゅつ)変化(へんか)
PMFを見(み)つけた後(あと)、以下(いか)の領域(りょういき)でアップグレードが必要(ひつよう)になります:
┌───────────────────────────────────────────────┐
│ Stage 1 技術スタックアップグレード │
├───────────────────────────────────────────────┤
│ │
│ Database: Supabase Pro → より大きなインスタンス│
│ Cache: + Redis (Upstash) │
│ Queue: + BullMQ (Redisベース) │
│ Search: + Meilisearch / Typesense │
│ CDN: + Cloudflare │
│ Monitoring: + Better Stack / Grafana Cloud │
│ CI/CD: GitHub Actions高度化 │
│ Error: Sentry Pro │
│ │
│ 月額コスト: 200 ~ 500 USD │
└───────────────────────────────────────────────┘
3.2 Redisキャッシュの追加(ついか)
// src/lib/cache.ts
import { Redis } from '@upstash/redis'
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
})
export async function getCached<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 3600 // 1時間
): Promise<T> {
const cached = await redis.get<T>(key)
if (cached !== null) {
return cached
}
const data = await fetcher()
await redis.set(key, data, { ex: ttl })
return data
}
export async function invalidateCache(pattern: string) {
const keys = await redis.keys(pattern)
if (keys.length > 0) {
await redis.del(...keys)
}
}
3.3 データベース最適化(さいてきか)
-- インデックス最適化
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
CREATE INDEX CONCURRENTLY idx_orders_user_created
ON orders(user_id, created_at DESC);
CREATE INDEX CONCURRENTLY idx_products_category_status
ON products(category_id, status) WHERE status = 'active';
-- パーティショニング(イベントテーブルが大きくなった時)
CREATE TABLE events (
id BIGSERIAL,
user_id UUID NOT NULL,
event_type TEXT NOT NULL,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);
CREATE TABLE events_2025_01 PARTITION OF events
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
CREATE TABLE events_2025_02 PARTITION OF events
FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
4. Stage 2: Growth(月額(げつがく)2K〜10Kドル)
4.1 成長(せいちょう)段階(だんかい)の技術(ぎじゅつ)スタック
┌───────────────────────────────────────────────┐
│ Stage 2 技術スタック │
├───────────────────────────────────────────────┤
│ │
│ Compute: AWS ECS Fargate / GKE │
│ Database: RDS PostgreSQL Multi-AZ │
│ Cache: ElastiCache Redis Cluster │
│ CDN: CloudFront + S3 │
│ Queue: SQS / RabbitMQ │
│ Search: ElasticSearch / OpenSearch │
│ CI/CD: GitHub Actions + ArgoCD │
│ Monitoring: Datadog / Grafana Stack │
│ Log: CloudWatch / Loki │
│ IaC: Terraform │
│ │
│ チーム規模: 10〜30名 │
│ 月額コスト: 2,000 ~ 10,000 USD │
└───────────────────────────────────────────────┘
4.2 Kubernetesの導入(どうにゅう)
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: my-registry/api-server:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
4.3 Terraformインフラコード
# terraform/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
resource "aws_db_instance" "main" {
identifier = "my-startup-production"
engine = "postgres"
engine_version = "16.1"
instance_class = "db.r6g.large"
allocated_storage = 100
max_allocated_storage = 500
storage_encrypted = true
multi_az = true
backup_retention_period = 14
performance_insights_enabled = true
monitoring_interval = 60
}
resource "aws_elasticache_replication_group" "main" {
replication_group_id = "my-startup-cache"
description = "Redis cache cluster"
node_type = "cache.r6g.large"
num_cache_clusters = 2
automatic_failover_enabled = true
multi_az_enabled = true
engine_version = "7.0"
port = 6379
at_rest_encryption_enabled = true
transit_encryption_enabled = true
}
5. Stage 3: Scale(月額(げつがく)10Kドル以上(いじょう))
5.1 スケール段階(だんかい)アーキテクチャ
┌─────────────────────────────────────────────────────────┐
│ Stage 3 アーキテクチャ │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │CloudFront│───>│ ALB/NLB │───>│ K8s │ │
│ │ + WAF │ │ │ │ Cluster │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ┌─────────────────┼──────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌────────┐ │
│ │API Gateway│ │ Worker │ │ Cron │ │
│ │ Service │ │ Service │ │Service │ │
│ └─────┬─────┘ └─────┬─────┘ └───┬────┘ │
│ │ │ │ │
│ ┌─────────────┼───────────────┼────────────┤ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────┐ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │RDS │ │Redis │ │ Kafka │ │ S3 │ │
│ │Multi-AZ│ │Cluster │ │ Cluster │ │ │ │
│ └────────┘ └─────────┘ └──────────┘ └────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ElasticSearch│ │
│ │ Cluster │ │
│ └───────────┘ │
│ │
│ チーム規模: 30名以上 │
│ 月額コスト: 10,000 USD以上 │
└─────────────────────────────────────────────────────────┘
5.2 イベント駆動(くどう)アーキテクチャ(Kafka)
// src/events/producer.ts
import { Kafka, Partitioners } from 'kafkajs'
const kafka = new Kafka({
clientId: 'my-startup',
brokers: (process.env.KAFKA_BROKERS || '').split(','),
ssl: true,
sasl: {
mechanism: 'scram-sha-256',
username: process.env.KAFKA_USERNAME!,
password: process.env.KAFKA_PASSWORD!,
},
})
const producer = kafka.producer({
createPartitioner: Partitioners.DefaultPartitioner,
idempotent: true,
})
export async function publishEvent(topic: string, event: {
type: string
payload: Record<string, unknown>
}) {
await producer.connect()
const message = {
key: event.payload.id as string || crypto.randomUUID(),
value: JSON.stringify({
...event,
timestamp: new Date().toISOString(),
version: '1.0',
}),
headers: {
'event-type': event.type,
'content-type': 'application/json',
},
}
await producer.send({ topic, messages: [message] })
}
6. フロントエンドスタック
6.1 フレームワーク比較(ひかく)
| 項目 | Next.js 15 | Remix | SvelteKit |
|---|---|---|---|
| レンダリング | SSR/SSG/ISR/RSC | SSR/CSR | SSR/SSG/CSR |
| サーバーコンポーネント | あり (RSC) | なし | なし (rune使用) |
| ルーティング | ファイルベース (App Router) | ファイルベース | ファイルベース |
| データフェッチ | Server Components, fetch | Loader/Action | Load関数 |
| デプロイ | Vercel最適化、どこでも | どこでも | どこでも |
| エコシステム | 非常に大きい | 中 | 成長中 |
| 学習曲線 | 中〜高 | 中 | 低〜中 |
| スタートアップ推奨 | 強く推奨 | 推奨 | 小規模チーム推奨 |
スタートアップの結論(けつろん): 2025年(ねん)時点(じてん)でNext.jsが最(もっと)も安全(あんぜん)な選択(せんたく)。エコシステム、採用(さいよう)市場(しじょう)、デプロイインフラすべての面(めん)で優位(ゆうい)。
6.2 状態(じょうたい)管理(かんり)
| ライブラリ | 複雑度 | 用途 | スタートアップ推奨 |
|---|---|---|---|
| React useState/useContext | 最小 | ローカル状態 | デフォルト |
| Zustand | 低 | グローバル状態 | 強く推奨 |
| Jotai | 低 | アトミック状態 | 推奨 |
| TanStack Query | 中 | サーバー状態 | 必須 |
| Redux Toolkit | 高 | 複雑な状態 | 非推奨(初期) |
// Zustandの例 - シンプルなストア
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface CartStore {
items: Array<{ id: string; name: string; price: number; quantity: number }>
addItem: (item: { id: string; name: string; price: number }) => void
removeItem: (id: string) => void
clearCart: () => void
total: () => number
}
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
items: [],
addItem: (item) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id)
if (existing) {
return {
items: state.items.map((i) =>
i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
),
}
}
return { items: [...state.items, { ...item, quantity: 1 }] }
}),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((i) => i.id !== id),
})),
clearCart: () => set({ items: [] }),
total: () =>
get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}),
{ name: 'cart-storage' }
)
)
7. バックエンドスタック
7.1 言語(げんご)比較(ひかく)(スタートアップ視点(してん))
| 項目 | Node.js (TS) | Go | Python | Java/Kotlin |
|---|---|---|---|---|
| 開発速度 | 速い | 中 | 速い | 遅い |
| 性能 | 中 | 非常に高い | 低い | 高い |
| 採用難易度 | 容易 | 中 | 容易 | 中 |
| フルスタック可能 | はい (Next.js) | いいえ | 限定的 | いいえ |
| エコシステム | 非常に大きい | 大きい | 非常に大きい (ML) | 非常に大きい |
| スタートアップ推奨 | 最高 | 性能重視時 | ML/データ時 | エンタープライズ |
7.2 スタートアップタイプ別(べつ)推奨(すいしょう)
B2C SaaS → Node.js (TypeScript) + Next.js
├── フルスタック1名で全てを構築可能
├── Vercelデプロイでインフラの心配を最小化
└── フロントエンド・バックエンドの型共有
B2B SaaS → Node.js or Go
├── APIパフォーマンスが重要ならGo
├── 迅速な機能開発が重要ならNode.js
└── どちらも良い選択
AI/MLスタートアップ → Python + TypeScript
├── MLパイプラインはPython
├── APIサーバーはFastAPI (Python) またはNext.js (TS)
└── フロントエンドはNext.js
フィンテック → Go or Java/Kotlin
├── 高い性能と安定性が必要
├── 静的型システムの安全性
└── 金融規制対応に強いエコシステム
8. データベース
8.1 PostgreSQL vs MySQL
| 項目 | PostgreSQL | MySQL |
|---|---|---|
| JSONサポート | JSONB(インデックス可能) | JSON(制限的) |
| 全文検索 | 内蔵(tsvector) | 内蔵(FULLTEXT) |
| 拡張性 | 豊富(PostGIS等) | 制限的 |
| レプリケーション | 論理的/物理的 | 物理的(デフォルト) |
| 性能 | 読み書きバランス | 読み取り最適化 |
| スタートアップ推奨 | 強く推奨 | MySQL経験チームならOK |
8.2 BaaS比較(ひかく)
| サービス | DBエンジン | 開始価格 | 特徴 | スタートアップ推奨 |
|---|---|---|---|---|
| Supabase | PostgreSQL | 無料 | Auth、Storage、Realtime含む | MVP最高 |
| Neon | PostgreSQL | 無料 | サーバーレス、ブランチング | 良い |
| PlanetScale | MySQL (Vitess) | 無料 | ブランチング、無停止スキーマ変更 | 良い |
| Turso | SQLite (libSQL) | 無料 | エッジ、非常に高速 | 小規模アプリ |
| CockroachDB | PostgreSQL互換 | 有料 | 分散SQL、グローバル | 大規模アプリ |
8.3 Redis導入(どうにゅう)のタイミング
Redis導入が必要な時:
├── 同じクエリが毎秒100回以上実行される時
├── APIレスポンス時間が500msを超える時
├── セッション管理が必要な時
├── リアルタイム機能(リーダーボード、カウンター等)が必要な時
├── レートリミットが必要な時
└── 分散ロックが必要な時
Redisがまだ不要な時:
├── DAU 1,000以下
├── DBクエリが20ms以内
├── Next.jsの組み込みキャッシュで十分な時
└── コストが制約になる時
9. インフラストラクチャ
9.1 PaaSからIaaSへの移行(いこう)タイミング
Vercel/RailwayからAWS/GCPへ移行すべき時:
1. コスト閾値
├── Vercel Proのコストが月500ドルを超える時
├── 帯域幅コストがコンピューティングより高い時
└── 関数実行時間の制限に引っかかる時
2. 技術的要件
├── ロングランニングプロセスが必要な時(5分以上)
├── WebSocketがコア機能の時
├── GPUコンピューティングが必要な時
└── カスタムネットワーク構成が必要な時
3. 規制/コンプライアンス
├── データ保存場所の規制
├── SOC 2、HIPAA等の認証が必要な時
└── VPC隔離が必要な時
移行不要な場合:
├── 月額コスト500ドル以下
├── サーバーレスアーキテクチャで十分
├── DevOps専任者がいない
└── トラフィックが予測可能で安定
10. 採用(さいよう)と技術(ぎじゅつ)スタック
10.1 技術(ぎじゅつ)スタックが採用(さいよう)に与(あた)える影響(えいきょう)
技術(ぎじゅつ)スタックの選択(せんたく)は採用(さいよう)プールを直接(ちょくせつ)決定(けってい)します:
| 技術 | 開発者プール | ジュニア比率 | シニア採用難易度 |
|---|---|---|---|
| React/Next.js | 非常に大きい | 高い | 中 |
| TypeScript | 大きい | 中 | 中 |
| Python | 非常に大きい | 高い | ML分野で困難 |
| Go | 小さい | 低い | 高い |
| Rust | 非常に小さい | 非常に低い | 非常に高い |
| Java/Kotlin | 非常に大きい | 高い | 中 |
10.2 採用(さいよう)しやすい技術(ぎじゅつ)スタック
採用が容易なスタック:
├── Next.js + TypeScript(フロント)
├── Node.js + Express/Nest.js(バックエンド)
├── PostgreSQL / MySQL(DB)
├── Redis(キャッシュ)
├── Docker + GitHub Actions(DevOps)
└── AWS(インフラ)
採用が困難なスタック:
├── Svelte / SolidJS(フロント)
├── Rust / Elixir(バックエンド)
├── CockroachDB / ScyllaDB(DB)
├── Nomad / Consul(オーケストレーション)
└── Pulumi / CDK(IaC)
11. よくある失敗(しっぱい)
11.1 履歴書(りれきしょ)駆動(くどう)開発(かいはつ)(Resume-Driven Development)
悪い意思決定パターン:
├── "K8sの経験を積みたい" → K8s導入 (DAU 100)
├── "Rustがトレンドだから" → RustでAPIサーバー (チーム2名)
├── "MSAが正統だから" → 3名で10サービス運用
├── "GraphQLが良いらしい" → シンプルCRUDにGraphQL
└── "Googleがモノレポを使っている" → 3名チームにTurborepo
正しい意思決定:
├── "この問題を解決する最もシンプルな方法は?"
├── "6ヶ月後もメンテナンスできるか?"
├── "チームメンバーが既に慣れた技術か?"
├── "採用が容易な技術か?"
└── "移行コストは許容範囲か?"
11.2 早期(そうき)最適化(さいてきか)
- DBインデックス40個(こ)を事前(じぜん)作成(さくせい)(クエリパターンも分(わ)からない状態(じょうたい)で)
- 3段階(だんかい)のキャッシュレイヤー構築(こうちく)(トラフィック100人(にん)/日(にち))
- グローバルCDN設定(せってい)(ユーザー全員(ぜんいん)が1つの国(くに))
- ロードバランサー + Auto Scaling(CPU使用率(しようりつ)5%)
11.3 早期(そうき)マイクロサービス
3名(めい)のチームがマイクロサービスを導入(どうにゅう)すると:
- サービス間(かん)通信(つうしん)のデバッグに丸一日(まるいちにち)
- 分散(ぶんさん)トランザクション問題(もんだい)でデータ不整合(ふせいごう)
- 10個(こ)のデプロイパイプラインの管理(かんり)
- モニタリングダッシュボードのセットアップだけで2週間(しゅうかん)
- 結果(けっか):開発(かいはつ)速度(そくど)50%減少(げんしょう)、インフラコスト300%増加(ぞうか)
バランス: MVP段階(だんかい)では80/20ルールを適用(てきよう)。20%のコアコードに80%の品質(ひんしつ)を投資(とうし)しましょう。
12. 実例(じつれい)
12.1 Vercelスタック
Vercel (Next.js開発元):
├── Frontend: Next.js(自社製品)
├── Backend: Go + Node.js
├── Database: PlanetScale (MySQL) → Neon (PostgreSQL)
├── Cache: Redis (Upstash)
├── Monorepo: Turborepoベース
├── Deploy: 自社プラットフォーム
├── Monitoring: 自社構築 + Datadog
└── 特徴: 自社製品を積極活用(ドッグフーディング)
12.2 Linearスタック
Linear (プロジェクト管理ツール):
├── Frontend: React + TypeScript
├── Backend: Node.js + TypeScript
├── Database: PostgreSQL
├── Cache: Redis
├── Sync: CRDTベースのリアルタイム同期
├── Deploy: Google Cloud
├── Monitoring: 自社構築
└── 特徴: オフラインファースト、ローカルデータ優先
12.3 Cal.comスタック
Cal.com (オープンソースのスケジュール管理):
├── Frontend: Next.js + TypeScript
├── Backend: tRPC + Prisma
├── Database: PostgreSQL
├── Auth: NextAuth.js
├── Deploy: Vercel
├── Monorepo: Turborepo
└── 特徴: オープンソース、モノレポ、tRPCで型安全
12.4 示唆点(しさてん)
これらの事例(じれい)の共通点(きょうつうてん):
- **実績(じっせき)のある技術(ぎじゅつ)**が主流(しゅりゅう)(PostgreSQL、Redis、TypeScript)
- モノリスまたはモジュラーモノリスで開始(かいし)
- TypeScriptをデフォルトとして採用(さいよう)
- PaaS優先(ゆうせん)(Vercel、Google Cloud マネージド)
- コア差別化(さべつか)ポイントにのみイノベーション投資(とうし)
13. クイズ
Q1: Boring Technology原則における「イノベーショントークン」とは何で、なぜ節約すべきですか?
A: イノベーショントークンはDan McKinleyが提案(ていあん)した概念(がいねん)で、チームが対処(たいしょ)できる新(あたら)しい/検証(けんしょう)されていない技術(ぎじゅつ)の数(かず)を意味(いみ)します。すべてのチームには限(かぎ)られた数(通常(つうじょう)3個(こ)程度(ていど))のトークンがあり、検証(けんしょう)されていない技術(ぎじゅつ)を導入(どうにゅう)するたびに1つ消費(しょうひ)します。
節約(せつやく)すべき理由(りゆう):スタートアップのビジネスモデル自体(じたい)がすでに1つのイノベーションなのでトークン1個(こ)を使用(しよう)します。残(のこ)りのトークンを技術(ぎじゅつ)スタックですべて消費(しょうひ)すると、問題(もんだい)発生時(はっせいじ)に対応(たいおう)する余力(よりょく)がなくなります。
Q2: MVP段階でなぜNext.js + Supabase + Vercelの組み合わせが推奨されるのですか?
A: この組(く)み合(あ)わせが推奨(すいしょう)される理由(りゆう):
- 開発(かいはつ)速度(そくど): Next.jsのフルスタック機能(きのう)でフロント/バック分離(ぶんり)なしに開発(かいはつ)可能(かのう)
- コスト: 3つのサービスすべてに寛大(かんだい)な無料(むりょう)ティアあり(月額(げつがく)0〜50ドル)
- インフラ管理(かんり)ゼロ: VercelがデプロイからCDN、SSLまですべて処理(しょり)、SupabaseがDB、Auth、Storageを処理(しょり)
- 型(かた)安全性(あんぜんせい): TypeScriptでフルスタックの型(かた)共有(きょうゆう)
- 拡張性(かくちょうせい): SupabaseはPostgreSQLベースなので将来(しょうらい)のマイグレーションが容易(ようい)
核心(かくしん)はPMFを見(み)つける前(まえ)にインフラに時間(じかん)を使(つか)わないことです。
Q3: PaaS(Vercel)からIaaS(AWS)への移行はいつすべきですか?
A: 移行(いこう)判断(はんだん)基準(きじゅん):
- コスト: Vercelのコストが月(つき)500ドルを超(こ)える時(とき)
- 技術的(ぎじゅつてき)制約(せいやく): 5分(ぷん)以上(いじょう)のロングランニングプロセス、WebSocketベースのリアルタイム機能(きのう)、GPUコンピューティングが必要(ひつよう)な時(とき)
- 規制(きせい): データ保存(ほぞん)場所(ばしょ)の規制(きせい)、SOC 2/HIPAA認証(にんしょう)、VPC隔離(かくり)が必要(ひつよう)な時(とき)
- チーム: DevOps/インフラ専任(せんにん)人員(じんいん)が確保(かくほ)された時(とき)
核心(かくしん)は「必要(ひつよう)な時(とき)に」移行(いこう)することであり、事前(じぜん)に準備(じゅんび)することではありません。
Q4: スタートアップが最初からマイクロサービスを採用すべきでない理由は?
A: 最初(さいしょ)からマイクロサービスを導入(どうにゅう)すべきでない理由(りゆう):
- サービス境界(きょうかい)の失敗(しっぱい): PMF前(まえ)はドメインが常(つね)に変化(へんか)するため、サービス境界(きょうかい)を正(ただ)しく定(さだ)めることが不可能(ふかのう)
- 運用(うんよう)オーバーヘッド: サービス間(かん)通信(つうしん)、分散(ぶんさん)トランザクション、サービスディスカバリーなどの複雑性(ふくざつせい)が爆発(ばくはつ)
- デバッグの困難(こんなん)さ: 分散(ぶんさん)システムのデバッグはモノリスの10倍(ばい)困難(こんなん)
- インフラコスト: 各(かく)サービスに個別(こべつ)のデプロイパイプライン、モニタリング、ロギングが必要(ひつよう)(コスト3倍(ばい)以上(いじょう))
- チーム規模(きぼ)のミスマッチ: 3〜5名(めい)のチームが10サービスを管理(かんり)するのは非現実的(ひげんじつてき)
正(ただ)しいアプローチ:モノリスで開始(かいし)し、ドメインの理解(りかい)が十分(じゅうぶん)になった後(あと)、ボトルネックのみを選択的(せんたくてき)にサービス分離(ぶんり)。
Q5: 技術スタックの選択は採用にどのような影響を与えますか?
A: 技術(ぎじゅつ)スタックは採用(さいよう)に直接的(ちょくせつてき)な影響(えいきょう)を与(あた)えます:
- 採用(さいよう)プールの規模(きぼ): React/TypeScript開発者(かいはつしゃ)は非常(ひじょう)に多(おお)いが、Rust/Elixir開発者(かいはつしゃ)は極少数(ごくしょうすう)
- 採用(さいよう)速度(そくど): 人気(にんき)のスタックは応募者(おうぼしゃ)が多(おお)く、迅速(じんそく)な採用(さいよう)が可能(かのう)
- 採用(さいよう)コスト: 希少(きしょう)な技術(ぎじゅつ)にはプレミアム年俸(ねんぽう)が必要(ひつよう)
- オンボーディング: 既知(きち)の技術(ぎじゅつ)なら1週間(しゅうかん)以内(いない)、新(あたら)しい技術(ぎじゅつ)なら1ヶ月(かげつ)以上(いじょう)
- 離職率(りしょくりつ): 市場(しじょう)で人気(にんき)のない技術(ぎじゅつ)はキャリア不安(ふあん)から離職率(りしょくりつ)が上昇(じょうしょう)
推奨(すいしょう): TypeScript + React + PostgreSQL + AWSは最(もっと)も採用(さいよう)しやすい組(く)み合(あ)わせです。
参考(さんこう)資料(しりょう)
- McKinley, D. "Choose Boring Technology." https://boringtechnology.club/
- Fowler, M. "Monolith First." Martin Fowler's blog.
- Next.js Documentation (2025). https://nextjs.org/docs
- Supabase Documentation. https://supabase.com/docs
- Vercel Documentation. https://vercel.com/docs
- Stripe Documentation. https://stripe.com/docs
- Terraform AWS Provider Documentation.
- "The Twelve-Factor App." https://12factor.net/
- Kleppmann, M. "Designing Data-Intensive Applications." O'Reilly.
- Newman, S. "Building Microservices, 2nd Edition." O'Reilly.
- Basecamp. "Getting Real." https://basecamp.com/gettingreal
- Linear Engineering Blog. https://linear.app/blog
- Cal.com GitHub Repository. https://github.com/calcom/cal.com
- AWS Startup Guides. https://aws.amazon.com/startups/
- Google for Startups Cloud Program. https://cloud.google.com/startup
- Y Combinator Library - Technical Decisions. https://www.ycombinator.com/library