Chapter 2: Architecture Overview

Now that we understand the challenges, let’s explore different architectural approaches for building a comment system. Each architecture has trade-offs in complexity, cost, scalability, and features.

Architecture Comparison

┌────────────────────────────────────────────────────────────────────────┐
│                    ARCHITECTURE COMPARISON MATRIX                       │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Architecture      Cost     Complexity   Scalability   Latency         │
│  ─────────────────────────────────────────────────────────────────     │
│  Git-based         Free     Low          Low           High            │
│  Serverless        Low      Medium       High          Medium          │
│  Traditional API   Medium   Medium       Medium        Low             │
│  Edge Functions    Low      Medium       High          Very Low        │
│  Hybrid/JAMstack   Low      High         High          Low             │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

Architecture 1: Git-Based Comments

Store comments directly in your repository. Examples: Staticman, Utterances, Giscus.

┌─────────────────────────────────────────────────────────────────────────┐
│                     GIT-BASED ARCHITECTURE                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌──────────────┐      ┌──────────────┐      ┌──────────────┐           │
│  │              │      │              │      │              │           │
│  │    User      │──────│   Bot/API    │──────│    Git       │           │
│  │   Browser    │      │   Service    │      │    Repo      │           │
│  │              │      │              │      │              │           │
│  └──────────────┘      └──────────────┘      └──────────────┘           │
│         │                     │                     │                    │
│         │ 1. Submit           │ 2. Create PR        │                    │
│         │    Comment          │    or Commit        │                    │
│         │                     │                     │                    │
│         │                     │              ┌──────┴──────┐             │
│         │                     │              │             │             │
│         │                     │              │  Build      │             │
│         │                     │              │  Trigger    │             │
│         │                     │              │             │             │
│         │                     │              └──────┬──────┘             │
│         │                     │                     │                    │
│         │                     │              ┌──────┴──────┐             │
│         │                     │              │             │             │
│         │                     │              │  Static     │             │
│         │                     │              │  Site Gen   │             │
│         │                     │              │             │             │
│         │                     │              └──────┬──────┘             │
│         │                     │                     │                    │
│         │                     │                     ▼                    │
│         │                     │              ┌──────────────┐            │
│         │                     │              │              │            │
│         │◄────────────────────┼──────────────│   Deploy     │            │
│         │  3. See comment     │              │   (CDN)      │            │
│         │     on next build   │              │              │            │
│         │                     │              └──────────────┘            │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

How It Works

  1. User submits comment via form
  2. A service (Staticman) or bot creates a PR/commit to your repo
  3. New comment triggers site rebuild
  4. Comments become part of static site

Implementation Example (Staticman-style)

File structure:

your-site/
├── content/
│   └── posts/
│       └── my-article.md
└── data/
    └── comments/
        └── my-article/
            ├── comment-1.yml
            ├── comment-2.yml
            └── comment-3.yml

Comment file format:

# data/comments/my-article/comment-1.yml
_id: "a1b2c3d4"
name: "John Doe"
email_hash: "5d41402abc4b2a76b9719d911017c592"  # MD5 for Gravatar
date: 2025-01-15T10:30:00Z
message: "Great article! Really helped me understand the concept."
reply_to: ""  # Empty for top-level, ID for replies

Pros and Cons

Pros Cons
✅ Free hosting (GitHub/GitLab) ❌ Slow comment appearance (build time)
✅ Comments in version control ❌ Requires rebuild for each comment
✅ Easy backup/export ❌ Limited real-time features
✅ Works offline ❌ May expose email (or hash)
✅ No external dependencies ❌ Spam = polluted git history

Best For


Architecture 2: Serverless Functions

Use cloud functions to handle comment operations. Examples: AWS Lambda, Vercel Functions, Netlify Functions.

┌─────────────────────────────────────────────────────────────────────────┐
│                    SERVERLESS ARCHITECTURE                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                         STATIC SITE (CDN)                          │  │
│  │  ┌─────────────────────────────────────────────────────────────┐  │  │
│  │  │                      Your Blog Post                          │  │  │
│  │  │                                                              │  │  │
│  │  │  [Article Content]                                           │  │  │
│  │  │                                                              │  │  │
│  │  │  ┌────────────────────────────────────────────────────────┐ │  │  │
│  │  │  │               Comment Widget (JS)                       │ │  │  │
│  │  │  │                                                         │ │  │  │
│  │  │  │   fetch('/api/comments?page=my-article')                │ │  │  │
│  │  │  │                                                         │ │  │  │
│  │  │  └────────────────────────────────────────────────────────┘ │  │  │
│  │  └─────────────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────────────┘  │
│                                      │                                   │
│                                      │ API Calls                         │
│                                      ▼                                   │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                    SERVERLESS FUNCTIONS                            │  │
│  │                                                                    │  │
│  │  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐       │  │
│  │  │                │  │                │  │                │       │  │
│  │  │  GET /api/     │  │  POST /api/    │  │  DELETE /api/  │       │  │
│  │  │  comments      │  │  comments      │  │  comments/:id  │       │  │
│  │  │                │  │                │  │                │       │  │
│  │  └───────┬────────┘  └───────┬────────┘  └───────┬────────┘       │  │
│  │          │                   │                   │                │  │
│  │          │      ┌────────────┴────────────┐      │                │  │
│  │          │      │                         │      │                │  │
│  │          │      │     Spam Filter         │      │                │  │
│  │          │      │     (Akismet/Custom)    │      │                │  │
│  │          │      │                         │      │                │  │
│  │          │      └────────────┬────────────┘      │                │  │
│  │          │                   │                   │                │  │
│  │          └───────────────────┼───────────────────┘                │  │
│  │                              │                                    │  │
│  └──────────────────────────────┼────────────────────────────────────┘  │
│                                 │                                        │
│                                 ▼                                        │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                         DATABASE                                   │  │
│  │                                                                    │  │
│  │    Options:                                                        │  │
│  │    • DynamoDB (AWS)                                               │  │
│  │    • FaunaDB                                                      │  │
│  │    • PlanetScale (MySQL)                                          │  │
│  │    • Supabase (PostgreSQL)                                        │  │
│  │    • MongoDB Atlas                                                │  │
│  │    • Cloudflare D1 (SQLite)                                       │  │
│  │                                                                    │  │
│  └───────────────────────────────────────────────────────────────────┘  │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Implementation Example (Vercel + Supabase)

// api/comments/index.js (Vercel Serverless Function)
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_KEY
);

export default async function handler(req, res) {
  // CORS headers
  res.setHeader('Access-Control-Allow-Origin', 'https://yourblog.com');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  
  if (req.method === 'OPTIONS') {
    return res.status(200).end();
  }

  const { page_id } = req.query;

  if (req.method === 'GET') {
    // Fetch comments for a page
    const { data, error } = await supabase
      .from('comments')
      .select('*')
      .eq('page_id', page_id)
      .eq('status', 'approved')
      .order('created_at', { ascending: true });

    if (error) {
      return res.status(500).json({ error: error.message });
    }
    return res.status(200).json(data);
  }

  if (req.method === 'POST') {
    const { author, email, content, parent_id } = req.body;

    // Basic validation
    if (!author || !content || !page_id) {
      return res.status(400).json({ error: 'Missing required fields' });
    }

    // Insert comment (pending moderation)
    const { data, error } = await supabase
      .from('comments')
      .insert({
        page_id,
        parent_id: parent_id || null,
        author,
        email,
        content,
        status: 'pending',  // Or 'approved' if auto-approve
        ip_address: req.headers['x-forwarded-for'] || req.socket.remoteAddress,
        user_agent: req.headers['user-agent']
      })
      .select();

    if (error) {
      return res.status(500).json({ error: error.message });
    }
    return res.status(201).json(data[0]);
  }

  return res.status(405).json({ error: 'Method not allowed' });
}

Pros and Cons

Pros Cons
✅ Pay-per-use pricing ❌ Cold start latency
✅ Auto-scaling ❌ Vendor lock-in
✅ Real-time comments ❌ Database costs separate
✅ Easy deployment ❌ More moving parts
✅ Good for variable traffic ❌ Debugging complexity

Cost Estimate

┌─────────────────────────────────────────────────────────────┐
│         SERVERLESS COST ESTIMATE (10K views/month)          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Vercel (Hobby Plan)                                        │
│  ├── Functions: 100K invocations free         $0.00         │
│  └── Bandwidth: 100GB free                    $0.00         │
│                                                              │
│  Supabase (Free Tier)                                       │
│  ├── Database: 500MB                          $0.00         │
│  ├── API calls: Unlimited                     $0.00         │
│  └── Bandwidth: 2GB                           $0.00         │
│                                                              │
│  ─────────────────────────────────────────────────          │
│  TOTAL (10K views):                           $0.00/month   │
│                                                              │
│  ─────────────────────────────────────────────────          │
│  At 100K views:                               ~$5-10/month  │
│  At 1M views:                                 ~$25-50/month │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Architecture 3: Edge Functions

Run code at CDN edge locations for lowest latency. Examples: Cloudflare Workers, Deno Deploy.

┌─────────────────────────────────────────────────────────────────────────┐
│                      EDGE ARCHITECTURE                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│    User in               User in               User in                   │
│    New York              London                Tokyo                     │
│        │                    │                     │                      │
│        ▼                    ▼                     ▼                      │
│   ┌─────────┐          ┌─────────┐          ┌─────────┐                 │
│   │  Edge   │          │  Edge   │          │  Edge   │                 │
│   │ Worker  │          │ Worker  │          │ Worker  │                 │
│   │  (NYC)  │          │  (LHR)  │          │  (TYO)  │                 │
│   └────┬────┘          └────┬────┘          └────┬────┘                 │
│        │                    │                    │                       │
│        │                    │                    │                       │
│        │        ┌───────────┴───────────┐       │                       │
│        │        │                       │       │                       │
│        └────────►    Distributed DB     ◄───────┘                       │
│                 │   (Cloudflare D1 /    │                               │
│                 │    Turso / etc.)      │                               │
│                 │                       │                               │
│                 └───────────────────────┘                               │
│                                                                          │
│   Latency: ~20-50ms        ~20-50ms           ~20-50ms                  │
│   (vs 100-300ms for centralized)                                        │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Implementation Example (Cloudflare Workers + D1)

// worker.js - Cloudflare Worker with D1 Database
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    
    // CORS
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, DELETE',
          'Access-Control-Allow-Headers': 'Content-Type',
        },
      });
    }

    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Content-Type': 'application/json',
    };

    // GET /api/comments?page_id=xxx
    if (request.method === 'GET' && url.pathname === '/api/comments') {
      const pageId = url.searchParams.get('page_id');
      
      const { results } = await env.DB.prepare(`
        SELECT id, author, content, created_at, parent_id
        FROM comments
        WHERE page_id = ? AND status = 'approved'
        ORDER BY created_at ASC
      `).bind(pageId).all();

      return new Response(JSON.stringify(results), { headers: corsHeaders });
    }

    // POST /api/comments
    if (request.method === 'POST' && url.pathname === '/api/comments') {
      const body = await request.json();
      const { page_id, author, email, content, parent_id } = body;

      // Spam check (simple example)
      if (await isSpam(content, env)) {
        return new Response(
          JSON.stringify({ error: 'Comment flagged as spam' }),
          { status: 400, headers: corsHeaders }
        );
      }

      const id = crypto.randomUUID();
      
      await env.DB.prepare(`
        INSERT INTO comments (id, page_id, parent_id, author, email, content, status, created_at)
        VALUES (?, ?, ?, ?, ?, ?, 'pending', datetime('now'))
      `).bind(id, page_id, parent_id || null, author, email, content).run();

      return new Response(
        JSON.stringify({ id, message: 'Comment submitted for moderation' }),
        { status: 201, headers: corsHeaders }
      );
    }

    return new Response('Not Found', { status: 404 });
  },
};

async function isSpam(content, env) {
  // Simple spam detection - in production, use more sophisticated methods
  const spamPatterns = [
    /\b(viagra|casino|lottery|winner)\b/i,
    /https?:\/\/[^\s]+\.(ru|cn|xyz)/i,
    /(.)\1{10,}/,  // Repeated characters
  ];
  return spamPatterns.some(pattern => pattern.test(content));
}

D1 Schema

-- schema.sql
CREATE TABLE comments (
  id TEXT PRIMARY KEY,
  page_id TEXT NOT NULL,
  parent_id TEXT,
  author TEXT NOT NULL,
  email TEXT,
  content TEXT NOT NULL,
  status TEXT DEFAULT 'pending',
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
  ip_address TEXT,
  user_agent TEXT
);

CREATE INDEX idx_page_id ON comments(page_id);
CREATE INDEX idx_status ON comments(status);
CREATE INDEX idx_parent_id ON comments(parent_id);

Pros and Cons

Pros Cons
✅ Lowest latency globally ❌ Limited compute time
✅ No cold starts ❌ Restricted runtime APIs
✅ Very cheap at scale ❌ Database consistency challenges
✅ Built-in DDoS protection ❌ Learning curve
✅ Great free tier ❌ Limited debugging tools

Architecture 4: Traditional API Server

Self-hosted server with database. Classic approach with most flexibility.

┌─────────────────────────────────────────────────────────────────────────┐
│                    TRADITIONAL API ARCHITECTURE                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                     STATIC SITE                                    │  │
│  │                     (Netlify/Vercel/GitHub Pages)                  │  │
│  └────────────────────────────────┬──────────────────────────────────┘  │
│                                   │                                      │
│                                   │ HTTPS API Calls                      │
│                                   ▼                                      │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                     REVERSE PROXY                                  │  │
│  │                     (Nginx / Caddy / Traefik)                      │  │
│  │                                                                    │  │
│  │  • SSL Termination                                                 │  │
│  │  • Rate Limiting                                                   │  │
│  │  • Caching                                                         │  │
│  │                                                                    │  │
│  └────────────────────────────────┬──────────────────────────────────┘  │
│                                   │                                      │
│                                   ▼                                      │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                     API SERVER                                     │  │
│  │                     (Python/Node/Go/Rust)                          │  │
│  │                                                                    │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                │  │
│  │  │   Routes    │  │   Spam      │  │   Auth      │                │  │
│  │  │   Handler   │──│   Filter    │──│   Check     │                │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘                │  │
│  │         │                                                          │  │
│  │         ▼                                                          │  │
│  │  ┌─────────────┐                                                   │  │
│  │  │   ORM /     │                                                   │  │
│  │  │   Query     │                                                   │  │
│  │  └──────┬──────┘                                                   │  │
│  │         │                                                          │  │
│  └─────────┼─────────────────────────────────────────────────────────┘  │
│            │                                                             │
│            ▼                                                             │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                     DATABASE                                       │  │
│  │                     (PostgreSQL / SQLite / MySQL)                  │  │
│  │                                                                    │  │
│  │  ┌─────────────────────────────────────────────────────────────┐  │  │
│  │  │  comments    │  users      │  pages       │  spam_log       │  │  │
│  │  └─────────────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────────────┘  │
│                                                                          │
│  Deployment Options:                                                     │
│  • VPS (DigitalOcean, Linode, Hetzner): $4-20/month                     │
│  • Docker + fly.io: $0-5/month                                          │
│  • Railway/Render: $0-7/month                                           │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Implementation Example (Python + FastAPI)

# main.py - FastAPI Comment Server
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, EmailStr
from sqlalchemy import create_engine, Column, String, DateTime, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import uuid
import hashlib

# Database setup
DATABASE_URL = "sqlite:///./comments.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
Base = declarative_base()

# Models
class Comment(Base):
    __tablename__ = "comments"
    
    id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
    page_id = Column(String, nullable=False, index=True)
    parent_id = Column(String, nullable=True)
    author = Column(String, nullable=False)
    email = Column(String, nullable=True)
    email_hash = Column(String, nullable=True)
    content = Column(Text, nullable=False)
    status = Column(String, default="pending")
    created_at = Column(DateTime, default=datetime.utcnow)
    ip_address = Column(String)
    user_agent = Column(String)

Base.metadata.create_all(engine)

# Pydantic schemas
class CommentCreate(BaseModel):
    page_id: str
    parent_id: str | None = None
    author: str
    email: EmailStr | None = None
    content: str

class CommentResponse(BaseModel):
    id: str
    page_id: str
    parent_id: str | None
    author: str
    email_hash: str | None
    content: str
    created_at: datetime

    class Config:
        from_attributes = True

# App
app = FastAPI(title="Comment API")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://yourblog.com"],
    allow_methods=["GET", "POST", "DELETE"],
    allow_headers=["*"],
)

def get_email_hash(email: str | None) -> str | None:
    if not email:
        return None
    return hashlib.md5(email.lower().strip().encode()).hexdigest()

def check_spam(content: str, ip: str) -> bool:
    """Simple spam check - expand with Akismet, ML, etc."""
    spam_keywords = ['viagra', 'casino', 'crypto', 'winner']
    content_lower = content.lower()
    return any(word in content_lower for word in spam_keywords)

@app.get("/api/comments", response_model=list[CommentResponse])
async def get_comments(page_id: str):
    db = SessionLocal()
    try:
        comments = db.query(Comment).filter(
            Comment.page_id == page_id,
            Comment.status == "approved"
        ).order_by(Comment.created_at.asc()).all()
        return comments
    finally:
        db.close()

@app.post("/api/comments", response_model=CommentResponse, status_code=201)
async def create_comment(comment: CommentCreate, request: Request):
    # Get client info
    ip = request.client.host
    user_agent = request.headers.get("user-agent", "")
    
    # Spam check
    if check_spam(comment.content, ip):
        raise HTTPException(status_code=400, detail="Comment flagged as spam")
    
    db = SessionLocal()
    try:
        db_comment = Comment(
            page_id=comment.page_id,
            parent_id=comment.parent_id,
            author=comment.author,
            email=comment.email,
            email_hash=get_email_hash(comment.email),
            content=comment.content,
            status="pending",  # Change to "approved" for auto-approve
            ip_address=ip,
            user_agent=user_agent
        )
        db.add(db_comment)
        db.commit()
        db.refresh(db_comment)
        return db_comment
    finally:
        db.close()

@app.get("/api/admin/pending")
async def get_pending_comments():
    """Admin endpoint - add authentication in production!"""
    db = SessionLocal()
    try:
        comments = db.query(Comment).filter(
            Comment.status == "pending"
        ).order_by(Comment.created_at.desc()).all()
        return comments
    finally:
        db.close()

Pros and Cons

Pros Cons
✅ Full control ❌ Server maintenance required
✅ Any language/framework ❌ Fixed cost even at low traffic
✅ No vendor lock-in ❌ Need to handle scaling
✅ Custom features unlimited ❌ Security responsibility
✅ Can be very cheap ❌ More DevOps work

Architecture 5: Hybrid / Build-Time Integration

Fetch comments at build time, update via webhooks.

┌─────────────────────────────────────────────────────────────────────────┐
│                    HYBRID ARCHITECTURE                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  BUILD TIME                              RUNTIME                         │
│  ──────────────────────────────────────────────────────────────────────  │
│                                                                          │
│  ┌─────────────┐      ┌─────────────┐                                   │
│  │             │      │             │      Comment                       │
│  │  Static     │◄─────│  Comment    │      API                          │
│  │  Site Gen   │      │  API        │◄──────────┐                       │
│  │             │      │             │           │                        │
│  └──────┬──────┘      └──────┬──────┘           │                        │
│         │                    │                   │                        │
│         │                    │ Webhook           │                        │
│         ▼                    │ (new comment)     │                        │
│  ┌─────────────┐            │                   │                        │
│  │             │◄───────────┘                   │                        │
│  │  Trigger    │                                │                        │
│  │  Rebuild    │                                │                        │
│  │             │                                │                        │
│  └──────┬──────┘                                │                        │
│         │                                       │                        │
│         ▼                                       │                        │
│  ┌─────────────┐                                │                        │
│  │             │      ┌─────────────┐           │                        │
│  │  Deploy     │─────►│   Users     │───────────┘                        │
│  │  to CDN     │      │             │                                    │
│  │             │      │  • View static comments (fast)                   │
│  └─────────────┘      │  • Submit new comments (API)                     │
│                       │  • See new comments on rebuild                   │
│                       └─────────────┘                                    │
│                                                                          │
│  ─────────────────────────────────────────────────────────────────────  │
│  KEY INSIGHT: Comments are "eventually static"                          │
│  • Initial load: Pre-rendered comments (lightning fast)                 │
│  • New comments: Via API (real-time submission)                         │
│  • Rebuild: Every X minutes or on webhook                               │
│  ─────────────────────────────────────────────────────────────────────  │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Implementation Concept

// Build script that fetches comments
// scripts/fetch-comments.js

const fs = require('fs');
const path = require('path');

async function fetchAllComments() {
  const API_URL = process.env.COMMENT_API_URL;
  const pages = getPageList(); // Your function to get all page IDs
  
  for (const pageId of pages) {
    const response = await fetch(`${API_URL}/api/comments?page_id=${pageId}`);
    const comments = await response.json();
    
    // Write to data file
    const outputPath = path.join('data', 'comments', `${pageId}.json`);
    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, JSON.stringify(comments, null, 2));
  }
}

fetchAllComments();

Choosing Your Architecture

Use this decision tree:

                         START
                           │
                           ▼
              ┌────────────────────────┐
              │  What's your monthly   │
              │  traffic level?        │
              └───────────┬────────────┘
                          │
         ┌────────────────┼────────────────┐
         │                │                │
         ▼                ▼                ▼
    < 1K views      1K - 50K views    > 50K views
         │                │                │
         ▼                ▼                ▼
    Git-based        Serverless       Edge or
    (Free)           Functions        Traditional
         │                │                │
         │                │                ▼
         │                │         ┌──────────────┐
         │                │         │ Need custom  │
         │                │         │ features?    │
         │                │         └──────┬───────┘
         │                │                │
         │                │         ┌──────┴──────┐
         │                │         │             │
         │                │         ▼             ▼
         │                │       Yes            No
         │                │         │             │
         │                │         ▼             ▼
         │                │    Traditional     Edge
         │                │    API Server     Functions
         │                │                      │
         ▼                ▼                      ▼
    Staticman        Vercel +              Cloudflare
    Utterances       Supabase              Workers + D1
    Giscus

Chapter Summary

Architecture Best For Cost Complexity
Git-based Low traffic, tech audience Free Low
Serverless Variable traffic, quick setup $0-50/mo Medium
Edge Global audience, performance $0-25/mo Medium
Traditional Custom needs, full control $5-50/mo High
Hybrid Best of both worlds Varies High

In the next chapter, we’ll dive deep into database design and data modeling for comments.


Navigation: