<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>byte-by-byte — Daily Tech Learning</title>
    <link>https://github.com/YushengAuggie/byte-by-byte</link>
    <description>Daily bilingual (Chinese/English) tech learning: system design, algorithms, soft skills, frontend, and AI.</description>
    <language>en-us</language>
    <lastBuildDate>Sat, 04 Apr 2026 15:11:36 +0000</lastBuildDate>
    <atom:link href="https://github.com/YushengAuggie/byte-by-byte/raw/main/docs/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>byte-by-byte — 2026-04-04</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-04-04</guid>
      <pubDate>Sat, 04 Apr 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>Saturday Deep Dive Placeholder — system-design</h2>

<p><strong>Date:</strong> 2026-04-04</p>
<p><strong>Day:</strong> 17</p>
<p><strong>Note:</strong> This is a Saturday Deep Dive day. Individual section content is not generated.</p>
<p>Today&#x27;s full content is in: 2026-04-04-deepdive.md</p>

<p><strong>Topic covered today:</strong> Rate Limiting &amp; Throttling — a complete 18-minute bilingual deep dive</p>
<p>covering Token Bucket, Sliding Window Counter, Fixed Window, and Leaky Bucket algorithms,</p>
<p>with production-grade Redis implementation, distributed systems edge cases, real-world</p>
<p>examples from GitHub/Stripe/OpenAI/Cloudflare, and a full interview simulation section.</p>

<p>See: /Users/davidding/.openclaw/workspace/byte-by-byte/archive/2026-04-04-deepdive.md</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>Saturday Deep Dive Placeholder — algorithms</h2>

<p><strong>Date:</strong> 2026-04-04</p>
<p><strong>Day:</strong> 17</p>
<p><strong>Note:</strong> This is a Saturday Deep Dive day. Individual section content is not generated.</p>
<p>Today&#x27;s full content is in: 2026-04-04-deepdive.md</p>

<p><strong>Topic covered today:</strong> Rate Limiting &amp; Throttling — a complete 18-minute bilingual deep dive</p>
<p>covering Token Bucket, Sliding Window Counter, Fixed Window, and Leaky Bucket algorithms,</p>
<p>with production-grade Redis implementation, distributed systems edge cases, real-world</p>
<p>examples from GitHub/Stripe/OpenAI/Cloudflare, and a full interview simulation section.</p>

<p>See: /Users/davidding/.openclaw/workspace/byte-by-byte/archive/2026-04-04-deepdive.md</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>Saturday Deep Dive Placeholder — soft-skills</h2>

<p><strong>Date:</strong> 2026-04-04</p>
<p><strong>Day:</strong> 17</p>
<p><strong>Note:</strong> This is a Saturday Deep Dive day. Individual section content is not generated.</p>
<p>Today&#x27;s full content is in: 2026-04-04-deepdive.md</p>

<p><strong>Topic covered today:</strong> Rate Limiting &amp; Throttling — a complete 18-minute bilingual deep dive</p>
<p>covering Token Bucket, Sliding Window Counter, Fixed Window, and Leaky Bucket algorithms,</p>
<p>with production-grade Redis implementation, distributed systems edge cases, real-world</p>
<p>examples from GitHub/Stripe/OpenAI/Cloudflare, and a full interview simulation section.</p>

<p>See: /Users/davidding/.openclaw/workspace/byte-by-byte/archive/2026-04-04-deepdive.md</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>Saturday Deep Dive Placeholder — frontend</h2>

<p><strong>Date:</strong> 2026-04-04</p>
<p><strong>Day:</strong> 17</p>
<p><strong>Note:</strong> This is a Saturday Deep Dive day. Individual section content is not generated.</p>
<p>Today&#x27;s full content is in: 2026-04-04-deepdive.md</p>

<p><strong>Topic covered today:</strong> Rate Limiting &amp; Throttling — a complete 18-minute bilingual deep dive</p>
<p>covering Token Bucket, Sliding Window Counter, Fixed Window, and Leaky Bucket algorithms,</p>
<p>with production-grade Redis implementation, distributed systems edge cases, real-world</p>
<p>examples from GitHub/Stripe/OpenAI/Cloudflare, and a full interview simulation section.</p>

<p>See: /Users/davidding/.openclaw/workspace/byte-by-byte/archive/2026-04-04-deepdive.md</p>

<hr/>
<h1>🤖 AI</h1>
<h2>Saturday Deep Dive Placeholder — ai</h2>

<p><strong>Date:</strong> 2026-04-04</p>
<p><strong>Day:</strong> 17</p>
<p><strong>Note:</strong> This is a Saturday Deep Dive day. Individual section content is not generated.</p>
<p>Today&#x27;s full content is in: 2026-04-04-deepdive.md</p>

<p><strong>Topic covered today:</strong> Rate Limiting &amp; Throttling — a complete 18-minute bilingual deep dive</p>
<p>covering Token Bucket, Sliding Window Counter, Fixed Window, and Leaky Bucket algorithms,</p>
<p>with production-grade Redis implementation, distributed systems edge cases, real-world</p>
<p>examples from GitHub/Stripe/OpenAI/Cloudflare, and a full interview simulation section.</p>

<p>See: /Users/davidding/.openclaw/workspace/byte-by-byte/archive/2026-04-04-deepdive.md</p>

<hr/>
<h1>Deepdive</h1>
<h2>🔬 Saturday Deep Dive: Rate Limiting &amp; Throttling（限流与节流）(18 min read)</h2>

<p>📊 Day 17/150 · NeetCode: 16/150 · SysDesign: 15/40 · Behavioral: 15/40 · Frontend: 15/50 · AI: 7/30</p>
<p>🔥 5-day streak!</p>

<hr/>

<h2>Overview / 概述</h2>

<p><strong>中文：</strong></p>
<p>限流（Rate Limiting）是分布式系统中最重要的防御机制之一。当你构建一个面向公众的 API 时，如果不做限流，任何一个恶意用户或者写了死循环的程序都可以把你的服务打垮。Netflix 每天处理数亿请求，Stripe 的 API 每秒承接数万笔支付——他们都依赖精心设计的限流系统来保持稳定。</p>

<p>限流解决三个核心问题：</p>
<p>1. <strong>可用性保护</strong>：防止单个用户耗尽服务资源</p>
<p>2. <strong>成本控制</strong>：LLM API 调用、短信等高成本操作必须被约束</p>
<p>3. <strong>公平性</strong>：确保资源在用户之间公平分配</p>

<p><strong>English:</strong></p>
<p>Rate limiting is one of the most critical defensive mechanisms in distributed systems. Without it, a single misbehaving client — whether a DDoS attacker or a developer&#x27;s buggy retry loop — can bring down your entire service. Netflix, Stripe, GitHub, and every major API platform relies on sophisticated rate limiting. This deep dive covers four algorithms with increasing sophistication, a production-grade Redis implementation, and the distributed systems challenges that make this problem genuinely hard.</p>

<hr/>

<h2>Part 1: Theory / 理论基础 (5 min)</h2>

<h3>四大限流算法 / The Four Algorithms</h3>

<p><strong>中文：</strong></p>
<p>限流有四种主流算法，每种都有不同的特性和适用场景。</p>

<p><strong>English:</strong></p>
<p>There are four main rate limiting algorithms, each with distinct trade-offs.</p>

<hr/>

<h3>算法 1：固定窗口计数器 / Fixed Window Counter</h3>

<p><strong>中文：</strong></p>
<p>最简单的算法。把时间切成固定窗口（比如每分钟），窗口内计数，超过阈值就拒绝。</p>

<p>致命缺陷：<strong>窗口边界攻击</strong>。如果限制是每分钟 100 次，用户可以在 0:59 发送 100 次，在 1:01 再发送 100 次——2 秒内发送了 200 次请求，是限制的两倍。</p>

<p><strong>English:</strong></p>
<p>Simplest algorithm. Divide time into fixed windows (e.g., per minute), count requests in each window, reject when threshold exceeded.</p>

<p>Fatal flaw: <strong>boundary burst attacks</strong>. A limit of 100/min can be abused to send 200 requests in 2 seconds by straddling window boundaries.</p>

<pre><code>
Timeline: |--window 1--|--window 2--|
Requests:    99 at :59   100 at :01
Result:      Both OK! But 199 requests in 2 seconds 🚨
</code></pre>

<hr/>

<h3>算法 2：滑动窗口日志 / Sliding Window Log</h3>

<p><strong>中文：</strong></p>
<p>精确解决边界问题。为每个用户维护一个请求时间戳的有序日志，每次请求时移除窗口外的旧时间戳，检查剩余数量是否超限。</p>

<p>优点：精确。缺点：内存开销大，每个用户每个时间窗口内都要存所有时间戳。</p>

<p><strong>English:</strong></p>
<p>Precisely solves the boundary problem. Maintain a sorted log of request timestamps per user. On each request, evict timestamps outside the window, then check count.</p>

<p>Pro: Exact. Con: Memory-heavy — stores every timestamp for every active user.</p>

<pre><code>
User A: [12:00:01, 12:00:23, 12:00:45, 12:00:58] ← 4 requests, all within 1 min window
New request at 12:01:05:
  Evict 12:00:01 (&gt;60s ago) → [12:00:23, 12:00:45, 12:00:58, 12:01:05]
  Count = 4 → OK (if limit is 5)
</code></pre>

<hr/>

<h3>算法 3：滑动窗口计数器 / Sliding Window Counter</h3>

<p><strong>中文：</strong></p>
<p>固定窗口和滑动窗口日志的折中方案——工程上最常用的方案。</p>

<p>核心思想：利用当前窗口计数和上一个窗口计数的加权组合来近似&quot;真实&quot;的滑动窗口。</p>

<p>公式：<code>estimated_count = prev_window_count × (1 - elapsed_ratio) + curr_window_count</code></p>

<p><strong>English:</strong></p>
<p>The practical middle ground — most commonly used in production.</p>

<p>Core idea: approximate the sliding window using a weighted combination of the previous and current fixed window counts.</p>

<p>Formula: <code>estimated_count = prev_window_count × (1 - elapsed_ratio) + curr_window_count</code></p>

<pre><code>
Window size: 60s. Limit: 100.
prev_window (12:00-13:00): 80 requests
curr_window (13:00-14:00): 30 requests, 40s elapsed

elapsed_ratio = 40/60 = 0.667
estimate = 80 × (1 - 0.667) + 30 = 80 × 0.333 + 30 = 26.6 + 30 = 56.6 → OK
</code></pre>

<hr/>

<h3>算法 4：令牌桶 / Token Bucket</h3>

<p><strong>中文：</strong></p>
<p>最灵活的算法，也是 AWS、Stripe 等使用的主流方案。</p>

<p>每个用户有一个容量为 <code>capacity</code> 的桶，以 <code>rate</code> 的速度持续向桶中添加令牌。每次请求消耗一个令牌，桶空则拒绝。</p>

<p>核心优势：天然支持突发流量（Burst）。桶满时积累的令牌允许短时间内的高频请求，只要平均速率不超限。</p>

<p><strong>English:</strong></p>
<p>The most flexible algorithm, used by AWS API Gateway, Stripe, and most cloud providers.</p>

<p>Each user has a bucket with capacity <code>C</code>. Tokens fill at rate <code>r</code> tokens/second. Each request consumes one token; empty bucket = reject.</p>

<p>Key advantage: <strong>burst tolerance</strong>. A full bucket lets users send <code>C</code> requests instantly, then sustain <code>r</code> requests/second long-term.</p>

<pre><code>
capacity = 10, rate = 2 tokens/sec

t=0s:  bucket=10, request→bucket=9  ✅
t=0s:  burst of 9 more → bucket=0  ✅ (all burst allowed!)
t=1s:  bucket=2 (refilled), request→bucket=1  ✅
t=1s:  1 more request → bucket=0  ✅
t=1s:  1 more request → bucket empty → REJECT  ❌
</code></pre>

<hr/>

<h3>漏桶算法 / Leaky Bucket</h3>

<p><strong>中文：</strong></p>
<p>令牌桶的&quot;镜像&quot;。请求进入队列，以固定速率处理（&quot;漏出&quot;）。保证输出速率绝对平滑，但不允许突发。适合需要精确控制输出速率的场景（如视频流）。</p>

<p><strong>English:</strong></p>
<p>The mirror of token bucket. Requests enter a queue, processed at fixed rate. Guarantees smooth output but no burst tolerance. Great for video streaming or payment processing where you need steady throughput.</p>

<hr/>

<h3>算法选择指南 / Algorithm Selection Guide</h3>

<p>| 场景 / Scenario | 推荐算法 / Algorithm |</p>
<p>|---|---|</p>
<p>| 简单 API 限流 / Simple API rate limit | Sliding Window Counter |</p>
<p>| 允许突发 / Burst traffic OK | Token Bucket |</p>
<p>| 需要平滑输出 / Smooth output required | Leaky Bucket |</p>
<p>| 精确计费 / Exact billing | Sliding Window Log |</p>

<hr/>

<h2>Part 2: Step-by-Step Implementation / 一步一步实现 (8 min)</h2>

<p><strong>中文：</strong></p>
<p>我们来实现两个版本：（1）单机内存版——理解算法核心；（2）分布式 Redis 版——生产可用。</p>

<p><strong>English:</strong></p>
<p>We&#x27;ll build two versions: (1) in-memory single-node to understand the algorithm; (2) distributed Redis version that&#x27;s production-ready.</p>

<hr/>

<h3>Version 1: In-Memory Token Bucket / 单机令牌桶</h3>

<pre><code>
import time
import threading
from dataclasses import dataclass, field
from typing import Dict

@dataclass
class TokenBucket:
    capacity: float        # Max tokens (burst limit)
    rate: float            # Tokens added per second
    tokens: float = field(init=False)
    last_refill: float = field(init=False)
    lock: threading.Lock = field(default_factory=threading.Lock, init=False)

    def __post_init__(self):
        self.tokens = self.capacity  # Start full
        self.last_refill = time.monotonic()

    def _refill(self):
        &quot;&quot;&quot;Add tokens based on elapsed time since last refill.&quot;&quot;&quot;
        now = time.monotonic()
        elapsed = now - self.last_refill
        # Calculate how many tokens to add
        new_tokens = elapsed * self.rate
        # Cap at capacity (can&#x27;t overflow the bucket)
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_refill = now

    def consume(self, tokens: float = 1.0) -&gt; bool:
        &quot;&quot;&quot;Try to consume `tokens`. Returns True if allowed.&quot;&quot;&quot;
        with self.lock:
            self._refill()
            if self.tokens &gt;= tokens:
                self.tokens -= tokens
                return True  # Allow request
            return False  # Reject: bucket empty


class RateLimiter:
    &quot;&quot;&quot;Per-user rate limiter using token bucket algorithm.&quot;&quot;&quot;
    
    def __init__(self, capacity: int = 10, rate: float = 2.0):
        self.capacity = capacity
        self.rate = rate
        self._buckets: Dict[str, TokenBucket] = {}
        self._lock = threading.Lock()

    def _get_bucket(self, user_id: str) -&gt; TokenBucket:
        &quot;&quot;&quot;Lazily create bucket for user.&quot;&quot;&quot;
        with self._lock:
            if user_id not in self._buckets:
                self._buckets[user_id] = TokenBucket(
                    capacity=self.capacity,
                    rate=self.rate
                )
            return self._buckets[user_id]

    def is_allowed(self, user_id: str) -&gt; bool:
        return self._get_bucket(user_id).consume()


# Usage
limiter = RateLimiter(capacity=5, rate=1.0)  # 5 burst, 1/sec sustained

for i in range(8):
    allowed = limiter.is_allowed(&quot;user_123&quot;)
    print(f&quot;Request {i+1}: {&#x27;✅ ALLOWED&#x27; if allowed else &#x27;❌ REJECTED&#x27;}&quot;)
    # Output: first 5 allowed (burst), next 3 rejected (bucket empty)
</code></pre>

<hr/>

<h3>Version 2: Distributed Sliding Window Counter with Redis / 分布式滑动窗口计数器</h3>

<p><strong>中文：</strong></p>
<p>单机版本有个致命问题：在多实例部署中，每个实例维护各自的计数器，用户可以通过轮询绕过限制。Redis 提供原子操作，是分布式限流的标准选择。</p>

<p><strong>English:</strong></p>
<p>Single-node limiters have a fatal flaw: in multi-instance deployments, each instance has its own counters. A user hitting 3 servers with 10 req/server sees 30 effective requests per window. Redis atomic operations solve this.</p>

<pre><code>
import time
import redis
from typing import Tuple

class RedisRateLimiter:
    &quot;&quot;&quot;
    Sliding Window Counter using Redis.
    
    Key insight: we store TWO counters per user — current window and previous.
    We use the weighted formula to approximate a true sliding window.
    This uses O(1) memory per user, vs O(n) for log-based approach.
    &quot;&quot;&quot;
    
    def __init__(
        self,
        redis_client: redis.Redis,
        limit: int = 100,
        window_seconds: int = 60,
        key_prefix: str = &quot;rl&quot;
    ):
        self.redis = redis_client
        self.limit = limit
        self.window = window_seconds
        self.prefix = key_prefix

    def _get_keys(self, user_id: str) -&gt; Tuple[str, str]:
        &quot;&quot;&quot;Get Redis keys for current and previous windows.&quot;&quot;&quot;
        # Integer division gives us the current window bucket
        current_window = int(time.time()) // self.window
        prev_window = current_window - 1
        
        curr_key = f&quot;{self.prefix}:{user_id}:{current_window}&quot;
        prev_key = f&quot;{self.prefix}:{user_id}:{prev_window}&quot;
        return curr_key, prev_key

    def is_allowed(self, user_id: str) -&gt; Tuple[bool, int]:
        &quot;&quot;&quot;
        Check if request is allowed.
        Returns (allowed: bool, remaining: int)
        &quot;&quot;&quot;
        curr_key, prev_key = self._get_keys(user_id)
        
        # Lua script for atomic read-increment-check
        # Redis executes Lua atomically — no race conditions possible
        lua_script = &quot;&quot;&quot;
        local curr_key = KEYS[1]
        local prev_key = KEYS[2]
        local limit = tonumber(ARGV[1])
        local window = tonumber(ARGV[2])
        local now = tonumber(ARGV[3])
        
        -- Get current counts (default 0)
        local curr_count = tonumber(redis.call(&#x27;GET&#x27;, curr_key) or 0)
        local prev_count = tonumber(redis.call(&#x27;GET&#x27;, prev_key) or 0)
        
        -- Calculate what fraction of current window has elapsed
        local elapsed_in_window = now % window
        local elapsed_ratio = elapsed_in_window / window
        
        -- Weighted estimate: prev window contributes less as current window progresses
        local estimated = prev_count * (1 - elapsed_ratio) + curr_count
        
        if estimated &gt;= limit then
            return {0, 0}  -- Reject: over limit
        end
        
        -- Increment current window counter, set TTL to 2x window
        redis.call(&#x27;INCR&#x27;, curr_key)
        redis.call(&#x27;EXPIRE&#x27;, curr_key, window * 2)
        
        local remaining = math.floor(limit - estimated - 1)
        return {1, remaining}
        &quot;&quot;&quot;
        
        script = self.redis.register_script(lua_script)
        now = time.time()
        
        result = script(
            keys=[curr_key, prev_key],
            args=[self.limit, self.window, now]
        )
        
        allowed = bool(result[0])
        remaining = int(result[1])
        return allowed, remaining

    def get_headers(self, user_id: str) -&gt; dict:
        &quot;&quot;&quot;Return rate limit headers (standard HTTP spec).&quot;&quot;&quot;
        curr_key, prev_key = self._get_keys(user_id)
        curr_count = int(self.redis.get(curr_key) or 0)
        
        return {
            &quot;X-RateLimit-Limit&quot;: str(self.limit),
            &quot;X-RateLimit-Remaining&quot;: str(max(0, self.limit - curr_count)),
            &quot;X-RateLimit-Reset&quot;: str(
                (int(time.time()) // self.window + 1) * self.window
            ),
        }


# FastAPI middleware example
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()
redis_client = redis.Redis(host=&quot;localhost&quot;, port=6379, db=0)
limiter = RedisRateLimiter(redis_client, limit=100, window_seconds=60)

@app.middleware(&quot;http&quot;)
async def rate_limit_middleware(request: Request, call_next):
    # Extract user identifier (API key, JWT sub, or IP as fallback)
    user_id = request.headers.get(&quot;X-API-Key&quot;) or request.client.host
    
    allowed, remaining = limiter.is_allowed(user_id)
    headers = limiter.get_headers(user_id)
    
    if not allowed:
        return JSONResponse(
            status_code=429,
            content={
                &quot;error&quot;: &quot;rate_limit_exceeded&quot;,
                &quot;message&quot;: &quot;Too many requests. Please slow down.&quot;,
                &quot;retry_after&quot;: headers[&quot;X-RateLimit-Reset&quot;]
            },
            headers={**headers, &quot;Retry-After&quot;: headers[&quot;X-RateLimit-Reset&quot;]}
        )
    
    response = await call_next(request)
    # Inject rate limit headers into every response (good practice)
    for key, value in headers.items():
        response.headers[key] = value
    
    return response
</code></pre>

<hr/>

<h3>Tiered Rate Limiting / 分级限流</h3>

<p><strong>中文：</strong></p>
<p>真实系统中通常有多层限流：不同 API 端点有不同限制，不同用户等级有不同配额。</p>

<p><strong>English:</strong></p>
<p>Production systems use tiered limits — different endpoints, different user tiers, different time windows:</p>

<pre><code>
# Tiered config — typically loaded from DB or feature flags
RATE_LIMIT_CONFIG = {
    &quot;free&quot;: {
        &quot;default&quot;: {&quot;limit&quot;: 60, &quot;window&quot;: 60},         # 60 req/min
        &quot;/api/search&quot;: {&quot;limit&quot;: 10, &quot;window&quot;: 60},     # Search is expensive
        &quot;/api/export&quot;: {&quot;limit&quot;: 2, &quot;window&quot;: 3600},    # 2 exports/hour
    },
    &quot;pro&quot;: {
        &quot;default&quot;: {&quot;limit&quot;: 1000, &quot;window&quot;: 60},
        &quot;/api/search&quot;: {&quot;limit&quot;: 100, &quot;window&quot;: 60},
        &quot;/api/export&quot;: {&quot;limit&quot;: 50, &quot;window&quot;: 3600},
    },
    &quot;enterprise&quot;: {
        &quot;default&quot;: {&quot;limit&quot;: 10000, &quot;window&quot;: 60},     # Effectively unlimited
    }
}

def get_limit_config(user_tier: str, endpoint: str) -&gt; dict:
    tier_config = RATE_LIMIT_CONFIG.get(user_tier, RATE_LIMIT_CONFIG[&quot;free&quot;])
    # Fall back to default if endpoint not specifically configured
    return tier_config.get(endpoint, tier_config[&quot;default&quot;])
</code></pre>

<hr/>

<h2>Part 3: Edge Cases &amp; Gotchas / 边界情况 (2 min)</h2>

<p><strong>中文：</strong></p>

<p><strong>1. 竞态条件（Race Condition）</strong></p>
<p>不用 Redis Lua 脚本而用 GET → 业务逻辑 → SET 会导致竞态。两个请求同时读到 count=99，都认为可以递增到 100，结果实际到达 101。<strong>永远使用原子操作：INCR 或 Lua 脚本。</strong></p>

<p><strong>2. Redis 故障时怎么办？</strong></p>
<p>两种策略：</p>
<p>- <strong>Fail Open（故障放行）</strong>：Redis 挂了就放行所有请求，服务可用性优先</p>
<p>- <strong>Fail Closed（故障拒绝）</strong>：Redis 挂了就拒绝所有请求，安全性优先</p>
<p>大多数 API 服务选择 Fail Open。</p>

<p><strong>3. 分布式时钟漂移</strong></p>
<p>多台服务器的系统时钟可能不同步（差几百毫秒），导致窗口边界不一致。解决方案：使用 Redis 的 <code>TIME</code> 命令获取统一时间源。</p>

<p><strong>4. 代理和 CDN 后的真实 IP</strong></p>
<p><code>request.client.host</code> 会返回负载均衡器 IP，不是用户真实 IP。需要解析 <code>X-Forwarded-For</code> 或 <code>CF-Connecting-IP</code>（Cloudflare），但要注意这些 header 可伪造。</p>

<p><strong>5. 重试风暴（Retry Storm）</strong></p>
<p>客户端收到 429 后立即重试，会加剧问题。标准做法：返回 <code>Retry-After</code> header，客户端实现指数退避 + 抖动（Jitter）。</p>

<p><strong>English:</strong></p>

<p>1. <strong>Race condition</strong>: Never GET → check → SET. Always use <code>INCR</code> or Lua for atomicity.</p>
<p>2. <strong>Redis failure</strong>: Choose Fail Open (availability) or Fail Closed (strict safety). Most APIs choose Fail Open.</p>
<p>3. <strong>Clock skew</strong>: Servers clocks drift. Use Redis <code>TIME</code> for a single source of truth.</p>
<p>4. <strong>IP behind proxy</strong>: <code>X-Forwarded-For</code> is user-controlled and can be spoofed. Validate carefully, trust only the last hop your own infrastructure added.</p>
<p>5. <strong>Retry storms</strong>: Always return <code>Retry-After</code>. Clients should use exponential backoff with jitter: <code>delay = base * 2^attempt + random(0, base)</code>.</p>

<hr/>

<h2>Part 4: Real-World Application / 实际应用 (2 min)</h2>

<p><strong>中文：</strong></p>

<p>- <strong>GitHub API</strong>：未认证请求 60次/小时，认证请求 5000次/小时，搜索 API 10次/分钟（独立限流！）</p>
<p>- <strong>Stripe</strong>：100次/秒，但 Webhook 重试使用指数退避而非固定速率</p>
<p>- <strong>OpenAI API</strong>：按 Token 计算（TPM: Tokens Per Minute），不只是按请求次数——这是令牌桶算法，只是&quot;令牌&quot;的单位是 LLM token 而非请求数</p>
<p>- <strong>Cloudflare</strong>：在边缘节点（CDN PoP）进行限流，在请求到达源站之前就拦截——这叫&quot;边缘限流&quot;，延迟极低但实现复杂</p>
<p>- <strong>Netflix</strong>：每个 API 路由有独立的限流配置，Hystrix/Resilience4j 提供熔断器（Circuit Breaker）作为限流的补充</p>

<p><strong>English:</strong></p>

<p>- <strong>GitHub API</strong>: 60 unauthenticated / 5000 authenticated reqs/hour. Search is separately limited at 10/min — same backend, different resource cost profile.</p>
<p>- <strong>Stripe</strong>: 100 reqs/sec baseline, but billing webhooks use exponential backoff rather than fixed rate limiting to handle payment processing spikes.</p>
<p>- <strong>OpenAI</strong>: Limits on <em>tokens</em> per minute (TPM), not just requests — effectively a token bucket where the &quot;token&quot; unit is an LLM token. They also layer <em>requests</em> per minute (RPM) on top.</p>
<p>- <strong>Cloudflare Workers</strong>: Rate limiting at the edge (CDN PoP level), before requests even reach your origin. Reduces attack surface at sub-millisecond latency with zero origin load.</p>
<p>- <strong>DoorDash</strong>: Uses a combination of per-user limits AND global service limits with adaptive throttling — if downstream services degrade, they automatically tighten rate limits upstream.</p>

<hr/>

<h2>Part 5: Interview Simulation / 面试模拟 (3 min)</h2>

<p><strong>中文：</strong>面试中，限流是系统设计面试的常见子问题。以下是面试官最常问的 5 个追问。</p>

<p><strong>English:</strong> Rate limiting appears in nearly every system design interview, either as the main topic or as a component. Here are the 5 most common follow-ups:</p>

<hr/>

<p><strong>Q1: 如果我们有 100 个 API 服务器，怎么保证全局限流准确？</strong></p>
<p><strong>If we have 100 API servers, how do we enforce a global rate limit accurately?</strong></p>

<p>&gt; <strong>A:</strong> 中心化存储（Redis 集群）是标准方案。每台 API 服务器都读写同一个 Redis，原子操作保证一致性。但如果 Redis 延迟高，可以考虑&quot;本地令牌 + 同步&quot;方案：每台服务器缓存一小批令牌（比如 10%），定期从 Redis 补充，减少网络往返。这叫 Token Bucket with Local Cache，牺牲少量精度换取性能。</p>

<p><strong>Q2: 怎么防止用户伪造 IP 绕过限流？</strong></p>
<p><strong>How do you prevent IP spoofing to bypass rate limits?</strong></p>

<p>&gt; <strong>A:</strong> IP-based limiting should be a last resort. Prefer authenticated identifiers (API keys, JWT user IDs). For unauthenticated endpoints, trust only the last hop of <code>X-Forwarded-For</code> that your own infrastructure controls (e.g., the IP your load balancer added). Never trust client-supplied IPs blindly. For high-security scenarios, layer fingerprinting (User-Agent + TLS fingerprint) on top.</p>

<p><strong>Q3: 限流和熔断器（Circuit Breaker）有什么区别？</strong></p>
<p><strong>What&#x27;s the difference between rate limiting and a circuit breaker?</strong></p>

<p>&gt; <strong>A:</strong> Rate limiting protects <em>your service</em> from <em>excessive client requests</em> (inbound). Circuit breakers protect <em>your service</em> from <em>failing downstream dependencies</em> (outbound). Rate limiter rejects requests at the door; circuit breaker stops you from calling a service that&#x27;s already down. They&#x27;re complementary: use both. Netflix Hystrix (and its successor Resilience4j) implements circuit breakers; Redis or API Gateway handles rate limiting.</p>

<p><strong>Q4: 如何对 &quot;代价不同&quot; 的 API 操作做限流？比如写操作比读操作贵得多。</strong></p>
<p><strong>How do you rate limit operations with different &quot;costs&quot;? Writes vs reads, for example.</strong></p>

<p>&gt; <strong>A:</strong> Use weighted token consumption. Instead of <code>consume(1)</code>, writes call <code>consume(5)</code>. This is exactly how OpenAI handles LLM tokens — a 4K-token completion costs 4000 units from the TPM bucket, while a 100-token prompt costs 100. Define a &quot;cost unit&quot; (e.g., 1 unit = 1 DB read), assign costs to each operation, and run one token bucket per user with a capacity in cost units rather than request counts.</p>

<p><strong>Q5: 用户触发限流了，怎么给他们好的体验？</strong></p>
<p><strong>What&#x27;s the user experience when a rate limit is hit?</strong></p>

<p>&gt; <strong>A:</strong> Return HTTP 429 with three key pieces of info: (1) <code>Retry-After</code> header with exact seconds until window resets; (2) <code>X-RateLimit-Limit</code>, <code>X-RateLimit-Remaining</code>, <code>X-RateLimit-Reset</code> on <em>every</em> response so clients can self-throttle before hitting the limit; (3) A clear error body with a link to rate limit docs. Pro tip: send a 429 with <code>Retry-After: 0</code> once the window resets, which is a soft signal that the client can retry immediately. Client libraries like the official Stripe SDK and OpenAI Python client parse these headers and auto-retry with backoff.</p>

<hr/>

<h2>🔗 References / 参考资料</h2>

<p>- 📄 <strong>System Design Primer — Rate Limiting</strong>: https://github.com/donnemartin/system-design-primer</p>
<p>- 📄 <strong>Stripe Engineering Blog — Rate Limiters</strong>: https://stripe.com/blog/rate-limiters</p>
<p>- 📄 <strong>Cloudflare — How We Built Rate Limiting</strong>: https://blog.cloudflare.com/counting-things-a-lot-of-different-things/</p>
<p>- 📄 <strong>Redis Official Docs — INCR pattern</strong>: https://redis.io/docs/latest/commands/incr/</p>
<p>- 📄 <strong>RFC 6585 — HTTP 429 Too Many Requests</strong>: https://tools.ietf.org/html/rfc6585</p>
<p>- 📹 <strong>NeetCode — Sliding Window Intro</strong> (relevant for SW counter): https://neetcode.io/courses/advanced-algorithms/1</p>

<hr/>

<p><em>Generated for byte-by-byte Saturday Deep Dive · 2026-04-04</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-04-03</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-04-03</guid>
      <pubDate>Fri, 03 Apr 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 15 / System Design Day 15</h2>

<h2>Rate Limiting &amp; Throttling — 保护你的 API / Protecting Your API</h2>

<hr/>

<h3>🌏 真实场景 / Real-World Scenario</h3>

<p>想象你在 Twitter（X）做工程师。某个下午，一个爬虫脚本突然对你的搜索 API 发起每秒 10,000 次请求，导致数据库崩溃，所有用户无法刷推。</p>

<p>你需要一个<strong>限流系统</strong>——在不影响正常用户的前提下，拒绝滥用流量。</p>

<p>Imagine you&#x27;re an engineer at Twitter (X). One afternoon, a scraper script hammers your search API at 10,000 requests/second, crashing the database for all users. You need a <strong>rate limiting system</strong> — one that blocks abuse without affecting normal users.</p>

<hr/>

<h3>📐 架构图 / Architecture Diagram</h3>

<pre><code>
                          ┌─────────────────────┐
   Client Request ───────►│   API Gateway /      │
                          │   Rate Limiter       │
                          │                      │
                          │  1. Identify client  │
                          │     (IP / User ID /  │
                          │      API Key)        │
                          │  2. Check counter    │
                          │     in Redis         │
                          │  3. Allow or Block   │
                          └──────────┬───────────┘
                                     │ Allowed
                          ┌──────────▼───────────┐
                          │   Backend Service     │
                          └──────────────────────┘

Redis Counter Example (Sliding Window):
  key: &quot;ratelimit:user123:2026-04-03T08&quot;
  value: 47  (requests this hour)
  TTL: 3600s
</code></pre>

<hr/>

<h3>⚙️ 主要算法 / Key Algorithms</h3>

<p>| 算法 | 原理 | 优点 | 缺点 |</p>
<p>|------|------|------|------|</p>
<p>| <strong>Token Bucket</strong> | 令牌以固定速率补充，请求消耗令牌 | 允许突发流量 | 实现稍复杂 |</p>
<p>| <strong>Leaky Bucket</strong> | 请求以固定速率流出（队列） | 输出极平滑 | 不允许突发 |</p>
<p>| <strong>Fixed Window</strong> | 每个时间窗重置计数器 | 最简单 | 边界突破问题 |</p>
<p>| <strong>Sliding Window</strong> | 精确追踪过去 N 秒 | 最精确 | 内存占用高 |</p>

<p><strong>推荐：Token Bucket（令牌桶）</strong> — 生产中最常用，兼顾突发和平均速率。</p>

<hr/>

<h3>🔑 关键权衡 / Key Tradeoffs</h3>

<p><strong>为什么用 Redis 而不是本地内存？/ Why Redis, not local memory?</strong></p>

<p>- 多台服务器共享同一计数器 → 分布式限流</p>
<p>- 原子操作（INCR + EXPIRE）避免竞态条件</p>
<p>- Redis 单线程模型保证计数器一致性</p>

<p><strong>限流粒度选择 / Granularity choices:</strong></p>
<p>- <strong>Per IP</strong> — 防爬虫，但误伤 NAT 用户（办公室）</p>
<p>- <strong>Per User ID</strong> — 最精确，需要认证</p>
<p>- <strong>Per API Key</strong> — B2B 场景首选</p>
<p>- <strong>Per Endpoint</strong> — 写接口比读接口更严格</p>

<hr/>

<h3>❌ 常见错误 / Common Mistakes</h3>

<p><strong>坑 1：Fixed Window 边界问题</strong></p>
<pre><code>
Window 1 (0:00-1:00): 99 requests ← allowed
Window 2 (1:00-2:00): 99 requests ← allowed
But at 0:59 + 1:01 = 198 requests in 2 seconds! ← spike!
</code></pre>
<p>→ 用 Sliding Window Log 或 Sliding Window Counter 解决</p>

<p><strong>坑 2：忘记 HTTP 响应头</strong></p>
<pre><code>
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1743685200
Retry-After: 3600  ← 429 时必须包含
</code></pre>
<p>客户端需要知道何时重试！</p>

<p><strong>坑 3：硬拒绝 vs 降级</strong></p>
<p>不要直接返回 <code>503</code>，试试 queue、degrade（返回缓存数据）或 soft-limit（超出后降速）。</p>

<hr/>

<h3>📚 References</h3>

<p>- [System Design Interview — Rate Limiting (ByteByteGo)](https://blog.bytebytego.com/p/rate-limiting-fundamentals)</p>
<p>- [Cloudflare Rate Limiting Docs](https://developers.cloudflare.com/waf/rate-limiting-rules/)</p>
<p>- [Redis INCR for Rate Limiting](https://redis.io/docs/manual/patterns/rate-limiting/)</p>

<hr/>

<h3>🧒 ELI5</h3>

<p>就像游乐场的入口检票员：每个小时只放 100 个人进去。人满了就让你在外面等，而不是把游乐场挤爆。</p>

<p>It&#x27;s like a bouncer at a club: &quot;100 people per hour max.&quot; When it&#x27;s full, you wait outside — the club doesn&#x27;t collapse.</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 16 / Algorithms Day 16</h2>

<h2>🧩 滑动窗口模式 (2/6) — Building on the template from Day 15</h2>

<p>&gt; #3 Longest Substring Without Repeating Characters 🟡 Medium</p>

<p>🔗 [LeetCode #3](https://leetcode.com/problems/longest-substring-without-repeating-characters/)</p>
<p>📹 [NeetCode Video](https://neetcode.io/problems/longest-substring-without-repeating-characters)</p>

<hr/>

<p><strong>今天的问题是模式的第 2 题（共 6 题）</strong>，我们继续用滑动窗口模板。相比昨天的 #121（股票买卖，只追踪一个 min 值），今天窗口需要追踪「一个集合」——这是滑动窗口真正的威力所在。</p>

<p>Today is problem <strong>2/6</strong> in the Sliding Window block. Unlike #121 (tracking a single min value), today&#x27;s window tracks a <strong>set of characters</strong> — this is where the pattern gets powerful.</p>

<hr/>

<h3>🗺️ 模板回顾 / Template Recap</h3>

<pre><code>
left = 0
for right in range(len(arr)):
    window.add(arr[right])      # expand right
    while CONDITION_VIOLATED:
        window.remove(arr[left])  # shrink left
        left += 1
    result = max(result, right - left + 1)
</code></pre>

<p><strong>核心洞察 / Key Insight:</strong> 右指针扩张探索，左指针收缩维护约束。每个元素最多进出窗口各一次 → O(n)。</p>

<hr/>

<h3>🌍 真实类比 / Real-World Analogy</h3>

<p>想象你在刷 Spotify 播放历史，想找「连续播放、没有重复歌曲」的最长片段。当出现重复时，你从头开始，直到重复歌曲被移出窗口之外。</p>

<p>Imagine scanning your Spotify history for the longest streak where no song repeats. The moment a duplicate appears, you slide your start forward until the duplicate is gone.</p>

<hr/>

<h3>📝 问题 / Problem</h3>

<p>给定字符串 <code>s</code>，找出不含重复字符的最长子串的长度。</p>

<p>Given string <code>s</code>, find the length of the longest substring without repeating characters.</p>

<pre><code>
Input:  s = &quot;abcabcbb&quot;
Output: 3  (&quot;abc&quot;)

Input:  s = &quot;pwwkew&quot;
Output: 3  (&quot;wke&quot;)
</code></pre>

<hr/>

<h3>🗂️ 映射到模板 / Mapping to Template</h3>

<p>| 模板元素 | 本题对应 |</p>
<p>|----------|----------|</p>
<p>| <code>window</code> | <code>set()</code> — 当前窗口中的字符 |</p>
<p>| <code>CONDITION_VIOLATED</code> | <code>arr[right] in window</code>（出现重复） |</p>
<p>| 扩张 | 把 <code>s[right]</code> 加入 set |</p>
<p>| 收缩 | 把 <code>s[left]</code> 从 set 移除，<code>left += 1</code> |</p>
<p>| <code>result</code> | <code>max(result, right - left + 1)</code> |</p>

<hr/>

<h3>🐍 Python 解法 + 追踪 / Python Solution + Trace</h3>

<pre><code>
def lengthOfLongestSubstring(s: str) -&gt; int:
    window = set()       # characters currently in window
    left = 0
    result = 0

    for right in range(len(s)):
        # Shrink window until no duplicate
        while s[right] in window:
            window.remove(s[left])
            left += 1

        # Expand: add new character
        window.add(s[right])
        result = max(result, right - left + 1)

    return result
</code></pre>

<p><strong>追踪 &quot;abcabcbb&quot; / Trace:</strong></p>
<pre><code>
right=0: window={&#x27;a&#x27;}, len=1
right=1: window={&#x27;a&#x27;,&#x27;b&#x27;}, len=2
right=2: window={&#x27;a&#x27;,&#x27;b&#x27;,&#x27;c&#x27;}, len=3  ← result=3
right=3: &#x27;a&#x27; dup! → remove &#x27;a&#x27;, left=1 → window={&#x27;b&#x27;,&#x27;c&#x27;,&#x27;a&#x27;}, len=3
right=4: &#x27;b&#x27; dup! → remove &#x27;b&#x27;, left=2 → window={&#x27;c&#x27;,&#x27;a&#x27;,&#x27;b&#x27;}, len=3
right=5: &#x27;c&#x27; dup! → remove &#x27;c&#x27;, left=3 → window={&#x27;a&#x27;,&#x27;b&#x27;,&#x27;c&#x27;}, len=3
right=6: &#x27;b&#x27; dup! → remove &#x27;a&#x27;,&#x27;b&#x27;, left=5 → window={&#x27;c&#x27;,&#x27;b&#x27;}, len=2
right=7: &#x27;b&#x27; dup! → remove &#x27;c&#x27;,&#x27;b&#x27;, left=7 → window={&#x27;b&#x27;}, len=1
Final: 3 ✅
</code></pre>

<p><strong>复杂度 / Complexity:</strong></p>
<p>- Time: O(n) — each char enters/exits window at most once</p>
<p>- Space: O(min(m, n)) where m = charset size (26 for lowercase)</p>

<p><strong>优化版 / Optimized (HashMap for O(1) jump):</strong></p>
<pre><code>
def lengthOfLongestSubstring(s: str) -&gt; int:
    char_index = {}  # char → last seen index
    left = 0
    result = 0

    for right, ch in enumerate(s):
        # Jump left past the duplicate directly
        if ch in char_index and char_index[ch] &gt;= left:
            left = char_index[ch] + 1
        char_index[ch] = right
        result = max(result, right - left + 1)

    return result
</code></pre>
<p>→ 避免 while 循环，直接跳跃 left，同样 O(n) 但常数更小。</p>

<hr/>

<h3>🔄 举一反三 / This Pattern Block</h3>

<p>| 题目 | 窗口内容 | 约束条件 |</p>
<p>|------|----------|----------|</p>
<p>| #121 (Day 15) | 单个 min 值 | 无，只追踪最小值 |</p>
<p>| <strong>#3 (今天)</strong> | <strong>字符集合</strong> | <strong>无重复</strong> |</p>
<p>| #424 (下期) | 字符频率 map | 替换次数 ≤ k |</p>
<p>| #567 | 字符频率 map | 频率完全匹配 |</p>
<p>| #76 (Hard) | 字符频率 map | 包含所有目标字符 |</p>

<hr/>

<h3>🧒 ELI5</h3>

<p>用两根手指夹住一段字符串，右手不断向右扩张。一旦右手摸到一个和窗口内重复的字母，左手就往右收缩，直到没有重复为止。全程记录最宽的窗口宽度。</p>

<p>Use two fingers on a string. Right finger keeps expanding right. The moment it hits a duplicate, the left finger moves right to shrink the window until no duplicates remain. Track the max width you ever saw.</p>

<hr/>

<h3>📚 References</h3>

<p>- [LeetCode #3 — Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/)</p>
<p>- [NeetCode Explanation](https://neetcode.io/problems/longest-substring-without-repeating-characters)</p>
<p>- [Sliding Window Pattern — GeeksForGeeks](https://www.geeksforgeeks.org/window-sliding-technique/)</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 15 / Soft Skills Day 15</h2>

<h2>适应性：快速学习新技术 / Adaptability: Learning New Tech Fast</h2>

<p><strong>面试题 / Question:</strong></p>
<p>&quot;Describe a time you had to learn a new technology quickly to solve a problem.&quot;</p>

<hr/>

<h3>🎯 为什么面试官问这个 / Why Interviewers Ask This</h3>

<p>技术栈变化极快。面试官想知道：</p>
<p>1. 你面对陌生技术时会恐慌还是系统地应对？</p>
<p>2. 你的学习方法是否高效？</p>
<p>3. 你能否在压力下交付？</p>

<p>Tech stacks evolve fast. Interviewers want to know: Do you panic or adapt? Do you have a <strong>system</strong> for learning under pressure? Can you still deliver?</p>

<hr/>

<h3>⭐ STAR 框架 / STAR Breakdown</h3>

<p><strong>Situation（情境）:</strong></p>
<p>描述具体的业务压力 — 不要说&quot;我需要学习新技术&quot;，要说&quot;我们有 2 周的 deadline，而现有的技术栈无法满足需求&quot;。</p>

<p><strong>Task（任务）:</strong></p>
<p>你的具体职责是什么？是主导学习还是支援？</p>

<p><strong>Action（行动）— 这是重点！</strong></p>
<p>面试官最想听的是你的<strong>学习策略</strong>：</p>
<p>- 你怎么快速搭建 mental model？（官方文档 → 官方示例 → 一个真实小项目）</p>
<p>- 你怎么判断&quot;够用了&quot;？（能解决当前问题即可，不追求精通）</p>
<p>- 你遇到障碍时怎么求助？（Stack Overflow → 同事 → 官方 issue）</p>

<p><strong>Result（结果）:</strong></p>
<p>量化交付：时间、质量、影响。</p>

<hr/>

<h3>❌ Bad Answer vs ✅ Good Answer</h3>

<p><strong>❌ 差劲的回答:</strong></p>
<p>&quot;我们需要用 Kubernetes，我就去学了 K8s，然后把服务迁移过去了，很顺利。&quot;</p>

<p>问题：没有细节，没有困难，没有学习过程——听起来是在背稿。</p>

<hr/>

<p><strong>✅ 优秀的回答 (示例):</strong></p>
<p>&gt; &quot;We were 3 weeks from launching a real-time feature, and our backend team decided mid-project to use WebSockets via Socket.io — something I&#x27;d never touched. I had 4 days before my frontend piece needed to integrate.</p>
<p>&gt;</p>
<p>&gt; I started with the official docs to get a mental model (30 min), then built a tiny chat demo locally to feel the API (2 hours). I identified the 3 patterns I&#x27;d actually need: <code>emit</code>, <code>on</code>, and room-based broadcasting. I skipped everything else.</p>
<p>&gt;</p>
<p>&gt; Day 2, I hit a race condition where events fired before the socket connected. I found the root cause via the Socket.io FAQ, added a connection guard, and documented it for the team.</p>
<p>&gt;</p>
<p>&gt; We shipped on time. The feature had zero WebSocket-related bugs in production. I also wrote an internal doc that helped 2 other engineers onboard faster.&quot;</p>

<hr/>

<h3>💡 Senior/Staff 级加分点 / Senior/Staff Tips</h3>

<p>1. <strong>展示元认知 / Show metacognition</strong> — 不只是&quot;我学了 X&quot;，而是&quot;我用了 Y 策略学 X，因为 Z&quot;</p>
<p>2. <strong>说明你如何判断边界 / Scope your learning</strong> — &quot;我在 40% 的时间里掌握了 80% 的需求场景——这是故意的选择&quot;</p>
<p>3. <strong>团队放大效应 / Multiply impact</strong> — &quot;我写了文档/做了分享，减少了团队学习成本&quot;</p>
<p>4. <strong>展示迁移能力 / Show transfer</strong> — &quot;这次学 Redis Streams 的经验，让我 3 个月后学 Kafka 快了 3 倍&quot;</p>

<hr/>

<h3>🔑 关键要点 / Key Takeaways</h3>

<p>- 学习要有<strong>系统</strong>：mental model → 最小可用知识 → 实战 → 总结</p>
<p>- 面试时强调<strong>trade-off</strong>：你选择了快速上手而不是全面掌握</p>
<p>- <strong>量化</strong>：时间节省、错误减少、团队效率提升</p>

<hr/>

<h3>📚 References</h3>

<p>- [STAR Method Explained — The Muse](https://www.themuse.com/advice/star-interview-method)</p>
<p>- [How to Learn Anything Fast (Josh Kaufman)](https://joshkaufman.net/the-first-20-hours/)</p>
<p>- [Meta Engineering: Learning Culture](https://engineering.fb.com/culture/)</p>

<hr/>

<h3>🧒 ELI5</h3>

<p>面试官在问：&quot;当你碰到从没见过的东西，你会怎么办？&quot; 正确答案不是&quot;我啥都会&quot;，而是&quot;我有一套靠谱的方法，让我快速从零变成够用。&quot;</p>

<p>The interviewer asks: &quot;What happens when you hit something you&#x27;ve never seen?&quot; The right answer isn&#x27;t &quot;I know everything.&quot; It&#x27;s &quot;I have a reliable method to go from zero to useful, fast.&quot;</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 15 / Frontend Day 15</h2>

<h2>React Composition — Children, Render Props, HOCs</h2>
<h2>React 组合模式 — 子组件、渲染属性、高阶组件</h2>

<hr/>

<h3>🌏 真实场景 / Real Scenario</h3>

<p>你在做一个 Dashboard，需要一个 <code>Card</code> 组件——有时里面是图表，有时是表格，有时是表单。你不想为每种情况写 <code>ChartCard</code>、<code>TableCard</code>、<code>FormCard</code>……</p>

<p>You&#x27;re building a Dashboard. You need a <code>Card</code> component — sometimes it holds a chart, sometimes a table, sometimes a form. You don&#x27;t want to write <code>ChartCard</code>, <code>TableCard</code>, <code>FormCard</code> separately.</p>

<p><strong>React Composition（组合）</strong> 是解决方案。</p>

<hr/>

<h3>📦 Pattern 1: Children Props（最常用）</h3>

<pre><code>
// Generic Card shell — accepts anything as children
interface CardProps {
  title: string;
  children: React.ReactNode; // &lt;-- the magic
}

function Card({ title, children }: CardProps) {
  return (
    &lt;div className=&quot;card&quot;&gt;
      &lt;h2&gt;{title}&lt;/h2&gt;
      &lt;div className=&quot;card-body&quot;&gt;{children}&lt;/div&gt;
    &lt;/div&gt;
  );
}

// Usage: inject any content
function Dashboard() {
  return (
    &lt;Card title=&quot;用户增长 / User Growth&quot;&gt;
      &lt;LineChart data={chartData} /&gt;
    &lt;/Card&gt;
  );
}
</code></pre>

<p><strong>何时用 / When to use:</strong> 容器/布局组件，内容不确定。</p>

<hr/>

<h3>🎯 Pattern 2: Render Props（渲染属性）</h3>

<p>当父组件需要<strong>向子内容传递数据</strong>时：</p>

<p>When the parent needs to <strong>pass data into the child content</strong>:</p>

<pre><code>
interface DataFetcherProps&lt;T&gt; {
  url: string;
  render: (data: T | null, loading: boolean) =&gt; React.ReactNode;
}

function DataFetcher&lt;T&gt;({ url, render }: DataFetcherProps&lt;T&gt;) {
  const [data, setData] = useState&lt;T | null&gt;(null);
  const [loading, setLoading] = useState(true);

  useEffect(() =&gt; {
    fetch(url).then(r =&gt; r.json()).then(d =&gt; {
      setData(d);
      setLoading(false);
    });
  }, [url]);

  return &lt;&gt;{render(data, loading)}&lt;/&gt;;
}

// Usage
&lt;DataFetcher&lt;User[]&gt;
  url=&quot;/api/users&quot;
  render={(users, loading) =&gt;
    loading ? &lt;Spinner /&gt; : &lt;UserTable data={users!} /&gt;
  }
/&gt;
</code></pre>

<p>&gt; 💡 现代 React 里，<strong>Custom Hooks 通常比 Render Props 更清晰</strong>。Render Props 更多见于老代码或需要 JSX 层控制的场景。</p>

<hr/>

<h3>🔧 Pattern 3: Higher-Order Components (HOC)</h3>

<pre><code>
// withAuth: wraps any component to require authentication
function withAuth&lt;P extends object&gt;(
  WrappedComponent: React.ComponentType&lt;P&gt;
) {
  return function AuthenticatedComponent(props: P) {
    const { isLoggedIn } = useAuth();

    if (!isLoggedIn) {
      return &lt;Navigate to=&quot;/login&quot; /&gt;;
    }

    return &lt;WrappedComponent {...props} /&gt;;
  };
}

// Usage
const ProtectedDashboard = withAuth(Dashboard);
</code></pre>

<hr/>

<h3>🎮 猜猜输出 / Quiz</h3>

<pre><code>
function Wrapper({ children }: { children: React.ReactNode }) {
  console.log(&quot;Wrapper rendered&quot;);
  return &lt;div&gt;{children}&lt;/div&gt;;
}

function App() {
  const [count, setCount] = useState(0);
  return (
    &lt;Wrapper&gt;
      &lt;button onClick={() =&gt; setCount(c =&gt; c + 1)}&gt;
        Count: {count}
      &lt;/button&gt;
    &lt;/Wrapper&gt;
  );
}
</code></pre>

<p>点击按钮时，&quot;Wrapper rendered&quot; 会打印吗？/ Does &quot;Wrapper rendered&quot; print on button click?</p>

<p><strong>A)</strong> 每次点击都打印</p>
<p><strong>B)</strong> 只在初始渲染打印</p>
<p><strong>C)</strong> 每次点击都打印（因为 <code>children</code> 是新 JSX 对象）</p>
<p><strong>D)</strong> 取决于 React 版本</p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>答案：C</strong> — <code>Wrapper</code> 每次都会重新渲染。</p>

<p>虽然 <code>Wrapper</code> 本身没有 state，但每次 <code>App</code> 渲染时，<code>&lt;button&gt;Count: {count}&lt;/button&gt;</code> 是一个<strong>新的 JSX 元素</strong>（新的对象引用），所以 <code>Wrapper</code> 也会重新渲染。</p>

<p>要阻止这种行为，需要把 <code>Wrapper</code> 包裹在 <code>React.memo</code> 中 <strong>并且</strong> 确保 <code>children</code> 引用不变（实际上很难）。</p>

<p>In React, <code>children</code> is a new JSX object on every parent render — so <code>Wrapper</code> re-renders too. To prevent this, you&#x27;d need <code>React.memo</code> AND stable <code>children</code> refs.</p>

<p>&lt;/details&gt;</p>

<hr/>

<h3>❌ vs ✅ 常见错误 / Common Mistakes</h3>

<p><strong>❌ Prop Drilling Hell:</strong></p>
<pre><code>
// Passing title 5 levels deep just to show it in a card
&lt;Page title=&quot;Dashboard&quot;&gt;
  &lt;Layout title=&quot;Dashboard&quot;&gt;
    &lt;Section title=&quot;Dashboard&quot;&gt;
      &lt;Card title=&quot;Dashboard&quot;&gt; ... &lt;/Card&gt;
</code></pre>

<p><strong>✅ Composition with Children:</strong></p>
<pre><code>
&lt;Card&gt;
  &lt;CardHeader&gt;Dashboard&lt;/CardHeader&gt;
  &lt;CardBody&gt;...&lt;/CardBody&gt;
&lt;/Card&gt;
</code></pre>

<hr/>

<h3>⚖️ 何时用哪个 / When to Use Which</h3>

<p>| 场景 | 推荐模式 |</p>
<p>|------|----------|</p>
<p>| 容器/布局，内容不定 | <code>children</code> props |</p>
<p>| 共享逻辑，不共享 UI | <strong>Custom Hook</strong> (首选) |</p>
<p>| 需要向内容注入数据 | Render Props 或 Custom Hook |</p>
<p>| 跨组件横切关注点（认证、日志） | HOC |</p>
<p>| 老 class component 代码 | HOC（因为 hooks 不能用于 class） |</p>

<hr/>

<h3>📚 References</h3>

<p>- [React Composition vs Inheritance — Official Docs](https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children)</p>
<p>- [Render Props Pattern — Kent C. Dodds](https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props)</p>
<p>- [HOC Docs — React](https://legacy.reactjs.org/docs/higher-order-components.html)</p>

<hr/>

<h3>🧒 ELI5</h3>

<p>就像乐高积木：<code>children</code> props 让你把任意积木放进一个盒子；Render Props 让盒子告诉你&quot;我准备好了，你可以放这种积木&quot;；HOC 像是给积木套一个外壳（防水壳、认证壳）。</p>

<p>Like Lego: <code>children</code> props = drop any brick into a box. Render Props = box tells you what brick fits. HOC = wrap a brick in a protective shell.</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 17 — 本周 AI 大事件 / AI News Roundup</h2>
<p><em>来源 / Sources: web search, April 2026</em></p>

<hr/>

<p><strong>📰 Story 1: OpenAI 收购科技脱口秀 TBPN，首次进军媒体 / OpenAI acquires tech talk show TBPN, its first move into media</strong></p>

<p>来源: https://openai.com/index/openai-acquires-tbpn/</p>

<p>OpenAI 宣布收购每日科技谈话节目 TBPN，并表示将保持其编辑独立性；此举被视为公司在“塑造 AI 叙事”和公共沟通上的战略加码。</p>
<p>OpenAI announced it acquired the daily tech talk show TBPN and said it will preserve editorial independence—signaling a strategic push to shape the public AI narrative.</p>

<p><strong>为什么你应该关心:</strong> 未来 AI 竞争不只在模型能力，也在“谁掌握公众理解与信任”；媒体渠道会影响监管、人才、客户与舆论风向。/ AI competition isn’t only about model quality—control over narrative and trust can influence regulation, hiring, customer adoption, and public sentiment.</p>

<hr/>

<p><strong>📰 Story 2: Google 推出 Gemini API 的 Flex / Priority Inference，帮助企业控成本与稳定性 / Google adds Flex &amp; Priority Inference tiers to the Gemini API</strong></p>

<p>来源: https://www.infoworld.com/article/4154145/google-gives-enterprises-new-controls-to-manage-ai-inference-costs-and-reliability.html</p>

<p>Google 为 Gemini API 增加新的推理服务分层，让企业在成本、延迟与可靠性之间做更细粒度的取舍，面向更复杂的多步骤“Agent 工作流”。</p>
<p>Google introduced new inference service tiers for the Gemini API, letting enterprises trade off cost, latency, and reliability—especially for complex, multi-step agent workflows.</p>

<p><strong>为什么你应该关心:</strong> “AI 变贵”是落地最大障碍之一；更灵活的推理定价与 QoS 会决定哪些产品能规模化、哪些只能停留在 demo。/ Inference economics often decide whether AI products scale or stay demos; pricing/QoS controls directly shape what’s viable in production.</p>

<hr/>

<p><strong>📰 Story 3: Google Research 发布 TurboQuant：大模型推理内存压缩 6 倍 / Google Research unveils TurboQuant memory compression for LLM inference</strong></p>

<p>来源: https://www.networkworld.com/article/4154034/google-research-talks-compression-technology-it-says-will-greatly-reduce-memory-needed-for-ai-processing.html</p>

<p>TurboQuant 据称可将大模型推理所需内存降低 6 倍，并在相同 GPU 数量下提升速度，同时尽量不牺牲精度；这类压缩技术有望推动更多“端侧 AI”能力。</p>
<p>TurboQuant reportedly cuts LLM inference memory by 6× and boosts speed with the same GPU count while preserving accuracy—potentially accelerating more capable on-device AI.</p>

<p><strong>为什么你应该关心:</strong> 算法层面的“省内存/省算力”会直接改变硬件需求与成本结构，决定 AI 是集中在云端，还是能更广泛地下沉到手机、PC 与边缘设备。/ Efficiency breakthroughs reshape hardware demand and unit economics, determining whether AI stays cloud-only or becomes truly ubiquitous on phones, PCs, and edge devices.</p>

<hr/>

<p><strong>📰 Story 4: 医疗 AI 新进展：Noah Labs 的 Vox 获 FDA 认定，可用 5 秒语音筛查心衰 / Healthcare AI: Noah Labs’ Vox gets FDA designation for detecting heart failure from a 5-second voice sample</strong></p>

<p>来源: https://www.buildez.ai/blog/ai-trending-april-2026-developments</p>

<p>报道指出 Noah Labs 的 Vox 获得 FDA 相关认定，可通过短短 5 秒语音信号进行心衰风险检测；这展示了 AI 在临床前筛查与远程健康管理的潜力。</p>
<p>Reports say Noah Labs’ Vox received an FDA-related designation and can detect heart failure risk from a 5-second voice sample, highlighting AI’s potential in pre-clinical screening and remote care.</p>

<p><strong>为什么你应该关心:</strong> 当 AI 开始进入受监管医疗体系，它的价值不再只是“更聪明”，而是能否真正改善结果、降低成本并通过合规审查。/ As AI enters regulated healthcare, the bar shifts from “smart” to clinically useful, cost-effective, and compliant—opening massive markets (and responsibilities).</p>

<hr/>

<p>📚 <strong>References</strong></p>
<p>1. https://openai.com/index/openai-acquires-tbpn/</p>
<p>2. https://www.infoworld.com/article/4154145/google-gives-enterprises-new-controls-to-manage-ai-inference-costs-and-reliability.html</p>
<p>3. https://www.networkworld.com/article/4154034/google-research-talks-compression-technology-it-says-will-greatly-reduce-memory-needed-for-ai-processing.html</p>
<p>4. https://www.buildez.ai/blog/ai-trending-april-2026-developments</p>

<p>🧒 <strong>ELI5:</strong> 这周 AI 的重点是：大公司在“更省钱更稳定地跑 AI”、以及把 AI 推进现实世界（媒体与医疗）上同时加速。/ This week’s theme: big players are making AI cheaper and more reliable to run—and pushing it deeper into the real world (media and healthcare).</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-04-02</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-04-02</guid>
      <pubDate>Thu, 02 Apr 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 14 / System Design Day 14</h2>
<h2>API Gateway &amp; Service Mesh</h2>

<hr/>

<h3>🌍 真实场景 / Real-World Scenario</h3>

<p>想象你在优步工作，系统里有 200 个微服务：乘客服务、司机服务、定价服务、路线服务、支付服务……</p>

<p>每个客户端（iOS App、Android App、Web 前端、第三方合作伙伴）直接调用每个服务？<strong>噩梦开始了。</strong></p>

<p>这就是为什么优步、Netflix、Amazon 都在用 <strong>API Gateway + Service Mesh</strong> 这两层抽象。</p>

<hr/>

<p>Imagine you&#x27;re at Uber with 200 microservices: rider, driver, pricing, routing, payments...</p>

<p>Every client (iOS, Android, web, partners) calling each service directly? <strong>Nightmare begins.</strong></p>

<p>This is why Uber, Netflix, and Amazon all use <strong>API Gateway + Service Mesh</strong> — two layers of abstraction.</p>

<hr/>

<h3>🏛️ ASCII 架构图</h3>

<pre><code>
外部流量 / External Traffic
         │
         ▼
┌─────────────────────┐
│    API Gateway      │  ← 统一入口 / Single Entry Point
│  (Kong/AWS API GW)  │    认证、限流、路由、日志
│                     │    Auth, Rate Limit, Route, Log
└────────┬────────────┘
         │ 内部流量 / Internal Traffic
         ▼
┌─────────────────────────────────────────┐
│           Service Mesh (Istio/Envoy)    │
│                                         │
│  ┌──────────┐    ┌──────────┐          │
│  │Service A │◄──►│Service B │          │
│  │[sidecar] │    │[sidecar] │          │
│  └──────────┘    └──────────┘          │
│         ↕               ↕              │
│  ┌──────────┐    ┌──────────┐          │
│  │Service C │◄──►│Service D │          │
│  │[sidecar] │    │[sidecar] │          │
│  └──────────┘    └──────────┘          │
│                                         │
│  自动 mTLS、熔断、重试、可观测性        │
│  Auto mTLS, Circuit Break, Retry, Obs  │
└─────────────────────────────────────────┘
</code></pre>

<hr/>

<h3>🔍 核心概念 / Core Concepts</h3>

<p>#### API Gateway — 对外的门卫</p>
<p><strong>做什么 / What it does:</strong></p>
<p>- ✅ 认证鉴权 (JWT/OAuth2)</p>
<p>- ✅ 速率限制 (Rate Limiting) — 防刷</p>
<p>- ✅ 请求路由 — <code>/api/v1/users</code> → User Service</p>
<p>- ✅ 协议转换 — REST → gRPC</p>
<p>- ✅ 请求聚合 — 一次请求，内部调 3 个服务</p>
<p>- ✅ SSL 终止 (TLS Termination)</p>

<p><strong>常见产品 / Products:</strong> Kong, AWS API Gateway, Nginx, Envoy, Traefik</p>

<p>#### Service Mesh — 对内的神经系统</p>
<p><strong>做什么 / What it does:</strong></p>
<p>- ✅ 服务间 mTLS 加密（零信任网络）</p>
<p>- ✅ 熔断器 (Circuit Breaker) — 防雪崩</p>
<p>- ✅ 自动重试 + 超时</p>
<p>- ✅ 流量管理 (Canary, A/B Test)</p>
<p>- ✅ 分布式追踪 (Tracing)</p>
<p>- ✅ 服务发现 (Service Discovery)</p>

<p><strong>实现方式:</strong> Sidecar 代理（每个服务旁边注入一个 Envoy 代理）</p>
<p><strong>常见产品 / Products:</strong> Istio, Linkerd, Consul Connect</p>

<hr/>

<h3>⚖️ 关键权衡 / Key Tradeoffs</h3>

<p>| 方案 | 优点 | 缺点 |</p>
<p>|------|------|------|</p>
<p>| API Gateway 独立 | 简单，运维成本低 | 服务间通信无管控 |</p>
<p>| Service Mesh 独立 | 内部流量全覆盖 | 复杂度高，sidecar 开销 |</p>
<p>| 两者结合 ✅ | 完整的流量控制 | 需要专门的平台团队维护 |</p>

<p><strong>为什么这样设计？/ Why this design?</strong></p>

<p>API Gateway 和 Service Mesh 解决<strong>不同层面</strong>的问题：</p>
<p>- Gateway = <strong>南北流量</strong>（外→内）</p>
<p>- Service Mesh = <strong>东西流量</strong>（内→内）</p>

<p>用一个工具同时管两种流量会导致职责不清、配置混乱。</p>

<hr/>

<h3>⚠️ 常见踩坑 / Common Mistakes</h3>

<pre><code>
❌ 把所有业务逻辑放在 API Gateway 里
   → Gateway 应该是&quot;哑路由&quot;，不应该懂业务

❌ 在没有可观测性的情况下上 Service Mesh
   → Mesh 的价值在于追踪和监控，没有这些等于白上

❌ 用 Service Mesh 替代 API Gateway
   → Mesh 不做外部认证和速率限制

❌ 每个团队各自搭 Gateway
   → 应该是全公司统一，否则安全策略碎片化
</code></pre>

<hr/>

<h3>📚 References</h3>

<p>- [Kong API Gateway Docs](https://docs.konghq.com/gateway/latest/) — 主流开源 API Gateway</p>
<p>- [Istio Architecture Overview](https://istio.io/latest/docs/ops/deployment/architecture/) — Service Mesh 权威文档</p>
<p>- [What is a Service Mesh? — CNCF](https://glossary.cncf.io/service-mesh/) — 清晰的概念解释</p>

<hr/>

<h3>🧒 ELI5 (像我5岁一样解释)</h3>

<p><strong>中文：</strong></p>
<p>想象一个大型游乐园。API Gateway 是大门口的保安，检查你的票、告诉你哪个游乐设施在哪里。Service Mesh 是园区内部的通信系统——每个游乐设施之间如何协调、出故障时怎么绕路。一个管进门，一个管园内。</p>

<p><strong>English:</strong></p>
<p>Imagine a big theme park. The API Gateway is the security guard at the main entrance — checks your ticket, tells you where things are. The Service Mesh is the internal walkie-talkie system between rides — how they coordinate, what happens when one breaks down. One manages getting IN, the other manages moving AROUND.</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 15 / Algorithms Day 15</h2>
<h2>#121 Best Time to Buy and Sell Stock (Easy) — Sliding Window</h2>
<p>🔗 LeetCode: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/  🟢</p>
<p>📹 NeetCode: https://www.youtube.com/watch?v=1pkOgXD63yU</p>

<hr/>

<h3>🧩 新模式 / New Pattern: 滑动窗口模式 (Sliding Window)</h3>
<p>📍 This block: 6 problems</p>

<p><strong>什么时候用 / When to use:</strong> 连续子数组/子串的最大值、最小值、满足条件的最短/最长</p>

<p><strong>识别信号 / Signals:</strong> subarray, substring, contiguous, window, maximum/minimum length</p>

<p><strong>通用模版 / Template:</strong></p>
<pre><code>
left = 0
for right in range(len(arr)):
    window.add(arr[right])  # expand
    while CONDITION_VIOLATED:
        window.remove(arr[left])  # shrink
        left += 1
    result = max(result, right - left + 1)
</code></pre>

<p><strong>核心洞察 / Key Insight:</strong> 右指针扩张探索，左指针收缩维护约束 — 每个元素最多进出窗口各一次</p>

<hr/>

<h3>🌍 现实类比 / Real-world Analogy</h3>
<p><strong>中文：</strong>把股价想成每天的“进货价”。你要做的是：先找到历史最低进货价（买入），然后在未来某天卖出（卖出价 - 买入价最大）。</p>

<p><strong>English:</strong> Think of prices as daily “cost to buy inventory.” You want the lowest cost so far (buy) and the best future selling day to maximize profit.</p>

<hr/>

<h3>🧠 题意拆解 / Problem Restatement</h3>
<p><strong>中文：</strong>给定数组 <code>prices[i]</code> 表示第 i 天价格。只能买一次、卖一次（卖在买之后）。求最大利润。</p>

<p><strong>English:</strong> Given <code>prices[i]</code> as day i price. Buy once, sell once (sell after buy). Return max profit.</p>

<hr/>

<h3>🗺️ 映射到滑动窗口 / Map to the Pattern Template</h3>
<p>这题看起来不像“窗口里有什么元素集合”，但本质仍是“在线扫描 + 维护一个约束状态”。</p>

<p>- <code>right</code> = 今天（卖出日）</p>
<p>- <code>left</code> 不需要显式移动；我们维护“到目前为止最低买入价” = <code>min_price_so_far</code></p>
<p>- <code>result</code> = 当前最大利润</p>

<p><strong>关键变化 / Key variation:</strong></p>
<p>- 不需要 <code>while</code> 收缩窗口，因为约束不是“窗口合法性”，而是“买入必须在卖出之前”。</p>

<hr/>

<h3>✅ Python 解法 / Python Solution</h3>
<pre><code>
from typing import List

class Solution:
    def maxProfit(self, prices: List[int]) -&gt; int:
        min_price = float(&#x27;inf&#x27;)
        best = 0

        for price in prices:  # price is the current &#x27;sell&#x27; candidate
            # Update the best profit if we sell today
            best = max(best, price - min_price)
            # Update the minimum price seen so far (best &#x27;buy&#x27;)
            min_price = min(min_price, price)

        return best
</code></pre>

<hr/>

<h3>🔎 手动 Trace / Walkthrough Trace</h3>
<p>以 <code>prices = [7,1,5,3,6,4]</code> 为例：</p>

<p>- Day1 price=7: min=7, best=max(0, 7-∞)=0</p>
<p>- Day2 price=1: best=max(0, 1-7)=0, min=1</p>
<p>- Day3 price=5: best=max(0, 5-1)=4, min=1</p>
<p>- Day4 price=3: best=max(4, 3-1)=4, min=1</p>
<p>- Day5 price=6: best=max(4, 6-1)=5, min=1</p>
<p>- Day6 price=4: best=max(5, 4-1)=5, min=1</p>

<p>答案 = 5</p>

<hr/>

<h3>⏱️ 复杂度 / Complexity</h3>
<p>- Time: <strong>O(n)</strong></p>
<p>- Space: <strong>O(1)</strong></p>

<hr/>

<h3>举一反三 / Connect to Other Problems in This Pattern Block</h3>
<p>同一个“右指针扫过去，维护一个状态”的思路，在这个 block 里会逐步升级：</p>

<p>1. <strong>#3 Longest Substring Without Repeating Characters</strong>：窗口里维护“无重复”的约束，需要 <code>while</code> 收缩。</p>
<p>2. <strong>#424 Longest Repeating Character Replacement</strong>：维护“窗口内最多字符频次”和允许替换次数。</p>
<p>3. <strong>#76 Minimum Window Substring</strong>：需要精确覆盖目标字符计数，窗口收缩更讲究。</p>
<p>4. <strong>#239 Sliding Window Maximum</strong>：窗口最大值维护通常用单调队列。</p>

<p><strong>今天这题是最简形态：</strong>窗口里只需要记住“历史最小值”。</p>

<hr/>

<h3>📚 References</h3>
<p>- LeetCode Problem: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/</p>
<p>- NeetCode Explanation: https://www.youtube.com/watch?v=1pkOgXD63yU</p>
<p>- Sliding Window Technique (general): https://www.geeksforgeeks.org/window-sliding-technique/</p>

<hr/>

<h3>🧒 ELI5</h3>
<p><strong>中文：</strong>每天你看到一个价格，就问自己两件事：</p>
<p>1）如果我以前最便宜的时候买了，今天卖能赚多少？</p>
<p>2）今天会不会比以前更便宜，适合作为新的“最便宜买入日”？</p>
<p>一路走到最后，你就找到能赚最多的一次买卖。</p>

<p><strong>English:</strong> Each day, ask: (1) if I had bought at the cheapest earlier day, what profit do I get selling today? (2) is today the new cheapest day to buy? Keep the best profit.</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 14 / Soft Skills Day 14</h2>
<h2>How do you handle technical debt? Give me a specific example</h2>
<p><strong>类别 / Category:</strong> Technical Leadership · Senior/Staff Level</p>

<hr/>

<h3>🎯 为什么重要 / Why This Matters</h3>

<p>每个工程团队都有技术债。面试官不想听你说&quot;我们应该重构&quot;——他们想听你如何<strong>量化</strong>债务、<strong>说服</strong>利益相关者、<strong>执行</strong>还债计划，同时不停业务开发。</p>

<p>Every eng team has tech debt. Interviewers don&#x27;t want &quot;we should refactor&quot; — they want to hear how you <strong>quantified</strong> the debt, <strong>persuaded</strong> stakeholders, and <strong>executed</strong> payoff while keeping feature work moving.</p>

<hr/>

<h3>⭐ STAR 框架示范 / STAR Example</h3>

<p><strong>Situation 情境：</strong></p>
<p>我们的订单服务是 3 年前写的单体模块，每次改价格逻辑都要改 400 行 if-else，每月导致 2-3 次生产事故。</p>

<p>Our order service was a 3-year-old monolith module. Every pricing logic change touched 400 lines of if-else, causing 2-3 production incidents per month.</p>

<p><strong>Task 任务：</strong></p>
<p>作为 Tech Lead，我需要在 Q3 OKR 里推动重构，但产品经理有 12 个新功能排队。</p>

<p>As Tech Lead, I needed to push refactoring into Q3 OKRs while PM had 12 features queued.</p>

<p><strong>Action 行动：</strong></p>
<p>1. <strong>量化痛苦 / Quantify the pain:</strong> 统计过去 6 个月：incident 修复耗时 120 工程师小时，每次 pricing 功能开发耗时是预期的 3x</p>
<p>2. <strong>用数据说服 / Data-driven pitch:</strong> 向 VP Eng 展示&quot;如果不还债，Q4 每个 pricing 功能要 3 周而不是 1 周&quot;</p>
<p>3. <strong>渐进式重构 / Incremental approach:</strong> 不做 Big Bang，设计 Strangler Fig 模式——新功能走新架构，旧功能逐步迁移</p>
<p>4. <strong>20% 规则 / 20% rule:</strong> 每个 sprint 拿出 20% capacity 用于还债，写在 sprint contract 里</p>

<p><strong>Result 结果：</strong></p>
<p>3 个月后 incident rate 降了 70%，新 pricing 功能开发时间从 3 周降到 5 天。VP Eng 在季度 all-hands 上引用这个案例。</p>

<hr/>

<h3>❌ Bad vs ✅ Good</h3>

<pre><code>
❌ &quot;技术债很重要，我们应该分配时间去重构。&quot;
   → 太泛、没有 evidence、没有具体行动

✅ &quot;我追踪了6个月的 incident 数据，发现每月120小时
   浪费在补丁上。我提出 Strangler Fig 方案，用20%
   sprint capacity 渐进还债，3个月后 incident 降70%。&quot;
   → 有数据、有策略、有结果
</code></pre>

<hr/>

<h3>🏅 Senior/Staff Tips</h3>

<p>1. <strong>永远先量化 / Always quantify first</strong> — &quot;技术债导致了 X 小时浪费 / Y 次事故 / Z% 速度下降&quot;，不要用模糊感受</p>
<p>2. <strong>关联业务指标 / Tie to business metrics</strong> — &quot;如果我们不修，下季度功能交付速度慢 40%&quot;</p>
<p>3. <strong>Strangler Fig &gt; Big Bang</strong> — 渐进式替换比全部重写风险低 10 倍</p>
<p>4. <strong>建立持续机制 / Build ongoing mechanism</strong> — 20% rule、tech debt sprints、quality budget 都是好策略</p>
<p>5. <strong>展示 leadership / Show leadership</strong> — 你不是在&quot;要求时间做技术的事&quot;，而是在&quot;保护团队交付速度&quot;</p>

<hr/>

<h3>🔑 Key Takeaways</h3>
<p>- 技术债 = 利息在涨的贷款，不是可有可无的清洁工作</p>
<p>- 量化 + 数据 + 渐进执行 = 让所有人 buy-in 的方程式</p>
<p>- 最好的还债方式：和新功能开发并行，不是&quot;暂停一切来重构&quot;</p>

<hr/>

<h3>📚 References</h3>
<p>- [Martin Fowler — Technical Debt](https://martinfowler.com/bliki/TechnicalDebt.html) — 技术债经典定义</p>
<p>- [Strangler Fig Pattern — Microsoft](https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig) — 渐进式迁移模式</p>
<p>- [Managing Technical Debt — Software Engineering at Google](https://abseil.io/resources/swe-book/html/ch15.html) — Google 的技术债管理经验</p>

<hr/>

<h3>🧒 ELI5</h3>
<p><strong>中文：</strong>技术债就像你房间越来越乱。你可以继续往里塞东西（加功能），但找东西越来越难（bug 越来越多）。聪明的做法不是某天请假大扫除，而是每天花10分钟整理一点。</p>

<p><strong>English:</strong> Tech debt is like your room getting messier over time. You can keep stuffing things in (adding features), but finding anything gets harder (more bugs). The smart move isn&#x27;t taking a day off to deep-clean — it&#x27;s tidying up 10 minutes every day.</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 14 / Frontend Day 14</h2>
<h2>React Context — Global State Without Prop Drilling</h2>
<p><strong>类别 / Category:</strong> React Patterns · Week 3</p>

<hr/>

<h3>🌍 真实场景 / Real Scenario</h3>

<p>你在做一个 SaaS dashboard。用户登录后，<code>user</code> 对象需要在 Header、Sidebar、Settings、ProfileCard 都能访问。你总不能 <code>&lt;App user={user}&gt; → &lt;Layout user={user}&gt; → &lt;Sidebar user={user}&gt; → &lt;UserAvatar user={user}&gt;</code> 一路传下去吧？</p>

<p>You&#x27;re building a SaaS dashboard. After login, the <code>user</code> object needs to be accessible in Header, Sidebar, Settings, ProfileCard. You can&#x27;t keep passing <code>user</code> prop 4 levels deep.</p>

<p><strong>这就是 Prop Drilling 的痛。Context 来拯救你。</strong></p>

<hr/>

<h3>💻 核心代码 / Code Snippet</h3>

<pre><code>
import { createContext, useContext, useState, ReactNode } from &#x27;react&#x27;;

// 1. Create the context with a type
interface User { name: string; role: &#x27;admin&#x27; | &#x27;user&#x27;; }

interface AuthContextType {
  user: User | null;
  login: (user: User) =&gt; void;
  logout: () =&gt; void;
}

const AuthContext = createContext&lt;AuthContextType | null&gt;(null);

// 2. Create a provider component
function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState&lt;User | null&gt;(null);

  const login = (u: User) =&gt; setUser(u);
  const logout = () =&gt; setUser(null);

  return (
    &lt;AuthContext.Provider value={{ user, login, logout }}&gt;
      {children}
    &lt;/AuthContext.Provider&gt;
  );
}

// 3. Create a custom hook (ALWAYS do this)
function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error(&#x27;useAuth must be used within AuthProvider&#x27;);
  return ctx;
}

// 4. Usage — any nested component
function UserAvatar() {
  const { user } = useAuth();
  return &lt;span&gt;{user?.name ?? &#x27;Guest&#x27;}&lt;/span&gt;;
}
</code></pre>

<hr/>

<h3>🧠 猜猜这段代码输出什么？/ Quiz</h3>

<pre><code>
const ThemeContext = createContext(&#x27;light&#x27;);

function App() {
  return (
    &lt;ThemeContext.Provider value=&quot;dark&quot;&gt;
      &lt;Parent /&gt;
    &lt;/ThemeContext.Provider&gt;
  );
}

function Parent() {
  return &lt;Child /&gt;;
}

function Child() {
  const theme = useContext(ThemeContext);
  console.log(theme);
  return &lt;div&gt;{theme}&lt;/div&gt;;
}
</code></pre>

<p><strong>A)</strong> <code>undefined</code></p>
<p><strong>B)</strong> <code>&#x27;light&#x27;</code></p>
<p><strong>C)</strong> <code>&#x27;dark&#x27;</code></p>
<p><strong>D)</strong> Throws an error</p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>C) <code>&#x27;dark&#x27;</code></strong></p>

<p><code>Child</code> 在 <code>ThemeContext.Provider value=&quot;dark&quot;</code> 内部，所以 <code>useContext(ThemeContext)</code> 返回 <code>&#x27;dark&#x27;</code>。<code>&#x27;light&#x27;</code> 是 default，只有在<strong>没有 Provider 包裹时</strong>才生效。</p>

<p>&lt;/details&gt;</p>

<hr/>

<h3>❌ Bad vs ✅ Good</h3>

<pre><code>
// ❌ BAD: Putting everything in one giant Context
const AppContext = createContext({
  user: null, theme: &#x27;light&#x27;, locale: &#x27;en&#x27;,
  cart: [], notifications: [], settings: {}
});
// Problem: ANY change re-renders ALL consumers

// ✅ GOOD: Split into focused contexts
const AuthContext = createContext&lt;AuthContextType | null&gt;(null);
const ThemeContext = createContext&lt;ThemeContextType&gt;({ theme: &#x27;light&#x27; });
const CartContext = createContext&lt;CartContextType&gt;({ items: [] });
// Each context only re-renders its own consumers
</code></pre>

<pre><code>
// ❌ BAD: Using context for frequently changing values
&lt;PositionContext.Provider value={{ x: mouseX, y: mouseY }}&gt;
// Re-renders EVERY consumer 60 times/sec

// ✅ GOOD: Use useRef + subscription for high-frequency updates
// Or use a state manager like Zustand/Jotai for this case
</code></pre>

<hr/>

<h3>🧭 什么时候用 / When to Use</h3>

<p>| 用 Context ✅ | 不用 Context ❌ |</p>
<p>|-------------|---------------|</p>
<p>| 主题 (theme) | 频繁变化的值 (mouse position) |</p>
<p>| 认证状态 (auth) | 复杂的全局状态 (use Zustand) |</p>
<p>| 国际化 (locale/i18n) | 只传 1-2 层的 props |</p>
<p>| 功能开关 (feature flags) | 需要 selector 优化的场景 |</p>

<p><strong>经验法则 / Rule of thumb:</strong> Context 适合&quot;读多写少&quot;的全局数据。如果值频繁变化，考虑 Zustand 或 Jotai。</p>

<hr/>

<h3>📚 References</h3>
<p>- [React Docs — useContext](https://react.dev/reference/react/useContext) — 官方文档</p>
<p>- [React Docs — Passing Data Deeply with Context](https://react.dev/learn/passing-data-deeply-with-context) — 详细教程</p>
<p>- [Kent C. Dodds — How to use React Context effectively](https://kentcdodds.com/blog/how-to-use-react-context-effectively) — 最佳实践</p>

<hr/>

<h3>🧒 ELI5</h3>
<p><strong>中文：</strong>想象你家有一个&quot;公告板&quot;（Context Provider）。任何家庭成员（子组件）不管在哪个房间，都能直接看到公告板上的信息，不需要一个人传一个人地接力。</p>

<p><strong>English:</strong> Think of Context as a family bulletin board. Any family member (child component), no matter which room they&#x27;re in, can read the bulletin directly — no need for a game of telephone passing the message through each person.</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 16 — CONCEPT</h2>
<h2>RAG — Retrieval Augmented Generation</h2>
<h2>检索增强生成 — 让 AI 说真话的关键技术</h2>

<hr/>

<h3>💡 直觉解释 / Intuitive Explanation</h3>

<p><strong>中文：</strong></p>
<p>LLM 有两个大问题：1) 知识有截止日期 2) 会&quot;一本正经地胡说八道&quot;（幻觉）。</p>

<p>RAG 的思路很简单：<strong>先查资料，再回答。</strong></p>

<p>就像一个学生考开卷考试：不用背所有知识，考试时翻书找到相关段落，然后用自己的话回答。LLM 就是那个学生，你的知识库就是那本书。</p>

<p><strong>English:</strong></p>
<p>LLMs have two big problems: 1) knowledge cutoff date 2) they &quot;hallucinate&quot; confidently.</p>

<p>RAG&#x27;s idea is simple: <strong>look it up first, then answer.</strong></p>

<p>Like a student in an open-book exam: instead of memorizing everything, find the relevant passages in the book, then answer in your own words. The LLM is the student, your knowledge base is the book.</p>

<hr/>

<h3>⚙️ 工作原理 / How It Works</h3>

<pre><code>
用户提问                     知识库 (文档/网页/DB)
&quot;Redis 和 Memcached           │
  有什么区别？&quot;                │ 预处理：切块 → 向量化
       │                      │ → 存入向量数据库
       ▼                      │
 ┌───────────┐                ▼
 │ 1. Embed  │──查询向量──► ┌──────────────┐
 │   Query   │              │ Vector DB    │
 └───────────┘              │ (Pinecone/   │
       │                    │  ChromaDB)   │
       │                    └──────┬───────┘
       │                           │
       │    Top-K 相关片段         │
       │  ◄────────────────────────┘
       ▼
 ┌───────────────────────────────┐
 │ 2. 构建 Prompt                │
 │ &quot;根据以下资料回答用户问题：    │
 │  [片段1] [片段2] [片段3]      │
 │  问题：Redis vs Memcached？&quot;  │
 └───────────────┬───────────────┘
                 │
                 ▼
 ┌───────────────────────────────┐
 │ 3. LLM 生成带引用的回答       │
 │ &quot;根据文档，Redis 支持持久化    │
 │  而 Memcached 是纯内存的...&quot;  │
 └───────────────────────────────┘
</code></pre>

<p><strong>三步流程 / Three Steps:</strong></p>
<p>1. <strong>Retrieve 检索</strong> — 把问题转成向量，在向量数据库中找最相关的文档片段</p>
<p>2. <strong>Augment 增强</strong> — 把检索到的片段塞进 prompt 作为上下文</p>
<p>3. <strong>Generate 生成</strong> — LLM 基于上下文生成回答（不靠猜，靠证据）</p>

<hr/>

<h3>🌍 实际应用 / Applications</h3>

<p>| 场景 | 怎么用 RAG |</p>
<p>|------|-----------|</p>
<p>| 企业知识库 | 内部文档 → 向量化 → 员工用自然语言提问 |</p>
<p>| 客服机器人 | FAQ + 产品手册 → 回答准确率从 60% → 95% |</p>
<p>| 代码助手 | 项目代码 + 文档 → 上下文感知的代码建议 |</p>
<p>| 法律/医疗 | 法规/病例库 → 有据可查的回答 |</p>

<p><strong>为什么不直接 Fine-tune？</strong></p>
<p>- Fine-tune：慢、贵、知识固化在模型权重里，更新需要重新训练</p>
<p>- RAG：快、便宜、换文档就换知识，实时更新</p>

<hr/>

<h3>🐍 可运行代码 / Runnable Python Snippet</h3>

<pre><code>
pip install chromadb openai
</code></pre>

<pre><code>
import chromadb

# 1. Create a local vector DB and add documents
client = chromadb.Client()
collection = client.create_collection(&quot;demo&quot;)

collection.add(
    documents=[
        &quot;Redis supports persistence (RDB/AOF), Memcached is in-memory only.&quot;,
        &quot;Redis has data structures: strings, hashes, lists, sets, sorted sets.&quot;,
        &quot;Memcached is multi-threaded; Redis is single-threaded with io_threads.&quot;,
    ],
    ids=[&quot;doc1&quot;, &quot;doc2&quot;, &quot;doc3&quot;],
)

# 2. Query — ChromaDB auto-embeds and finds relevant docs
results = collection.query(query_texts=[&quot;Redis vs Memcached?&quot;], n_results=2)
print(&quot;Retrieved:&quot;, results[&quot;documents&quot;])
# 3. Feed results[&quot;documents&quot;] into your LLM prompt as context
</code></pre>

<hr/>

<h3>📚 References</h3>
<p>- [LangChain RAG Tutorial](https://python.langchain.com/docs/tutorials/rag/) — 实战教程</p>
<p>- [ChromaDB Getting Started](https://docs.trychroma.com/docs/overview/getting-started) — 轻量向量数据库</p>
<p>- [Lewis et al. 2020 — RAG Original Paper](https://arxiv.org/abs/2005.11401) — RAG 论文原文</p>

<hr/>

<h3>🧒 ELI5</h3>
<p><strong>中文：</strong></p>
<p>想象你在答一场考试。普通 AI 全靠记忆答题（有时候记错了还很自信）。RAG AI 答题前先翻了一遍参考书，找到最相关的几页，然后根据书上的内容回答。所以它答得更准，还能告诉你&quot;我是从第 42 页看到的&quot;。</p>

<p><strong>English:</strong></p>
<p>Imagine taking a test. Regular AI answers purely from memory (sometimes confidently wrong). RAG AI first flips through a reference book, finds the most relevant pages, then answers based on what the book says. So it&#x27;s more accurate and can even say &quot;I found this on page 42.&quot;</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-04-01</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-04-01</guid>
      <pubDate>Wed, 01 Apr 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>Review</h1>
<h2>🔄 复习日 Day 15 / Review Day 15</h2>

<p><strong>📊 进度 / Progress:</strong> Day 15/150 · NeetCode: 14/150 · SysDesign: 13/40 · Behavioral: 13/40 · Frontend: 13/50 · AI: 6/30</p>
<p><strong>🔥 4-day streak!</strong></p>

<hr/>

<p>今天是复习日！回顾过去4天的内容。</p>
<p>Today is a review day! Let&#x27;s revisit the past 4 days of content.</p>

<p><strong>回顾范围 / Review scope:</strong> Days 11–14</p>
<p>- 🏗️ Consistent Hashing, CAP Theorem, Message Queues, Microservices vs Monolith</p>
<p>- 💻 Two Sum II, 3Sum, Container With Most Water, Trapping Rain Water</p>
<p>- 🗣️ Proactive problem-solving, Prioritization, Delivering bad news, Cross-team initiatives</p>
<p>- 🎨 React useEffect, useRef, useMemo/useCallback, Custom Hooks</p>
<p>- 🤖 AI News, RLHF, AI News, LoRA &amp; QLoRA</p>

<hr/>

<h2>📝 Quick Quiz — 3 Mini-Reviews</h2>

<hr/>

<p><strong>Q1: [🏗️ System Design — Consistent Hashing &amp; CAP Theorem]</strong></p>

<p>你在设计一个分布式缓存系统（比如 Redis Cluster）。当一个节点崩溃时，使用普通哈希（<code>hash(key) % N</code>）和一致性哈希（Consistent Hashing）各会发生什么？为什么分布式数据库（如 Cassandra）选择 Eventual Consistency 而不是强一致性？</p>

<p>You&#x27;re designing a distributed cache (like Redis Cluster). When one node goes down, what happens with regular hashing (<code>hash(key) % N</code>) vs consistent hashing? And why do databases like Cassandra prefer eventual consistency over strong consistency?</p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>普通哈希的问题 / Regular hashing problem:</strong></p>
<p>当节点数 N 变化（比如从 5 变成 4），几乎所有的 key 都需要重新映射到不同节点 — 导致大规模缓存失效（cache stampede）。</p>
<p>When N changes (5→4), nearly all keys get remapped to different nodes — causing a massive cache miss storm.</p>

<p><strong>一致性哈希的优势 / Consistent hashing advantage:</strong></p>
<p>将节点和 key 都映射到一个&quot;哈希环&quot;上。某节点下线时，只有该节点顺时针方向的 key 需要迁移到下一个节点，其他 key 完全不受影响。通常只影响 1/N 的数据。</p>
<p>Both nodes and keys are mapped onto a ring. When a node goes down, only the keys <em>between</em> the failed node and its predecessor need to migrate to the next node clockwise — roughly 1/N of all keys.</p>

<p><strong>Virtual nodes (VNodes)</strong> 虚拟节点进一步让每个物理节点在哈希环上占据多个位置，使数据分布更均匀。</p>

<p><strong>CAP &amp; Eventual Consistency:</strong></p>
<p>CAP 定理说在网络分区（P）发生时，你必须在一致性（C）和可用性（A）之间选一个。Cassandra 选择 AP（可用性 + 分区容忍），允许暂时不一致，通过后台的 gossip protocol 和 hinted handoff 最终达到一致。这对&quot;写多读多&quot;场景更实用 — 写入不会因为网络抖动而失败。</p>

<p>CAP says during a network partition, choose C or A. Cassandra picks AP (availability + partition tolerance), accepting that different replicas may briefly disagree. Background reconciliation (gossip, read repair, hinted handoff) eventually converges. For high-traffic apps, &quot;eventually consistent&quot; is a feature — writes don&#x27;t fail just because one replica is slow.</p>

<p><strong>核心记忆点 / Key insight:</strong> Consistent hashing = <strong>minimal redistribution</strong>. CAP = <strong>pick your tradeoff explicitly</strong>.</p>

<p>&lt;/details&gt;</p>

<hr/>

<p><strong>Q2: [💻 Algorithms — Two Pointers: Container With Most Water vs Trapping Rain Water]</strong></p>

<p>两道题都用双指针，都涉及&quot;水&quot;，但思路有微妙差别。<code>Container With Most Water</code>（#11）和 <code>Trapping Rain Water</code>（#42）的关键区别是什么？为什么前者一次遍历就够，后者需要追踪历史最大值？</p>

<p>Both problems use two pointers and involve &quot;water&quot;, but the logic is subtly different. What&#x27;s the key difference between Container With Most Water (#11) and Trapping Rain Water (#42)? Why does the first need only one pass while the second requires tracking historical maximums?</p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>Container With Most Water (#11) — 选最大容器:</strong></p>
<p>你在两个柱子之间装水，水量 = <code>min(left, right) * distance</code>。双指针从两端向中间移动，每次移动<strong>较短的那端</strong> — 因为较长端已经尽力了，继续缩小宽度只有移动短板才有可能增加面积。</p>

<p>You&#x27;re choosing two walls. Water volume = <code>min(left, right) * width</code>. Move the shorter pointer inward — the taller wall can&#x27;t improve the result unless the other wall gets taller. This greedy choice is provably correct.</p>

<p><strong>Trapping Rain Water (#42) — 计算每个格子的积水:</strong></p>
<p>每个位置能接的水 = <code>min(leftMax, rightMax) - height[i]</code>。关键是<strong>每个位置都受其左右两侧最高柱子的限制</strong>，需要知道历史最大值。</p>
<p>双指针做法：维护 <code>leftMax</code> 和 <code>rightMax</code>，哪侧较小就处理哪侧（因为较小侧的瓶颈已经确定）。</p>

<p>Every cell can hold water = <code>min(leftMax, rightMax) - height[i]</code>. Each cell is bounded by the <em>tallest</em> wall on BOTH sides — you must track running maximums. Two-pointer trick: whichever side has the smaller max, process it (its bottleneck is determined regardless of the other side&#x27;s future values).</p>

<p><strong>核心区别 / Core difference:</strong></p>
<p>- Container: <strong>两点之间</strong> 的最大矩形，贪心移动短板 ✓</p>
<p>- Trapping: <strong>每个点</strong> 上方的积水，需要双侧历史最大值 ✓</p>

<p><strong>记忆口诀:</strong> Container = &quot;短板决定上限，移短板求最大&quot;；Trapping = &quot;每格水位 = 两侧最高墙的最小值 − 自身高度&quot;</p>

<p>&lt;/details&gt;</p>

<hr/>

<p><strong>Q3: [🎨 Frontend — React Hooks: useEffect, useRef, useMemo/useCallback, Custom Hooks]</strong></p>

<p>你在做一个数据密集型 dashboard，需要：</p>
<p>1. 在组件挂载时 fetch 数据并在卸载时取消请求</p>
<p>2. 直接操作一个 DOM 元素（聚焦输入框）而不触发重渲染</p>
<p>3. 避免昂贵的排序函数在每次渲染时重复执行</p>
<p>4. 把上面的 fetch 逻辑复用到多个组件</p>

<p>请说明应该用哪个 Hook，以及最常见的错误写法。</p>

<p>You&#x27;re building a data-heavy dashboard and need to: (1) fetch data on mount and cancel on unmount, (2) focus a DOM element without triggering re-renders, (3) avoid re-running an expensive sort on every render, (4) reuse the fetch logic across components. Which hook for each, and what&#x27;s the most common mistake?</p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>1. Fetch + 取消请求 → <code>useEffect</code></strong></p>
<pre><code>
useEffect(() =&gt; {
  const controller = new AbortController();
  fetch(url, { signal: controller.signal }).then(setData);
  return () =&gt; controller.abort(); // cleanup!
}, [url]); // dependency array matters!
</code></pre>
<p>❌ 最常见错误：省略 cleanup，导致组件卸载后仍然 setState，产生内存泄漏和 &quot;Can&#x27;t perform a React state update on an unmounted component&quot; 警告。</p>
<p>❌ Most common mistake: forgetting the cleanup function, causing state updates on unmounted components.</p>

<p><strong>2. 操作 DOM → <code>useRef</code></strong></p>
<pre><code>
const inputRef = useRef(null);
// inputRef.current.focus() — doesn&#x27;t trigger re-render
</code></pre>
<p><code>useRef</code> 的值改变<strong>不会触发重渲染</strong>，适合存储 DOM 引用、定时器 ID、或任何&quot;不影响 UI&quot;的可变值。</p>
<p>Changing <code>.current</code> never triggers a re-render — perfect for DOM refs, timer IDs, or previous values.</p>

<p><strong>3. 避免重复计算 → <code>useMemo</code></strong></p>
<pre><code>
const sorted = useMemo(() =&gt; expensiveSort(data), [data]);
</code></pre>
<p>❌ 过度使用 useMemo 反而增加开销。只在<strong>真正昂贵</strong>的计算或<strong>引用稳定性</strong>（传给子组件）时使用。</p>
<p>❌ Over-memoizing adds overhead. Only use for genuinely expensive computations or referential stability.</p>

<p><strong>4. 复用逻辑 → Custom Hook</strong></p>
<pre><code>
function useDashboardData(url) {
  const [data, setData] = useState(null);
  useEffect(() =&gt; { /* fetch logic */ }, [url]);
  return data;
}
</code></pre>
<p>Custom hooks = 把 hook 逻辑从组件里提取出来，不是 HOC，不是 render props，就是普通函数（必须以 <code>use</code> 开头）。</p>
<p>Custom hooks extract stateful logic — not a new React feature, just a naming convention (<code>use</code> prefix) that signals to React&#x27;s linter.</p>

<p><strong>记忆矩阵 / Memory matrix:</strong></p>
<p>| Goal | Hook |</p>
<p>|------|------|</p>
<p>| Side effects, fetch, subscriptions | <code>useEffect</code> |</p>
<p>| DOM access, mutable value (no re-render) | <code>useRef</code> |</p>
<p>| Expensive computation cache | <code>useMemo</code> |</p>
<p>| Stable function reference | <code>useCallback</code> |</p>
<p>| Reusable stateful logic | Custom Hook |</p>

<p>&lt;/details&gt;</p>

<hr/>

<p>💡 <em>复习巩固记忆，螺旋式上升。每次回顾不只是&quot;记住了吗&quot;，而是&quot;能用自己的话解释吗&quot;。</em></p>
<p><em>Review doesn&#x27;t just ask &quot;do you remember?&quot; — it asks &quot;can you explain it in your own words?&quot;</em></p>

<p>📅 明天继续新内容！Day 16 coming tomorrow!</p>

<hr/>

<p><em>Generated by byte-by-byte · Day 15 of 150 · 2026-04-01</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-31</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-31</guid>
      <pubDate>Tue, 31 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 14 / System Design Day 14</h2>
<h2>微服务 vs 单体架构 / Microservices vs Monolith</h2>

<p>&gt; <strong>难度 / Difficulty:</strong> Intermediate · <strong>阶段 / Phase:</strong> Growth · <strong>预计阅读 / Read time:</strong> 3 min</p>

<hr/>

<h2>🌍 真实场景 / Real-World Scenario</h2>

<p>想象你在一家初创公司工作，产品刚上线，代码都在一个仓库里。随着用户量增长到百万级别，你开始思考：要不要把代码拆成独立的服务？什么时候该拆？怎么拆？</p>

<p>Imagine you&#x27;re at a startup. Your entire product lives in one codebase. As you scale to millions of users, you face the classic question: should you break it apart into microservices? When? How?</p>

<hr/>

<h2>🏛️ 架构图 / Architecture Diagrams</h2>

<h3>单体架构 / Monolith</h3>
<pre><code>
┌─────────────────────────────────────────┐
│              Monolith App               │
│  ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│  │  Users   │ │ Orders   │ │Payments │ │
│  │ Module   │ │ Module   │ │ Module  │ │
│  └────┬─────┘ └────┬─────┘ └────┬────┘ │
│       └─────────────┴─────────────┘     │
│                    │                    │
│          ┌─────────▼─────────┐         │
│          │   Single Database │         │
│          └───────────────────┘         │
└─────────────────────────────────────────┘
         │ Deploy everything together │
</code></pre>

<h3>微服务架构 / Microservices</h3>
<pre><code>
Client ──► API Gateway
               │
      ┌────────┼────────┐
      ▼        ▼        ▼
 ┌────────┐ ┌──────┐ ┌──────────┐
 │ Users  │ │Orders│ │ Payments │
 │Service │ │Svc   │ │ Service  │
 └───┬────┘ └──┬───┘ └───┬──────┘
     │         │          │
  ┌──▼──┐  ┌──▼──┐   ┌───▼───┐
  │ DB  │  │ DB  │   │  DB   │
  └─────┘  └─────┘   └───────┘
   (独立部署, message queue 通信)
</code></pre>

<hr/>

<h2>⚖️ 核心权衡 / Key Tradeoffs</h2>

<h3>为什么选单体？/ Why Monolith?</h3>
<p>- <strong>简单</strong> — 一个代码库，一次部署，本地开发直接跑</p>
<p>- <strong>低延迟</strong> — 模块间函数调用，无网络开销</p>
<p>- <strong>事务一致性</strong> — 一个数据库，ACID 事务天然支持</p>
<p>- <strong>适合阶段</strong> — 团队 &lt; 20 人，产品 PMF 还没验证时</p>

<h3>为什么选微服务？/ Why Microservices?</h3>
<p>- <strong>独立扩展</strong> — Payment 服务流量暴增，只扩它，不动 Users 服务</p>
<p>- <strong>技术异构</strong> — 推荐系统用 Python/ML，API 层用 Go，各自最优</p>
<p>- <strong>故障隔离</strong> — 一个服务崩了，不影响整体</p>
<p>- <strong>团队自治</strong> — 不同团队独立发布，互不阻塞</p>
<p>- <strong>适合阶段</strong> — 团队 &gt; 50 人，有专门 DevOps/Platform 团队时</p>

<h3>对比表 / Comparison</h3>
<p>| 维度 | 单体 | 微服务 |</p>
<p>|------|------|--------|</p>
<p>| 部署复杂度 | 低 ✅ | 高 ❌ |</p>
<p>| 开发速度(早期) | 快 ✅ | 慢 ❌ |</p>
<p>| 独立扩展 | ❌ | ✅ |</p>
<p>| 故障隔离 | ❌ | ✅ |</p>
<p>| 数据一致性 | 容易 ✅ | 需要设计 ❌ |</p>
<p>| 运维成本 | 低 ✅ | 高 ❌ |</p>

<hr/>

<h2>🪤 别踩这个坑 / Common Mistakes</h2>

<p><strong>❌ 坑1: 过早微服务化 (Premature Microservices)</strong></p>
<p>刚起步就拆服务，结果团队只有3个人要维护10个服务+Kubernetes。</p>
<p>&gt; &quot;We went microservices on day one, and it almost killed us.&quot; — every startup that tried it too early</p>

<p><strong>✅ 正确做法:</strong> 先做&quot;模块化单体&quot;(Modular Monolith)，内部模块化，边界清晰，后期再物理拆分。</p>

<p><strong>❌ 坑2: 分布式单体 (Distributed Monolith)</strong></p>
<p>拆成多个服务，但服务之间强耦合，必须同步部署。既有微服务的复杂性，又没有微服务的好处。</p>

<p><strong>✅ 正确做法:</strong> 服务间通过 API 或消息队列解耦，不共享数据库。</p>

<p><strong>❌ 坑3: 忽视跨服务事务</strong></p>
<p>订单服务扣库存成功，支付服务失败了，数据不一致。</p>

<p><strong>✅ 正确做法:</strong> 使用 Saga 模式或最终一致性设计。</p>

<hr/>

<h2>📚 References</h2>
<p>- [Martin Fowler — Microservices](https://martinfowler.com/articles/microservices.html)</p>
<p>- [Martin Fowler — Monolith First](https://martinfowler.com/bliki/MonolithFirst.html)</p>
<p>- [AWS — Microservices vs Monolithic Architecture](https://aws.amazon.com/microservices/)</p>

<h2>🧒 ELI5</h2>
<p>单体就像一家小餐厅，一个厨房做所有菜，简单高效。微服务像大型餐厅连锁，每家分店专做一类菜，可以独立扩张，但管理更复杂。刚开始开一家店，别一上来就开连锁。</p>

<p>Monolith = one kitchen that cooks everything. Simple, fast to start. Microservices = a food court where each stall specializes. Great at scale, but way more management. Start with one kitchen; split when it gets too crowded.</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 14 / Algorithms Day 14</h2>
<h2>#42 Trapping Rain Water (Hard) — Two Pointers</h2>

<p>🧩 <strong>Two Pointers (5/5)</strong> — building on the template from earlier days in this block</p>

<p>- 🔗 LeetCode: https://leetcode.com/problems/trapping-rain-water/  🔴</p>
<p>- 📹 NeetCode: https://www.youtube.com/watch?v=ZI2z5pq0TqA</p>
<p>- <strong>Pattern / 模式:</strong> Two Pointers（双指针）</p>

<hr/>

<h2>🌧️ 现实类比 / Real-world analogy</h2>

<p>把城市的屋顶想成一排高度不同的墙。下雨后，低洼处会积水，但能积多少取决于它左边最高的墙和右边最高的墙：</p>

<p>&gt; <strong>water[i] = min(maxLeft, maxRight) - height[i]</strong>（如果为正）</p>

<p>Think of bars as walls. The water above a bar is limited by the shorter of the tallest wall on its left and the tallest wall on its right.</p>

<hr/>

<h2>🧠 问题重述 / Problem</h2>

<p>给定数组 <code>height</code> 表示柱子高度，每根柱子宽度为 1，计算下雨后能接多少雨水。</p>

<p>Given <code>height</code>, compute total trapped water.</p>

<hr/>

<h2>🧩 如何映射到双指针模板 / Map to the Two Pointers template</h2>

<p>之前的双指针块（</p>
<p>#125 回文、#167 两数之和II、#15 三数之和、#11 盛最多水的容器</p>
<p>）里，左右指针“夹逼”的核心是：</p>

<p>- <strong>每一步都能确定一侧的最优/可行性</strong>，因此可以移动那一侧，整体 O(n)</p>

<p>这题的“变化点”是：</p>
<p>- 我们不再追求 pair/sum，而是维护 <strong>leftMax / rightMax</strong>，并在每一步“结算”一侧的水量。</p>

<p>Key twist vs earlier problems: instead of comparing sums/areas, we compare <code>leftMax</code> and <code>rightMax</code>. The side with the smaller max can be finalized because its limiting wall is known.</p>

<hr/>

<h2>✅ 双指针解法 / Two pointers solution</h2>

<h3>核心思路 / Key idea</h3>

<p>- <code>l, r</code> 从两端向中间走</p>
<p>- 维护 <code>leftMax = max(height[0..l])</code>，<code>rightMax = max(height[r..end])</code></p>
<p>- <strong>如果 <code>leftMax &lt; rightMax</code></strong>：左边的水位上限已确定（被 leftMax 限制），可以计算 <code>l</code> 位置的水并 <code>l += 1</code></p>
<p>- 否则：对称处理右边</p>

<hr/>

<h3>Python 代码 / Python code</h3>

<pre><code>
from typing import List

class Solution:
    def trap(self, height: List[int]) -&gt; int:
        l, r = 0, len(height) - 1
        left_max, right_max = 0, 0
        water = 0

        while l &lt; r:
            if height[l] &lt; height[r]:
                # left side is bounded by left_max
                if height[l] &gt;= left_max:
                    left_max = height[l]
                else:
                    water += left_max - height[l]
                l += 1
            else:
                # right side is bounded by right_max
                if height[r] &gt;= right_max:
                    right_max = height[r]
                else:
                    water += right_max - height[r]
                r -= 1

        return water
</code></pre>

<hr/>

<h2>🔍 手动走一遍 / Quick trace</h2>

<p>例子：<code>[0,1,0,2,1,0,1,3,2,1,2,1]</code></p>

<p>- 开始 <code>l=0, r=11, left_max=0, right_max=0, water=0</code></p>
<p>- 右边较高（1 vs 1 走 else），更新 <code>right_max=1</code>，<code>r=10</code></p>
<p>- 当左边较小（0 &lt; 2）：左侧可结算，<code>left_max=0</code>，<code>l=1</code></p>
<p>- <code>height[2]=0</code> 时，<code>left_max=1</code>，水 += 1-0 = 1</p>
<p>- ... 最终累计 <code>water=6</code></p>

<p>Why it works: the side with the smaller boundary max is the limiting factor, so we can safely finalize water there without knowing the exact interior structure.</p>

<hr/>

<h2>⏱️ 复杂度 / Complexity</h2>

<p>- Time: <strong>O(n)</strong> (each pointer moves at most n steps)</p>
<p>- Space: <strong>O(1)</strong></p>

<hr/>

<h2>举一反三 / Transfer within this pattern block</h2>

<p>- #11 Container With Most Water：移动“短板”来寻找更可能变大的面积</p>
<p>- #15 3Sum：固定一个数 + 双指针夹逼</p>
<p>- #125 Valid Palindrome：两端检查并向内收缩</p>

<p>共同点：</p>
<p>- <strong>每一步移动都基于一个可证明的单调性/界限</strong>，避免 O(n^2)</p>

<hr/>

<h2>📚 References</h2>
<p>- LeetCode editorial: https://leetcode.com/problems/trapping-rain-water/editorial/</p>
<p>- NeetCode explanation (video): https://www.youtube.com/watch?v=ZI2z5pq0TqA</p>
<p>- GeeksforGeeks (two-pointer approach): https://www.geeksforgeeks.org/trapping-rain-water/</p>

<h2>🧒 ELI5</h2>

<p>想象你在一排积木之间倒水。某一格能装多少水，只取决于它左边最高的积木和右边最高的积木里较矮的那个。双指针就是从两边往中间走，随时记住“目前看到的最高积木”，然后一格一格把水算出来。</p>

<p>Imagine filling water between blocks. A spot’s water level is capped by the shorter of the tallest block on its left and right. Two pointers walk inward, tracking those tallest blocks and adding water as you go.</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 14 / Soft Skills Day 14</h2>
<h2>Tell me about a time you drove a large cross-team initiative</h2>

<p>&gt; <strong>级别 / Level:</strong> Staff · <strong>主题 / Category:</strong> Leadership · <strong>Read time:</strong> 2 min</p>

<hr/>

<h2>为什么重要 / Why this matters</h2>

<p><strong>中文：</strong></p>
<p>跨团队项目（例如：统一身份认证、支付迁移、数据平台升级、全站性能治理）最大的风险往往不是技术，而是<strong>对齐、节奏、依赖、沟通成本</strong>。Staff 级别面试官想听到的是：你如何在没有“直接汇报关系”的情况下，把很多人带到同一条船上。</p>

<p><strong>English:</strong></p>
<p>For cross-team initiatives, the hardest part is rarely the technical design—it’s alignment, dependencies, cadence, and communication overhead. Interviewers want evidence you can lead without formal authority.</p>

<hr/>

<h2>⭐ STAR 结构（建议 90 秒回答）/ STAR structure (aim for 90 seconds)</h2>

<h3>S — Situation（背景）</h3>
<p>- 中文：项目是什么？影响范围多大？涉及哪些团队？</p>
<p>- English: What was the initiative? Scope? Which teams?</p>

<h3>T — Task（你的职责）</h3>
<p>- 中文：你具体负责什么？目标/成功标准是什么（SLO、迁移比例、成本、上线日期）？</p>
<p>- English: What did you own? What were the success metrics?</p>

<h3>A — Action（你做了什么）</h3>
<p>用“可复制的方法论”讲：</p>
<p>1) <strong>定义北极星指标 / Define a north-star metric</strong>：例如 p95 latency、error budget、migration completion。</p>
<p>2) <strong>把问题拆成工作流 / Break into a plan</strong>：里程碑、风险清单、依赖图、RACI（谁负责/批准/咨询/知会）。</p>
<p>3) <strong>建立节奏 / Create operating cadence</strong>：每周 cross-team sync、异步周报、决策记录（ADR）、升级通道。</p>
<p>4) <strong>提前拆雷 / De-risk early</strong>：先做 POC / pilot、灰度、回滚预案、观测（dashboards + alerts）。</p>
<p>5) <strong>对齐激励 / Align incentives</strong>：明确“对他们有什么好处”（减少 oncall、降低成本、提高转化）。</p>

<p>English (same content):</p>
<p>1) Define a measurable north-star metric.</p>
<p>2) Turn ambiguity into a concrete plan (milestones, dependency map, RACI).</p>
<p>3) Establish cadence (syncs, async updates, decision logs).</p>
<p>4) De-risk early (pilot, gradual rollout, rollback plan, observability).</p>
<p>5) Align incentives so partner teams want to participate.</p>

<h3>R — Result（结果）</h3>
<p>- 中文：用数字结尾：提前/按期上线、迁移比例、故障率下降、成本节省、开发效率提升。</p>
<p>- English: Close with numbers: completion %, latency improvement, incidents reduced, cost savings.</p>

<hr/>

<h2>❌ Bad vs ✅ Good（面试官一听就懂）/ Bad vs Good</h2>

<p><strong>❌ Bad（空泛）</strong></p>
<p>- “我组织了很多会议，大家最后达成一致，然后上线了。”</p>

<p><strong>✅ Good（可验证）</strong></p>
<p>- “我先把目标写成 p95 从 800ms 降到 400ms，并把依赖拆成 3 条迁移路径；每周一次跨团队同步 + 每两天异步进度；关键风险是 X 团队的 schema 变更，于是先做了两周 pilot 和双写；最终 6 周内迁移 92%，相关 oncall 事故从每周 5 起降到 1 起。”</p>

<hr/>

<h2>Senior/Staff 加分点 / Senior/Staff-level tips</h2>

<p>- <strong>把决策写下来</strong>：用 ADR 记录 tradeoffs，不靠“口口相传”。</p>
<p>- <strong>沟通要“分层”</strong>：IC 关注任务与风险，Manager 关注里程碑与资源，Exec 关注指标与 ROI。</p>
<p>- <strong>处理冲突的方式</strong>：先找共同目标，再用数据/实验说话；必要时明确升级路径。</p>
<p>- <strong>让系统自动运行</strong>：好的机制（dashboard、SLO、自动化迁移工具）比个人英雄更可靠。</p>

<hr/>

<h2>Key Takeaways</h2>
<p>- 中文：跨团队项目 = 目标清晰 + 依赖可视化 + 节奏稳定 + 风险前置 + 激励对齐。</p>
<p>- English: Cross-team success = clear metrics + dependency visibility + steady cadence + early de-risking + aligned incentives.</p>

<hr/>

<h2>📚 References</h2>
<p>- Google SRE Book — Service Level Objectives: https://sre.google/sre-book/service-level-objectives/</p>
<p>- RACI matrix overview (Atlassian): https://www.atlassian.com/team-playbook/plays/roles-and-responsibilities</p>
<p>- Amazon Working Backwards (concept): https://www.aboutamazon.com/news/company-news/working-backwards-how-amazon-starts-with-the-customer</p>

<h2>🧒 ELI5</h2>

<p>中文：你要做一件很多同学一起完成的大作业。你得先说清楚“最终要拿多少分”（指标），再把任务分好、规定每周检查一次进度、提前发现最难的部分先做小实验，最后大家才会真的按同一个计划走。</p>

<p>English: It’s like a big group project. First define what “success” means, split work and owners, check progress regularly, test the risky parts early, and keep everyone moving together.</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 14 / Frontend Day 14</h2>
<h2>React Custom Hooks — Extract &amp; Reuse Logic</h2>

<p>&gt; <strong>阶段 / Phase:</strong> Growth · <strong>Read time:</strong> 2 min</p>

<hr/>

<h2>🧩 真实场景 / Real scenario</h2>

<p>中文：你在做一个 dashboard，需要在多个页面复用“拉取数据 + loading/error + 取消请求 + 刷新”的逻辑。你不想每个组件都写一遍 <code>useEffect</code> + <code>AbortController</code> + 一堆状态。</p>

<p>English: You’re building a dashboard and need reusable “fetch + loading/error + cancellation + refresh” logic across multiple pages.</p>

<hr/>

<h2>✅ 生产可用的 Custom Hook 例子 / Production-ready custom hook</h2>

<pre><code>
import { useCallback, useEffect, useRef, useState } from &quot;react&quot;;

type AsyncState&lt;T&gt; = {
  data: T | null;
  error: string | null;
  loading: boolean;
};

// Code comments in English
export function useJsonFetch&lt;T&gt;(url: string, deps: unknown[] = []) {
  const [state, setState] = useState&lt;AsyncState&lt;T&gt;&gt;({
    data: null,
    error: null,
    loading: true,
  });

  // Keep AbortController in a ref so we can cancel in-flight requests.
  const abortRef = useRef&lt;AbortController | null&gt;(null);

  const run = useCallback(async () =&gt; {
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    setState((s) =&gt; ({ ...s, loading: true, error: null }));

    try {
      const res = await fetch(url, { signal: controller.signal });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const json = (await res.json()) as T;
      setState({ data: json, error: null, loading: false });
    } catch (e) {
      // Abort is not a real “error” the user should see
      if ((e as any)?.name === &quot;AbortError&quot;) return;
      setState({ data: null, error: (e as Error).message, loading: false });
    }
  }, [url, ...deps]);

  useEffect(() =&gt; {
    void run();
    return () =&gt; abortRef.current?.abort();
  }, [run]);

  return { ...state, refresh: run };
}
</code></pre>

<p><strong>怎么用 / How to use:</strong></p>
<pre><code>
type User = { id: string; name: string };

function UsersPanel() {
  const { data, loading, error, refresh } = useJsonFetch&lt;User[]&gt;(&quot;/api/users&quot;);

  if (loading) return &lt;div&gt;Loading…&lt;/div&gt;;
  if (error) return &lt;button onClick={refresh}&gt;Retry: {error}&lt;/button&gt;;
  return (
    &lt;div&gt;
      &lt;button onClick={refresh}&gt;Refresh&lt;/button&gt;
      &lt;ul&gt;{data?.map((u) =&gt; &lt;li key={u.id}&gt;{u.name}&lt;/li&gt;)}&lt;/ul&gt;
    &lt;/div&gt;
  );
}
</code></pre>

<hr/>

<h2>🧠 “猜猜这段代码输出什么？”/ Output quiz</h2>

<pre><code>
function Demo() {
  const [n, setN] = useState(0);

  const inc = useCallback(() =&gt; setN(n + 1), []);

  return &lt;button onClick={inc}&gt;{n}&lt;/button&gt;;
}
</code></pre>

<p>A) 每次点击都会正常 +1 / Increments correctly each click</p>
<p>B) 永远显示 0 / Always shows 0</p>
<p>C) 只会变成 1，然后卡住 / Becomes 1 then stuck</p>
<p>D) 组件会崩溃 / Component crashes</p>

<p><strong>正确答案 / Correct:</strong> C</p>

<p>中文解释：<code>useCallback(..., [])</code> 把 <code>inc</code> 固定住了，但它闭包里捕获的 <code>n</code> 永远是初始值 0，所以每次都是 <code>setN(0 + 1)</code>。</p>

<p>English: The callback is memoized with an empty deps array, so it captures <code>n=0</code> forever. Each click sets <code>n</code> to <code>1</code> again.</p>

<p>✅ 修复方式：用 functional update</p>
<pre><code>
const inc = useCallback(() =&gt; setN((x) =&gt; x + 1), []);
</code></pre>

<hr/>

<h2>❌ 常见错误 vs ✅ 正确方式 / Common mistake vs correct approach</h2>

<p><strong>❌ 错误：custom hook 里依赖不稳定，导致无限刷新 / unstable deps causing loops</strong></p>
<pre><code>
useEffect(() =&gt; {
  fetch(url).then(...)
}, [options]) // options is a new object every render
</code></pre>

<p><strong>✅ 正确：让依赖稳定（useMemo / useCallback / 把对象提升到外层）</strong></p>
<pre><code>
const options = useMemo(() =&gt; ({ headers: { &quot;x&quot;: &quot;1&quot; } }), []);
useEffect(() =&gt; {
  fetch(url, options).then(...)
}, [url, options])
</code></pre>

<hr/>

<h2>什么时候用 / When to use</h2>
<p>- ✅ 当你要复用“状态 + 副作用 + 取消/清理 + 触发刷新”的组合逻辑</p>
<p>- ✅ 当你希望组件变得更像“UI 视图层”，逻辑下沉到 hook</p>

<h2>什么时候不要用 / When NOT to use</h2>
<p>- ❌ 只是复用一个纯函数：直接写 utility function 就好</p>
<p>- ❌ hook 内部逻辑强耦合某个页面的 UI 结构：可能该用组件/组合而不是 hook</p>
<p>- ❌ 你还没搞清楚边界：先写在组件里，稳定后再抽取（避免过早抽象）</p>

<hr/>

<h2>📚 References</h2>
<p>- React Docs — Reusing Logic with Custom Hooks: https://react.dev/learn/reusing-logic-with-custom-hooks</p>
<p>- React Docs — useCallback: https://react.dev/reference/react/useCallback</p>
<p>- MDN — AbortController (cancel fetch): https://developer.mozilla.org/en-US/docs/Web/API/AbortController</p>

<h2>🧒 ELI5</h2>

<p>中文：Custom Hook 就像把“做菜步骤”写成一个固定食谱。以后每次要做同样的菜（同样的逻辑），你就直接用这份食谱，而不是每次都从头想一遍。</p>

<p>English: A custom hook is a reusable recipe for state + side effects. You call the recipe in different components instead of rewriting the steps each time.</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 14</h2>
<h2>LoRA &amp; QLoRA — Efficient Fine-Tuning</h2>

<p>&gt; <strong>Mode:</strong> CONCEPT · <strong>Category:</strong> Training · <strong>Read time:</strong> 2 min</p>

<hr/>

<h2>🧠 直觉解释 / Intuition</h2>

<p><strong>中文：</strong></p>
<p>全量微调（full fine-tuning）像是把整本教科书都重写一遍：效果可能好，但成本极高（显存/时间/存储），而且不小心会“改坏”模型的通用能力。</p>

<p><strong>LoRA</strong>（Low-Rank Adaptation）更像是：</p>
<p>- <strong>原模型权重冻结不动</strong>（不重写教科书）</p>
<p>- 只加一层“薄薄的可训练适配器”来改变模型行为（像贴便签/补丁）</p>

<p><strong>QLoRA</strong> 则是在 LoRA 基础上再做一步：</p>
<p>- <strong>把基座模型量化</strong>（比如 4-bit）来极大降低显存占用</p>
<p>- 仍然用 LoRA 训练小适配器，从而让你在更小的 GPU 上也能做高质量微调</p>

<p><strong>English:</strong></p>
<p>Full fine-tuning is rewriting the whole book: powerful but expensive and risky. LoRA freezes the base model and learns small low-rank “adapter” matrices (patches). QLoRA further quantizes the base model (e.g., 4-bit) to cut VRAM dramatically while still training LoRA adapters.</p>

<hr/>

<h2>⚙️ 它是怎么工作的 / How it works</h2>

<p><strong>中文：</strong></p>
<p>在 Transformer 里，大量参数集中在注意力/FFN 的线性层（比如 <code>W</code>）。LoRA 把权重更新 ΔW 表示为两个小矩阵的乘积：</p>

<p>- <strong>ΔW = A · B</strong>，其中 A、B 的秩（rank）很小（r ≪ d）</p>
<p>- 训练时只更新 A、B（参数量从 O(d²) 变成 O(2·d·r)）</p>
<p>- 推理时可以把 ΔW 合并回 W（不增加太多推理开销）</p>

<p><strong>QLoRA：</strong></p>
<p>- 基座权重用 4-bit NF4 等量化方式存储</p>
<p>- 训练时用更高精度（比如 bfloat16）在计算路径中做补偿（常见做法是 double quantization 等技巧）</p>

<p><strong>English:</strong></p>
<p>LoRA factorizes the weight update as ΔW = A·B with small rank r. You train only A and B (far fewer parameters). QLoRA quantizes the base weights (often 4-bit) and trains LoRA adapters on top, using careful compute dtypes/quantization tricks to keep quality.</p>

<hr/>

<h2>✅ 什么时候用 / When to use</h2>

<p>- <strong>中文：</strong></p>
<p>- 你想针对“特定任务/风格/领域语料”提升效果，但预算有限</p>
<p>- 你希望可控地“加能力”，并且随时能切换不同 adapter（一个基座多个 LoRA）</p>
<p>- 你需要在单卡/小显存环境训练</p>

<p>- <strong>English:</strong></p>
<p>- You need task/domain/style adaptation on a budget</p>
<p>- You want modular adapters you can swap (one base, many LoRAs)</p>
<p>- You’re constrained by VRAM (single GPU / smaller GPUs)</p>

<hr/>

<h2>🧪 可运行示例（≤15 行）/ Runnable snippet (≤15 lines)</h2>

<p>&gt; 下面示例展示“加载 LoRA adapter”的最小思路（训练通常更长、代码更多）。</p>

<pre><code>
# pip install -U transformers peft torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base = &quot;gpt2&quot;  # demo base model
lora_path = &quot;./my_lora_adapter&quot;  # your saved LoRA adapter folder

tok = AutoTokenizer.from_pretrained(base)
model = AutoModelForCausalLM.from_pretrained(base)
model = PeftModel.from_pretrained(model, lora_path)

prompt = &quot;Write a short product update:&quot;
print(tok.decode(model.generate(**tok(prompt, return_tensors=&quot;pt&quot;), max_new_tokens=40)[0]))
</code></pre>

<hr/>

<h2>📚 References</h2>
<p>- LoRA paper (arXiv): https://arxiv.org/abs/2106.09685</p>
<p>- QLoRA paper (arXiv): https://arxiv.org/abs/2305.14314</p>
<p>- Hugging Face PEFT docs: https://huggingface.co/docs/peft/index</p>

<h2>🧒 ELI5</h2>

<p><strong>中文：</strong></p>
<p>LoRA 就像给机器人加一副“可拆卸的小眼镜”，不改它原来的大脑，只训练这副眼镜让它更擅长某件事。QLoRA 则是把机器人的大脑“压缩存起来”，省空间省钱，但仍能换不同眼镜来学习新技能。</p>

<p><strong>English:</strong></p>
<p>LoRA is a small attachable add-on that changes behavior without rewriting the whole brain. QLoRA compresses the brain to save memory, then still learns those small add-ons.</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-29</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-29</guid>
      <pubDate>Sun, 29 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>Week Review</h1>
<h2>📅 **Week in Review — Week 3 (10 min read)**</h2>
<p>📊 Day 13/150 · NeetCode: 13/150 · SysDesign: 12/40 · Behavioral: 12/40 · Frontend: 12/50 · AI: 5/30</p>
<p>🔥 13-day streak!</p>

<hr/>

<h2>🗓️ 本周回顾 / This Week&#x27;s Journey</h2>

<p>本周是第 3 周（第 8–13 天），阶段从 Foundation 迈入 Growth，难度明显上台阶。</p>

<p><em>Week 3 covered Days 8–13, bridging Foundation → Growth phase with a noticeable difficulty step-up.</em></p>

<p>| 日期 / Date | 亮点 / Highlight |</p>
<p>|---|---|</p>
<p>| <strong>Mon 3/23 · Day 8</strong> | 数据库索引 B-Tree + N+1 查询；CSS Animations &amp; Transitions；模糊需求处理 |</p>
<p>| <strong>Tue 3/24 · Day 9</strong> | 数据库复制 &amp; 分片（Replication / Sharding）；算法切换双指针模式（#125 Valid Palindrome）；AI 大新闻：Agentic AI、百万 Token 上下文 |</p>
<p>| <strong>Wed 3/25 · Day 10</strong> | 📝 复习日：回顾 Day 6-9 全部内容，巩固薄弱点 |</p>
<p>| <strong>Thu 3/26 · Day 11</strong> | 一致性哈希（Consistent Hashing）+ 虚拟节点；双指针 #167 Two Sum II；React useEffect；主动发现问题的 behavioral |</p>
<p>| <strong>Fri 3/27 · Day 12</strong> | CAP 定理 &amp; 最终一致性（Growth Phase 正式开始）；3Sum（双指针 3/5）；React useRef；RLHF 深度讲解；优先级排序 behavioral |</p>
<p>| <strong>Sat 3/28 · Day 13</strong> | 🔬 深挖：消息队列 &amp; 事件驱动架构（15 min read，含 Kafka 实战代码）|</p>

<hr/>

<h2>🧠 系统设计要点 / System Design: Key Takeaways</h2>

<p>本周系统设计从&quot;单机&quot;向&quot;分布式&quot;跃进，三个核心概念构成一套完整的分布式基础：</p>

<p><em>This week&#x27;s system design made the leap from single-machine to distributed, forming a complete distributed foundation:</em></p>

<p><strong>1. 数据库索引 → 复制 → 分片 / Indexing → Replication → Sharding</strong></p>
<p>- 索引解决<strong>单机查询速度</strong>（B-Tree O(log N) vs 全表扫 O(N)）</p>
<p>- 复制解决<strong>读扩展 + 高可用</strong>（多副本分担读流量）</p>
<p>- 分片解决<strong>写扩展 + 存储规模</strong>（每个 shard 只负责一部分数据）</p>
<p>- 三者是递进关系：先索引，再复制，实在撑不住再分片</p>

<p><strong>2. 一致性哈希 / Consistent Hashing</strong></p>
<p>- 普通哈希 <code>key % N</code>：增减节点触发 90% 数据重分配 ❌</p>
<p>- 一致性哈希：增减节点只影响 ~1/N 数据 ✅</p>
<p>- 核心武器：<strong>虚拟节点</strong>（每个物理节点 150-200 个虚拟位置），解决负载不均</p>
<p>- 真实应用：DynamoDB、Cassandra、Memcached</p>

<p><strong>3. CAP 定理 / CAP Theorem</strong></p>
<p>- 分布式系统三选二：Consistency（一致）、Availability（可用）、Partition Tolerance（分区容错）</p>
<p>- 网络分区不可避免，实际是 CP vs AP 的取舍</p>
<p>- <strong>CP</strong>：金融、支付、计数器（HBase、ZooKeeper）</p>
<p>- <strong>AP + 最终一致性</strong>：社交 feed、购物车、点赞（Cassandra、DynamoDB）</p>
<p>- 面试技巧：先问业务对一致性的要求，再决定选型</p>

<p><strong>本周连接点 / The thread connecting them all:</strong></p>
<p>索引 → 复制 → 分片 → 一致性哈希 → CAP，是任何大规模数据库系统设计的完整脉络。消息队列（Day 13）则是在此基础上，解耦服务间的依赖，进一步提升系统韧性。</p>

<hr/>

<h2>💻 算法模式 / Algorithms: Patterns Mastered</h2>

<p>本周完成双指针模式 5 题中的 3 题（进度 3/5）：</p>

<p><em>Completed 3 of 5 Two Pointers problems this week:</em></p>

<p>| 题目 | 核心变化 | 关键洞察 |</p>
<p>|---|---|---|</p>
<p>| <strong>#125 Valid Palindrome</strong> | 跳过非字母数字 | 双指针&quot;对比&quot;而非求和；Space O(1) 优于 O(n) |</p>
<p>| <strong>#167 Two Sum II</strong> | 有序数组求和 | 排序后才能确定性地收缩：sum &lt; target → left++，sum &gt; target → right-- |</p>
<p>| <strong>#15 3Sum</strong> | 三数之和为零 | 外层固定一个数，内层双指针；排序后跳过重复解 |</p>

<p><strong>模式识别信号 / When to reach for Two Pointers:</strong></p>
<p>&gt; ✅ 有序数组 · ✅ 找配对/三元组 · ✅ 回文检测 · ✅ 原地操作 · ✅ 要求 O(1) 空间</p>

<p><strong>下两题预告 / Coming next:</strong></p>
<p>- <code>#11 Container With Most Water</code> — 移动较短边指针，最大化面积</p>
<p>- <code>#42 Trapping Rain Water</code> — 最复杂变体，维护左右最大高度</p>

<hr/>

<h2>🗣️ 软技能练习 / Soft Skills: What to Practice</h2>

<p>本周覆盖了 5 个核心行为面试主题（Day 8–12），全部 Senior/Staff 级别：</p>

<p><em>5 behavioral interview topics covered, all targeting Senior/Staff level:</em></p>

<p>| 主题 | STAR 核心动作 | 需要强化的点 |</p>
<p>|---|---|---|</p>
<p>| <strong>模糊需求</strong> | 写假设文档、区分可逆/不可逆决策 | 结果要量化（&quot;避免了 1.5 周返工&quot;） |</p>
<p>| <strong>向上推回</strong> | 先跑负载测试量化风险，再提替代方案 | 带数据 + 替代方案是关键；不要空手说&quot;不行&quot; |</p>
<p>| <strong>主动发现问题</strong> | 复现 bug → 量化风险 → 独立推进修复 | 系统性主动（建立监控体系）比偶发性主动更有说服力 |</p>
<p>| <strong>优先级排序</strong> | Impact × Urgency 矩阵；把技术连接到业务目标 | 明确说出&quot;选 A 意味着 B 推迟到 X，风险是 Y&quot; |</p>
<p>| <strong>处理交付坏消息</strong> | 提前同步，给选项，聚焦解决方案 | 不要等到最后一刻才上报风险 |</p>

<p><strong>综合练习建议 / Practice focus:</strong></p>
<p>用 STAR 框架为每个主题准备 1-2 个真实故事，重点量化结果。&quot;主动发现问题&quot;和&quot;优先级排序&quot;是最容易被考到但准备不足的两个主题。</p>

<hr/>

<h2>🎨 前端巩固 / Frontend: Concepts to Lock In</h2>

<p>本周 React Hooks 三件套全部覆盖：</p>

<p><em>All three core React Hooks covered this week:</em></p>

<p><strong>useState → useEffect → useRef — 递进关系：</strong></p>

<pre><code>
useState: 需要 UI 更新时存状态（触发重渲染）
useEffect: 需要同步副作用（数据获取、订阅、DOM 操作）
useRef: 需要持久值但不想触发重渲染（DOM 引用、timer ID）
</code></pre>

<p><strong>高频考点 / High-frequency interview traps:</strong></p>

<p>1. <code>useState</code> 批量更新：<code>setCount(count+1)</code> 三次 → count 只 +1；用 <code>prev =&gt; prev + 1</code> 才能累加 ✅</p>
<p>2. <code>useEffect</code> 清理：return 里关闭 WebSocket/清除 timer，否则内存泄漏 ✅</p>
<p>3. CSS <code>transition</code> vs <code>animation</code>：transition 需要触发（hover/JS），animation 自动运行 ✅</p>
<p>4. 动画性能：优先 <code>transform</code> + <code>opacity</code>（GPU），避免 <code>width/height/margin</code>（触发 reflow）✅</p>

<p><strong>自查问题 / Quick self-check:</strong></p>
<p>- <code>useEffect</code> 的依赖数组为空 <code>[]</code> 和不传有什么区别？</p>
<p>- <code>useRef</code> 的值改变了，组件会重渲染吗？</p>
<p>- CSS <code>display: none</code> 可以加 transition 吗？</p>

<hr/>

<h2>🤖 AI 知识点 / AI: What Stuck</h2>

<p>本周 AI 内容从&quot;行业动态&quot;到&quot;技术机制&quot;都有覆盖：</p>

<p><em>AI content ranged from industry trends to technical mechanisms:</em></p>

<p><strong>最重要的技术概念 / Most important technical concept:</strong></p>

<p><strong>RLHF — ChatGPT 是怎么学会&quot;有用&quot;的：</strong></p>
<p>1. <strong>SFT（监督微调）</strong>：在人类示范答案上微调基础模型，学格式和风格</p>
<p>2. <strong>奖励模型（RM）训练</strong>：收集人类对多个回答的排序偏好，训练打分器</p>
<p>3. <strong>PPO 强化学习</strong>：模型生成 → RM 打分 → 更新策略（同时用 KL 散度约束偏移）</p>

<p><strong>正在替代 PPO 的新方法</strong>：DPO（Direct Preference Optimization）— 更简单，无需显式奖励模型。</p>

<p><strong>本周行业信号 / Industry signals:</strong></p>
<p>- Agentic AI 从&quot;生成文本&quot;→&quot;自主完成任务&quot;，GPT-5.4 已实现原生电脑操作（GUI）</p>
<p>- 76% 企业未准备好支持 AI Agent → 未来 1-3 年最值钱的技能：<strong>设计与 AI Agent 协作的系统架构</strong>（清晰 API、幂等操作、可审计工作流）</p>
<p>- 上下文窗口突破 100 万 token，某些场景 long-context 比构建向量数据库更简单准确</p>

<hr/>

<h2>⚠️ 需要复习的内容 / What to Review</h2>

<p><strong>优先级排序（从弱到强）：</strong></p>

<p>| 🔴 最需要复习 | 具体建议 |</p>
<p>|---|---|</p>
<p>| <strong>CAP 定理 + PACELC</strong> | 能否在 3 句话内解释 CP vs AP 的区别，并各举一个真实系统？DynamoDB 如何在同一系统里提供可调一致性？ |</p>
<p>| <strong>一致性哈希虚拟节点</strong> | 为什么虚拟节点数少了会有热点？能在白板上画出完整的哈希环 + 节点故障时的数据迁移吗？ |</p>
<p>| <strong>3Sum 去重逻辑</strong> | 能手写完整解法吗？排序后如何跳过重复的 <code>nums[i]</code>、<code>nums[left]</code>、<code>nums[right]</code>？ |</p>

<p>| 🟡 值得巩固 | 具体建议 |</p>
<p>|---|---|</p>
<p>| <strong>消息队列：Kafka vs RabbitMQ</strong> | 什么时候选 Kafka（高吞吐、回放），什么时候选 RabbitMQ（复杂路由、短生命周期消息）？ |</p>
<p>| <strong>useEffect 依赖数组</strong> | 写 3 个不同 useEffect 示例，分别对应&quot;mount once&quot;、&quot;每次 render&quot;、&quot;deps 变化时&quot; |</p>
<p>| <strong>STAR 故事量化</strong> | 为&quot;主动发现问题&quot;和&quot;优先级排序&quot;各准备一个有具体数字的故事 |</p>

<hr/>

<h2>🏆 本周亮点 / Win of the Week</h2>

<p><strong>成功跨越 Foundation → Growth 阶段！</strong></p>

<p>第 11 天开始 Growth Phase，内容难度明显上升（一致性哈希、CAP、3Sum、RLHF），但节奏保持稳定。最值得庆祝的是：Saturday Deep Dive（消息队列 &amp; 事件驱动架构）内容深度和代码质量都达到了 senior 面试的实战水准——从 Kafka producer/consumer 配置，到幂等性设计，到死信队列，一篇顶得上市面上很多付费课程的一章。</p>

<p>🔥 13 天，从未间断。系统设计的脉络正在形成：网络 → HTTP → 负载均衡 → 缓存 → 数据库（索引→复制→分片→一致性哈希→CAP）→ 消息队列。这不是孤立的知识点，这是在构建一张完整的系统设计地图。</p>

<p><em>Bridged Foundation → Growth phase without breaking stride. The Saturday Deep Dive on message queues hit senior-interview depth. More importantly, a coherent system design map is forming — 13 days, zero breaks.</em></p>

<hr/>

<h2>🎯 下周预告 / Next Week Preview</h2>

<p>基于当前进度（SysDesign: 12/40 · Algorithms: 13/150 · Behavioral: 12/40 · Frontend: 12/50 · AI: 5/30）：</p>

<p>| 模块 | 下周内容 |</p>
<p>|---|---|</p>
<p>| 🏗️ <strong>系统设计</strong> | #13 CDN &amp; 边缘计算 · #14 速率限制（Rate Limiting）· #15 SQL vs NoSQL 深度对比 |</p>
<p>| 💻 <strong>算法</strong> | 双指针收官：#11 Container With Most Water · #42 Trapping Rain Water · 开始滑动窗口模式 |</p>
<p>| 🗣️ <strong>软技能</strong> | 持续 Growth Phase：从影响力、跨团队协作到最终 behavioral |</p>
<p>| 🎨 <strong>前端</strong> | React Context · 自定义 Hook · 性能优化（useMemo/useCallback 实战） |</p>
<p>| 🤖 <strong>AI</strong> | AI News Roundup · 更多 AI 概念深挖 |</p>

<p><strong>本周学到的，下周就会用到</strong>：CAP 定理 → 在设计 CDN/速率限制时你需要选择 CP 还是 AP；消息队列 → 在任何通知系统设计中都会出现；双指针 → Container With Most Water 是下周双指针收官题，比 3Sum 还更直接地考查&quot;移动哪个指针&quot;的决策逻辑。</p>

<p>加油，下周见！💪</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-28</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-28</guid>
      <pubDate>Sat, 28 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>Saturday Deep Dive</h2>

<p>Today is Saturday — content delivered in the deep dive issue.</p>

<p>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>Saturday Deep Dive</h2>

<p>Today is Saturday — content delivered in the deep dive issue.</p>

<p>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>Saturday Deep Dive</h2>

<p>Today is Saturday — content delivered in the deep dive issue.</p>

<p>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>Saturday Deep Dive</h2>

<p>Today is Saturday — content delivered in the deep dive issue.</p>

<p>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>

<hr/>
<h1>🤖 AI</h1>
<h2>Saturday Deep Dive</h2>

<p>Today is Saturday — content delivered in the deep dive issue.</p>

<p>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>

<hr/>
<h1>Deepdive</h1>
<h2>🔬 Saturday Deep Dive: Message Queues &amp; Event-Driven Architecture (15 min read)</h2>

<p>📊 Day 13/150 · NeetCode: 12/150 · SysDesign: 12/40 · Behavioral: 12/40 · Frontend: 12/50 · AI: 5/30</p>
<p>🔥 Keep the streak alive!</p>

<hr/>

<h2>Overview / 概述</h2>

<p><strong>中文：</strong></p>
<p>消息队列是现代分布式系统的血管。当两个服务需要通信，但你不希望它们紧密耦合在一起时，消息队列就登场了。从 WhatsApp 的消息投递，到 Uber 的行程分配，再到你下单后收到的确认邮件——几乎每个大型系统背后都有消息队列在默默支撑。</p>

<p><strong>English:</strong></p>
<p>Message queues are the circulatory system of modern distributed architecture. They allow services to communicate <strong>asynchronously</strong> — decoupling producers from consumers so each can scale, fail, and recover independently. If you&#x27;ve ever placed an Amazon order and received a confirmation email seconds later without the checkout page hanging, you&#x27;ve experienced event-driven architecture in action.</p>

<p><strong>Why it matters in interviews:</strong> This topic appears in ~80% of senior-level system design interviews. &quot;Design Twitter&quot;, &quot;Design Uber&quot;, &quot;Design a notification system&quot; — all roads lead to message queues.</p>

<hr/>

<h2>Part 1: Theory / 理论基础 (5 min)</h2>

<h3>核心问题：为什么需要消息队列？/ The Core Problem</h3>

<p><strong>中文：</strong></p>
<p>想象一个在线零售系统。用户下单时，系统需要：</p>
<p>1. 扣减库存</p>
<p>2. 向支付服务收款</p>
<p>3. 通知仓库备货</p>
<p>4. 发送确认邮件</p>
<p>5. 更新用户积分</p>

<p>如果全部同步完成，任何一步失败都会导致整个请求失败。支付服务宕机了？用户收到 500 错误。邮件服务慢了？用户等 10 秒。这就是<strong>同步耦合</strong>的代价。</p>

<p><strong>English:</strong></p>
<p>In a synchronous world, the order service would call inventory → payment → warehouse → email → loyalty service, all in sequence. This creates:</p>
<p>- <strong>Temporal coupling:</strong> All services must be up at the same time</p>
<p>- <strong>Performance coupling:</strong> The slowest service determines the total latency</p>
<p>- <strong>Failure coupling:</strong> One failure cascades through the whole chain</p>

<p>Message queues break all three couplings.</p>

<hr/>

<h3>核心概念 / Core Concepts</h3>

<p><strong>中文 → English glossary:</strong></p>

<p>| 概念 | 英文 | 说明 |</p>
<p>|------|------|------|</p>
<p>| 生产者 | Producer | 发消息的服务 |</p>
<p>| 消费者 | Consumer | 收消息的服务 |</p>
<p>| 消息 | Message / Event | 传递的数据单元 |</p>
<p>| 队列 | Queue | 点对点：一条消息只被一个消费者消费 |</p>
<p>| 主题 | Topic | 发布/订阅：一条消息被多个消费者消费 |</p>
<p>| 消费者组 | Consumer Group | 多个消费者实例共享消费同一个 topic |</p>
<p>| 偏移量 | Offset (Kafka) | 消息在分区中的位置，消费者自己维护 |</p>
<p>| 确认 | Acknowledgment (ACK) | 消费者告诉队列&quot;我已成功处理&quot; |</p>
<p>| 死信队列 | Dead Letter Queue (DLQ) | 处理失败的消息的&quot;最终归宿&quot; |</p>

<hr/>

<h3>两种核心模型 / Two Models</h3>

<p><strong>Model 1: Point-to-Point (Queue)</strong></p>

<pre><code>
Producer → [Queue] → Consumer A  (Consumer B never sees this message)
</code></pre>

<p>- 每条消息只被消费一次</p>
<p>- 适合：任务分发、工作队列</p>
<p>- 代表：AWS SQS, RabbitMQ (default)</p>

<p><strong>Model 2: Publish-Subscribe (Topic)</strong></p>

<pre><code>
Producer → [Topic] → Consumer A
                   → Consumer B  
                   → Consumer C  (all three get the same message)
</code></pre>

<p>- 每条消息被所有订阅者消费</p>
<p>- 适合：事件通知、数据管道</p>
<p>- 代表：Apache Kafka, AWS SNS, Google Pub/Sub</p>

<hr/>

<h3>主流技术对比 / Technology Comparison</h3>

<p><strong>中文：</strong> 面试中最常被问到的是 Kafka vs RabbitMQ。记住核心区别：</p>

<p>| 特性 | Apache Kafka | RabbitMQ / AWS SQS |</p>
<p>|------|-------------|-------------------|</p>
<p>| 模型 | Log-based (追加写日志) | Queue-based (消费后删除) |</p>
<p>| 消息保留 | 时间/大小限制（可重放） | 消费后删除（默认） |</p>
<p>| 吞吐量 | 极高 (百万级/秒) | 高 (万-十万级/秒) |</p>
<p>| 顺序保证 | 分区内有序 | 队列内有序 |</p>
<p>| 适合场景 | 流处理、日志、事件溯源 | 任务队列、RPC、复杂路由 |</p>
<p>| 消费模型 | Pull (消费者拉取) | Push (队列推送) |</p>

<p><strong>English:</strong></p>
<p>The key insight: <strong>Kafka is a distributed log, not a traditional queue.</strong> Messages stay on disk until a retention policy removes them. Consumers maintain their own offsets, enabling:</p>
<p>- <strong>Replay:</strong> Reprocess all events from the beginning</p>
<p>- <strong>Multiple independent consumers:</strong> Each consumer group gets full history</p>
<p>- <strong>Event sourcing:</strong> The log IS the source of truth</p>

<hr/>

<h2>Part 2: Step-by-Step Implementation / 一步一步实现 (8 min)</h2>

<h3>场景：设计一个通知系统 / Scenario: Notification System Design</h3>

<p><strong>中文：</strong> 我们设计一个类似于电商平台的通知系统，支持邮件、短信、推送通知。</p>

<p><strong>English:</strong> Design a notification system that can send email, SMS, and push notifications after key events (order placed, payment failed, shipment update).</p>

<hr/>

<h3>Architecture Diagram</h3>

<pre><code>
┌─────────────┐     ┌──────────────────────────────────┐
│  Order Svc  │────▶│         Kafka Topic              │
│  Payment Svc│────▶│    &quot;notification-events&quot;          │
│  Shipping   │────▶│                                  │
└─────────────┘     │  Partition 0: user_id % 3 == 0   │
                    │  Partition 1: user_id % 3 == 1   │
                    │  Partition 2: user_id % 3 == 2   │
                    └──────────┬───────────────────────┘
                               │
                    ┌──────────▼───────────────────────┐
                    │     Notification Consumer Group   │
                    │                                  │
                    │  Consumer 0 ──▶ Email Worker      │
                    │  Consumer 1 ──▶ SMS Worker        │
                    │  Consumer 2 ──▶ Push Worker       │
                    └──────────────────────────────────┘
                                        │
                               Failed messages
                                        │
                               ┌────────▼────────┐
                               │  Dead Letter     │
                               │  Queue (DLQ)     │
                               └─────────────────┘
</code></pre>

<hr/>

<h3>Step 1: Define the Event Schema</h3>

<pre><code>
# events.py — Define event contracts clearly
from dataclasses import dataclass
from typing import Optional
import json
import time

@dataclass
class NotificationEvent:
    event_id: str          # Unique ID for deduplication
    event_type: str        # &quot;order.placed&quot;, &quot;payment.failed&quot;, &quot;shipment.updated&quot;
    user_id: str           # Kafka partition key — ensures order for same user
    timestamp: float       # Unix timestamp
    payload: dict          # Event-specific data
    metadata: Optional[dict] = None  # Tracing info, retry count, etc.
    
    def to_json(self) -&gt; bytes:
        return json.dumps({
            &quot;event_id&quot;: self.event_id,
            &quot;event_type&quot;: self.event_type,
            &quot;user_id&quot;: self.user_id,
            &quot;timestamp&quot;: self.timestamp,
            &quot;payload&quot;: self.payload,
            &quot;metadata&quot;: self.metadata or {}
        }).encode(&quot;utf-8&quot;)
    
    @classmethod
    def from_json(cls, data: bytes) -&gt; &quot;NotificationEvent&quot;:
        d = json.loads(data)
        return cls(**d)
</code></pre>

<hr/>

<h3>Step 2: Producer — Publishing Events</h3>

<pre><code>
# producer.py
from kafka import KafkaProducer
from kafka.errors import KafkaError
import logging

logger = logging.getLogger(__name__)

class NotificationProducer:
    def __init__(self, bootstrap_servers: list[str]):
        self.producer = KafkaProducer(
            bootstrap_servers=bootstrap_servers,
            # Durability: wait for all in-sync replicas to acknowledge
            acks=&quot;all&quot;,
            # Enable idempotent producer — prevents duplicate messages on retry
            enable_idempotence=True,
            # Retry up to 5 times on transient failures
            retries=5,
            # Batch messages for throughput (linger up to 10ms)
            linger_ms=10,
            batch_size=16384,  # 16KB batches
        )
        self.topic = &quot;notification-events&quot;
    
    def publish(self, event: NotificationEvent) -&gt; bool:
        &quot;&quot;&quot;Publish event; use user_id as partition key for ordering.&quot;&quot;&quot;
        try:
            future = self.producer.send(
                self.topic,
                key=event.user_id.encode(&quot;utf-8&quot;),  # Same user → same partition
                value=event.to_json(),
            )
            # Block until broker confirms receipt (with timeout)
            record_metadata = future.get(timeout=10)
            logger.info(
                f&quot;Published {event.event_type} to partition &quot;
                f&quot;{record_metadata.partition} offset {record_metadata.offset}&quot;
            )
            return True
        except KafkaError as e:
            logger.error(f&quot;Failed to publish event {event.event_id}: {e}&quot;)
            # In production: send to a fallback DB for retry
            return False
    
    def close(self):
        # Flush remaining buffered messages before shutdown
        self.producer.flush()
        self.producer.close()
</code></pre>

<hr/>

<h3>Step 3: Consumer — Processing with At-Least-Once Semantics</h3>

<pre><code>
# consumer.py
from kafka import KafkaConsumer
from kafka.structs import OffsetAndMetadata, TopicPartition
import json, logging, time

logger = logging.getLogger(__name__)

class NotificationConsumer:
    def __init__(self, bootstrap_servers: list[str], group_id: str):
        self.consumer = KafkaConsumer(
            &quot;notification-events&quot;,
            bootstrap_servers=bootstrap_servers,
            group_id=group_id,
            # Disable auto-commit — we commit AFTER successful processing
            # This guarantees at-least-once delivery
            enable_auto_commit=False,
            # If no committed offset, start from the earliest message
            auto_offset_reset=&quot;earliest&quot;,
            # Deserialize from JSON bytes
            value_deserializer=lambda b: json.loads(b.decode(&quot;utf-8&quot;)),
            key_deserializer=lambda b: b.decode(&quot;utf-8&quot;) if b else None,
        )
        self.dlq_producer = DLQProducer()  # Dead letter queue
    
    def process(self):
        &quot;&quot;&quot;Main consume loop with manual offset commit.&quot;&quot;&quot;
        for message in self.consumer:
            event_data = message.value
            event = NotificationEvent(**event_data)
            
            success = self._handle_with_retry(event, max_retries=3)
            
            if success:
                # Only commit offset after successful processing
                # This is the key to at-least-once semantics
                tp = TopicPartition(message.topic, message.partition)
                self.consumer.commit({tp: OffsetAndMetadata(message.offset + 1, None)})
            else:
                # Send to Dead Letter Queue for manual inspection/replay
                self.dlq_producer.send(event)
                # Still commit — we don&#x27;t want to block the partition forever
                tp = TopicPartition(message.topic, message.partition)
                self.consumer.commit({tp: OffsetAndMetadata(message.offset + 1, None)})
    
    def _handle_with_retry(self, event: NotificationEvent, max_retries: int) -&gt; bool:
        &quot;&quot;&quot;Route event to appropriate handler with exponential backoff retry.&quot;&quot;&quot;
        handler_map = {
            &quot;order.placed&quot;: self._send_order_confirmation,
            &quot;payment.failed&quot;: self._send_payment_alert,
            &quot;shipment.updated&quot;: self._send_shipment_update,
        }
        
        handler = handler_map.get(event.event_type)
        if not handler:
            logger.warning(f&quot;No handler for event type: {event.event_type}&quot;)
            return True  # Don&#x27;t retry unknown events
        
        for attempt in range(max_retries):
            try:
                handler(event)
                return True
            except Exception as e:
                wait = (2 ** attempt)  # Exponential backoff: 1s, 2s, 4s
                logger.warning(f&quot;Attempt {attempt+1} failed for {event.event_id}: {e}. Retrying in {wait}s&quot;)
                time.sleep(wait)
        
        return False  # All retries exhausted
    
    def _send_order_confirmation(self, event: NotificationEvent):
        # Call email service, SMS gateway, push notification service
        user_id = event.user_id
        order_id = event.payload[&quot;order_id&quot;]
        # ... actual notification logic
        logger.info(f&quot;Sent order confirmation to user {user_id} for order {order_id}&quot;)
</code></pre>

<hr/>

<h3>Step 4: Idempotency — The Unsung Hero</h3>

<p><strong>中文：</strong> 消息队列保证 <strong>at-least-once</strong> 投递，这意味着同一条消息可能被处理两次。如果你的系统没有幂等性保证，用户可能收到两封确认邮件，或被扣款两次。</p>

<p><strong>English:</strong> At-least-once delivery means the same message can arrive twice (e.g., consumer crashed after processing but before committing offset). Always design consumers to be <strong>idempotent</strong>.</p>

<pre><code>
# idempotency.py — Using Redis to track processed events
import redis

class IdempotentNotificationHandler:
    def __init__(self):
        self.redis = redis.Redis(host=&quot;localhost&quot;, port=6379, db=0)
        self.TTL = 86400  # 24 hours — enough to catch duplicates
    
    def process_event(self, event: NotificationEvent) -&gt; bool:
        idempotency_key = f&quot;processed:{event.event_id}&quot;
        
        # SET NX = &quot;set if not exists&quot; — atomic check-and-set
        was_new = self.redis.set(
            idempotency_key,
            &quot;1&quot;,
            ex=self.TTL,
            nx=True  # Only set if key doesn&#x27;t exist
        )
        
        if not was_new:
            # This event was already processed — skip it
            logger.info(f&quot;Duplicate event {event.event_id} — skipping&quot;)
            return True
        
        # First time seeing this event — process it
        return self._do_send_notification(event)
</code></pre>

<hr/>

<h2>Part 3: Edge Cases &amp; Gotchas / 边界情况 (2 min)</h2>

<p><strong>中文 + English:</strong></p>

<p><strong>1. 消费者重平衡 / Consumer Rebalancing</strong></p>
<p>当消费者加入或离开消费者组时，Kafka 会触发重平衡，暂停所有消费。设计时要确保处理过程的原子性。</p>
<p><em>When consumers join/leave a group, Kafka triggers rebalancing — all consumption pauses. Use cooperative rebalancing (<code>partition.assignment.strategy=CooperativeStickyAssignor</code>) to minimize disruption.</em></p>

<p><strong>2. 消息顺序保证 / Ordering Guarantees</strong></p>
<p>Kafka 只在<strong>分区内</strong>保证顺序。跨分区无顺序。设计时用有意义的 key（如 user_id）分区，确保同一用户的事件落在同一分区。</p>
<p><em>Kafka only guarantees order <strong>within a partition</strong>. Use a meaningful partition key (user_id, order_id) so related events land on the same partition.</em></p>

<p><strong>3. 消费者落后 / Consumer Lag</strong></p>
<p>如果消费者处理速度跟不上生产速度，lag 会越来越大。监控 consumer lag 是运维必备。</p>
<p><em>Monitor <code>consumer_lag</code> metric. If lag grows unbounded, add more consumer instances (up to the number of partitions) or optimize the handler.</em></p>

<p><strong>4. 大消息问题 / Large Message Problem</strong></p>
<p>Kafka 默认最大消息 1MB。发大文件？把内容存 S3，消息里只传引用。</p>
<p><em>Default max message size is 1MB. For large payloads: store in S3/GCS, put the reference URL in the message. Never put binary blobs in queues.</em></p>

<p><strong>5. 时钟偏移 / Clock Skew</strong></p>
<p>不要用消息里的 timestamp 做业务逻辑。生产者时钟可能偏移。</p>
<p><em>Don&#x27;t use event timestamps for business logic ordering — clocks drift. Use Kafka&#x27;s broker-assigned offset for true ordering.</em></p>

<hr/>

<h2>Part 4: Real-World Application / 实际应用 (2 min)</h2>

<p><strong>中文：</strong> 真实系统里的消息队列：</p>

<p><strong>English:</strong> How this looks in production systems:</p>

<p><strong>LinkedIn (Kafka 的诞生地 / Kafka&#x27;s birthplace)</strong></p>
<p>- LinkedIn 开源了 Kafka，最初用于处理 activity stream（用户点击、浏览等）</p>
<p>- 现在每天处理超过 <strong>7 万亿</strong>条消息</p>
<p>- 参考: https://engineering.linkedin.com/kafka/kafka-linkedin-current-and-future</p>

<p><strong>Uber — 行程派单 / Ride Dispatch</strong></p>
<p>- 司机位置每秒上报 → Kafka topic → 派单引擎消费</p>
<p>- 消息量：峰值每秒百万级</p>
<p>- 参考: https://www.uber.com/blog/reliable-reprocessing/</p>

<p><strong>Stripe — 支付事件 / Payment Events</strong></p>
<p>- 每笔支付产生 N 个事件（授权、捕获、退款等）</p>
<p>- 各个下游服务（风控、财务、通知）独立消费</p>
<p>- 保证每个事件至少被每个服务消费一次</p>
<p>- 参考: https://stripe.com/blog/message-queues</p>

<p><strong>Netflix — 实时数据管道 / Real-Time Pipeline</strong></p>
<p>- 视频观看数据 → Kafka → 推荐系统、A/B 测试分析、计费</p>
<p>- 参考: https://netflixtechblog.com/keystone-real-time-stream-processing-platform-a3ee651812a</p>

<p><strong>面试提示 / Interview Pattern:</strong></p>
<p>&gt; 每当你的设计里有一个服务需要触发多个下游服务，或者需要异步处理、削峰填谷、解耦，就应该加消息队列。</p>
<p>&gt;</p>
<p>&gt; <em>Whenever your design has one service that needs to trigger multiple downstream services, or you need async processing, buffering, or decoupling — reach for a message queue.</em></p>

<hr/>

<h2>Part 5: Interview Simulation / 面试模拟 (3 min)</h2>

<p><strong>中文：</strong> 以下是面试中最常见的追问，以及简洁回答思路。</p>

<p><strong>English:</strong> The 5 most common follow-up questions:</p>

<hr/>

<p><strong>Q1: 如何保证消息不丢失？/ How do you guarantee no message loss?</strong></p>

<p>&gt; <strong>Producer side:</strong> Set <code>acks=all</code> (wait for all in-sync replicas) + <code>enable_idempotence=True</code>.</p>
<p>&gt; <strong>Broker side:</strong> Set <code>min.insync.replicas=2</code> to require at least 2 replicas to acknowledge.</p>
<p>&gt; <strong>Consumer side:</strong> Disable auto-commit; manually commit only after successful processing.</p>
<p>&gt; <strong>Result:</strong> At-least-once delivery. Accept that duplicates can happen, design consumers to be idempotent.</p>

<hr/>

<p><strong>Q2: Kafka vs SQS，你会怎么选？/ Kafka vs SQS, how do you choose?</strong></p>

<p>&gt; - <strong>Choose Kafka</strong> when: you need message replay, multiple independent consumers, very high throughput (&gt;100K/s), stream processing, or event sourcing.</p>
<p>&gt; - <strong>Choose SQS</strong> when: you want fully managed simplicity, visibility timeout semantics, built-in DLQ, and don&#x27;t need replay or complex routing. Great for task queues.</p>
<p>&gt; - <strong>Rule of thumb:</strong> SQS for task queues, Kafka for data pipelines and event streams.</p>

<hr/>

<p><strong>Q3: 如何处理消费者崩溃？/ What happens when a consumer crashes?</strong></p>

<p>&gt; With manual offset commit disabled (<code>enable_auto_commit=False</code>):</p>
<p>&gt; 1. Consumer crashes after processing but before committing → same message redelivered to next consumer in group</p>
<p>&gt; 2. Consumer crashes mid-batch → entire batch redelivered</p>
<p>&gt; This is why idempotency is non-negotiable. Use Redis or DB to track <code>processed_event_ids</code>.</p>

<hr/>

<p><strong>Q4: 消息队列如何帮助削峰填谷？/ How does a queue help with traffic spikes?</strong></p>

<p>&gt; Queue acts as a <strong>buffer</strong>. During Black Friday, order service produces 100K orders/minute. Payment service can only process 10K/minute. Without a queue, payment service crashes under load. With a queue, orders buffer up; payment service consumes at its own pace. Users see &quot;order received&quot; immediately, payment confirms asynchronously. The queue absorbs the spike.</p>

<hr/>

<p><strong>Q5: 如果消费者处理一直失败怎么办？/ What if a message keeps failing?</strong></p>

<p>&gt; Dead Letter Queue (DLQ) pattern:</p>
<p>&gt; 1. Set <code>max_delivery_attempts = 3</code> (or retry in consumer code)</p>
<p>&gt; 2. After N failures, move message to DLQ topic</p>
<p>&gt; 3. DLQ messages trigger alerts to on-call engineer</p>
<p>&gt; 4. Engineer investigates, fixes the bug, then <strong>replays</strong> DLQ messages back to the original topic</p>
<p>&gt; Never silently drop messages. Always have a DLQ.</p>

<hr/>

<p><em>下周六继续深挖！Next Saturday: we&#x27;ll go deeper on Kafka internals — partitions, replication, and the ISR mechanism. 🚀</em></p>

<hr/>

<p><strong>References / 参考资料:</strong></p>
<p>- 📖 Confluent Kafka documentation: https://docs.confluent.io/platform/current/kafka/introduction.html</p>
<p>- 📖 Designing Data-Intensive Applications (Chapter 11 — Stream Processing) by Martin Kleppmann</p>
<p>- 🎥 Hussein Nasser — Kafka Deep Dive: https://www.youtube.com/watch?v=R873BlNVUqQ</p>
<p>- 📖 AWS SQS vs Kafka comparison: https://aws.amazon.com/compare/the-difference-between-sqs-and-kafka/</p>
<p>- 📖 Uber reliable reprocessing: https://www.uber.com/blog/reliable-reprocessing/</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-27</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-27</guid>
      <pubDate>Fri, 27 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 11 / System Design Day 11</h2>
<h2>CAP 定理与最终一致性 / CAP Theorem &amp; Eventual Consistency</h2>

<p>&gt; <strong>难度 / Difficulty:</strong> Intermediate | <strong>阶段 / Phase:</strong> Growth | <strong>预计阅读时间 / Read time:</strong> 3 min</p>

<hr/>

<h2>🌍 真实场景 / Real-World Scenario</h2>

<p>想象你在设计 Twitter（现 X）的点赞系统。用户遍布全球，分布在北美、欧洲、亚洲的数据中心。当网络故障发生时，你必须做一个选择：</p>

<p><strong>要么</strong>继续接受点赞写入（可能导致各地数据不一致）；</p>
<p><strong>要么</strong>拒绝所有写入（保证数据一致，但系统不可用）。</p>

<p>这就是 CAP 定理的核心困境。</p>

<p>Imagine you&#x27;re designing Twitter&#x27;s like system, with users spread across data centers in North America, Europe, and Asia. When a network partition occurs, you face a hard choice:</p>

<p><strong>Either</strong> keep accepting like writes (risking inconsistent counts across regions),</p>
<p><strong>or</strong> reject all writes (keeping data consistent, but making the system unavailable).</p>

<p>This is the core dilemma of the CAP theorem.</p>

<hr/>

<h2>📐 CAP 定理解释 / CAP Theorem Explained</h2>

<p>CAP 定理由 Eric Brewer 在 2000 年提出，它说：<strong>分布式系统最多只能同时满足以下三项中的两项：</strong></p>

<p>CAP theorem (Brewer, 2000) states: <strong>a distributed system can guarantee at most 2 of these 3 properties simultaneously:</strong></p>

<p>| 属性 | 英文 | 解释 |</p>
<p>|------|------|------|</p>
<p>| <strong>C</strong> | Consistency | 所有节点在同一时刻看到相同数据 / All nodes see same data at same time |</p>
<p>| <strong>A</strong> | Availability | 每个请求都收到响应（非错误）/ Every request gets a response (non-error) |</p>
<p>| <strong>P</strong> | Partition Tolerance | 网络分区时系统仍继续运行 / System works despite network partitions |</p>

<p>&gt; ⚠️ <strong>关键洞察：</strong> 在真实分布式系统中，网络分区（P）是不可避免的。所以你实际上是在 <strong>CA（一致性 vs 可用性）</strong> 之间做取舍。</p>

<hr/>

<h2>🏛️ ASCII 架构图 / Architecture Diagram</h2>

<pre><code>
正常状态 / Normal State:
┌──────────┐         ┌──────────┐         ┌──────────┐
│  Node A  │◄───────►│  Node B  │◄───────►│  Node C  │
│ likes=42 │         │ likes=42 │         │ likes=42 │
└──────────┘         └──────────┘         └──────────┘
      ▲
  user writes

网络分区 / Network Partition:
┌──────────┐    ✗    ┌──────────┐         ┌──────────┐
│  Node A  │ BROKEN  │  Node B  │◄───────►│  Node C  │
│ likes=45 │         │ likes=42 │         │ likes=42 │
└──────────┘         └──────────┘         └──────────┘
(user wrote 3 more)   (partition: can&#x27;t sync)

CP 选择 (e.g. HBase, Zookeeper):   AP 选择 (e.g. Cassandra, DynamoDB):
→ 拒绝 Node B/C 的写入               → 允许各自独立写入
→ 数据一致，但不可用                  → 可用，但数据暂时不一致
</code></pre>

<hr/>

<h2>⚖️ 关键权衡 / Key Tradeoffs</h2>

<h3>CP 系统（一致性 + 分区容错）</h3>

<p><strong>为什么这样设计？</strong> 需要强一致性的场景，例如金融交易、库存系统。</p>

<p>- ✅ 数据永远一致，不会有脏读</p>
<p>- ❌ 网络分区时部分节点不可用</p>
<p>- 📦 代表：HBase, MongoDB (default), ZooKeeper, etcd</p>

<h3>AP 系统（可用性 + 分区容错）</h3>

<p><strong>为什么这样设计？</strong> 高可用性更重要，短暂不一致可接受，例如社交 feed、购物车。</p>

<p>- ✅ 系统始终响应，用户体验好</p>
<p>- ❌ 不同节点可能返回不同结果（<strong>最终一致性</strong>）</p>
<p>- 📦 代表：Cassandra, DynamoDB, CouchDB, DNS</p>

<h3>最终一致性 / Eventual Consistency</h3>

<pre><code>
时间线 Timeline:

t=0  User writes likes=45 to Node A
t=1  Node A is isolated (partition)
t=2  User reads from Node B → gets 42 (stale!)
t=5  Partition heals, nodes sync
t=6  User reads from Node B → gets 45 ✅ (eventually consistent)
</code></pre>

<p><strong>最终一致性</strong> 并非&quot;随机错误&quot;，而是&quot;在网络恢复后，所有副本最终达到相同状态&quot;。</p>

<hr/>

<h2>🚫 常见错误 / Common Mistakes</h2>

<p><strong>别踩这个坑：</strong></p>

<p>1. <strong>误解 CAP 是固定选择</strong> — 现代系统（如 DynamoDB）允许你按操作级别调整一致性（<code>ConsistencyLevel: QUORUM</code> vs <code>ONE</code>），而不是全局二选一。</p>

<p>2. <strong>把 CA 作为选项</strong> — 不存在真正的&quot;CA 系统&quot;，因为没有网络分区容错的系统根本不是分布式系统。</p>

<p>3. <strong>忽视 PACELC 扩展</strong> — CAP 只描述分区时的行为，[PACELC 模型](https://en.wikipedia.org/wiki/PACELC_theorem)还考虑了<strong>正常运行时</strong>的延迟 vs 一致性权衡，更全面。</p>

<p>4. <strong>混淆最终一致性和弱一致性</strong> — 最终一致性保证&quot;最终会对齐&quot;；弱一致性不做任何保证。</p>

<hr/>

<h2>🔍 面试重点 / Interview Focus</h2>

<p>当面试官问&quot;你会如何设计 X 系统&quot;时，主动提出 CAP：</p>

<p>&gt; &quot;这取决于一致性需求。如果是金融交易，我会选 CP；如果是社交 feed，AP + 最终一致性更合适，因为用户短暂看到旧数据不是大问题。&quot;</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>- 🔗 [CAP Theorem Explained — IBM](https://www.ibm.com/topics/cap-theorem)</p>
<p>- 🔗 [Brewer&#x27;s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services](https://users.ece.cmu.edu/~adrian/731-sp04/readings/GL-cap.pdf)</p>
<p>- 🔗 [PACELC theorem — Wikipedia](https://en.wikipedia.org/wiki/PACELC_theorem)</p>
<p>- 🔗 [Cassandra vs MongoDB — Consistency Model Comparison](https://cassandra.apache.org/doc/latest/cassandra/architecture/dynamo.html)</p>

<hr/>

<h2>🧒 ELI5（像我5岁一样解释）</h2>

<p>想象你和好朋友各有一本记事本，记录班里同学的生日。你们约定互相抄写更新。</p>

<p>- <strong>CP</strong>：如果你们之间的电话断了，就不写新内容，直到联系上为止（一致，但暂停工作）</p>
<p>- <strong>AP</strong>：各自继续记录，电话修好后再对比合并（继续工作，但暂时可能不一样）</p>

<p>大多数社交网站选择 AP：你的点赞数可能偶尔显示&quot;旧数据&quot;，但网站从不宕机。</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 12 / Algorithms Day 12</h2>
<h2>#15 3Sum — Medium — Two Pointers (3/5)</h2>

<p>&gt; <strong>难度 / Difficulty:</strong> 🟡 Medium | <strong>阶段 / Phase:</strong> Growth | <strong>预计时间 / Read time:</strong> 4 min</p>

<hr/>

<h2>🧩 双指针模式 (3/5) — 继续 Day 9 引入的模版</h2>

<p>Building on the <strong>双指针模式 / Two Pointers</strong> template from Day 9 (Valid Palindrome).</p>

<p><strong>模式回顾 / Pattern Recap:</strong></p>
<pre><code>
left, right = 0, len(arr) - 1
while left &lt; right:
    total = arr[left] + arr[right]
    if total == target: return [left, right]
    elif total &lt; target: left += 1
    else: right -= 1
</code></pre>
<p><strong>本模块问题 / Block Problems:</strong></p>
<p>1. ✅ #125 Valid Palindrome (Easy) — Day 9</p>
<p>2. ✅ #167 Two Sum II (Medium) — Day 11</p>
<p>3. 👉 <strong>#15 3Sum (Medium) — TODAY</strong></p>
<p>4. 🔜 #11 Container With Most Water (Medium)</p>
<p>5. 🔜 #42 Trapping Rain Water (Hard)</p>

<p><strong>今天的变化 / Today&#x27;s Twist:</strong> 从找&quot;2个数之和&quot;升级到找&quot;3个数之和为0&quot;，需要先固定一个数，再用双指针扫余下部分。</p>

<hr/>

<h2>🔗 题目链接 / Links</h2>

<p>- 📝 [LeetCode #15 — 3Sum](https://leetcode.com/problems/3sum/)</p>
<p>- 📹 [NeetCode — 3Sum Solution](https://neetcode.io/problems/three-integer-sum)</p>

<hr/>

<h2>🌍 真实场景类比 / Real-World Analogy</h2>

<p>想象你在整理一箱重量不等的砝码，你想找到<strong>三个</strong>砝码，使它们的重量加起来恰好为零（一正一负加中间值）。</p>

<p>逐一穷举三个砝码的所有组合是 O(n³)，太慢了。如果先把砝码<strong>按重量排序</strong>，固定最左边的砝码，然后用左右两个指针扫剩余部分，就能降到 O(n²)。</p>

<p>Imagine sorting weights in a box and finding <strong>three</strong> that sum to zero. Brute force is O(n³). Sort them, fix the leftmost, and use two pointers for the rest → O(n²).</p>

<hr/>

<h2>📋 问题描述 / Problem</h2>

<p>Given an integer array <code>nums</code>, return all the triplets <code>[nums[i], nums[j], nums[k]]</code> such that:</p>
<p>- <code>i != j</code>, <code>i != k</code>, <code>j != k</code></p>
<p>- <code>nums[i] + nums[j] + nums[k] == 0</code></p>

<p>The solution set <strong>must not contain duplicate triplets.</strong></p>

<pre><code>
Input:  nums = [-1, 0, 1, 2, -1, -4]
Output: [[-1, -1, 2], [-1, 0, 1]]
</code></pre>

<hr/>

<h2>🗺️ 映射到模版 / Mapping to Template</h2>

<p><strong>核心思路：</strong> 排序后，外层遍历固定 <code>nums[i]</code>，内层用双指针找 <code>nums[left] + nums[right] == -nums[i]</code>。</p>

<pre><code>
Fixed:   nums[i] = -1   target for inner = 0 - (-1) = 1
Array:   [-4, -1, -1, 0, 1, 2]  (sorted)
              i  L        R
              
Step 1: left=-1, right=2 → sum=1 ✅ found! → skip duplicates
Step 2: left=0,  right=1 → sum=1 ✅ found!
Step 3: left &gt;= right → stop inner loop
</code></pre>

<hr/>

<h2>🐍 Python 解法 + 逐行追踪 / Solution + Trace</h2>

<pre><code>
def threeSum(nums: list[int]) -&gt; list[list[int]]:
    nums.sort()                           # [-4, -1, -1, 0, 1, 2]
    result = []
    
    for i in range(len(nums) - 2):        # fix the first element
        if nums[i] &gt; 0:                   # sorted: if first &gt; 0, no solution
            break
        if i &gt; 0 and nums[i] == nums[i-1]:  # skip duplicates for i
            continue
        
        left, right = i + 1, len(nums) - 1  # two pointers for the rest
        
        while left &lt; right:
            total = nums[i] + nums[left] + nums[right]
            
            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                # skip duplicates for left and right
                while left &lt; right and nums[left] == nums[left + 1]:
                    left += 1
                while left &lt; right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total &lt; 0:
                left += 1   # need larger sum
            else:
                right -= 1  # need smaller sum
    
    return result

# Trace with nums = [-1, 0, 1, 2, -1, -4]:
# After sort: [-4, -1, -1, 0, 1, 2]
# i=0: nums[i]=-4, left=1(-1), right=5(2) → sum=-3 → left++
#       left=2(-1), right=5(2) → sum=-3 → left++
#       left=3(0),  right=5(2) → sum=-2 → left++
#       left=4(1),  right=5(2) → sum=-1 → left++
#       left&gt;=right → stop
# i=1: nums[i]=-1, left=2(-1), right=5(2) → sum=0 ✅ append [-1,-1,2]
#       skip dups → left=3(0), right=4(1) → sum=0 ✅ append [-1,0,1]
#       left&gt;=right → stop
# i=2: nums[i]=-1 == nums[i-1]=-1 → skip (duplicate!)
# i=3: nums[i]=0, left=4(1), right=5(2) → sum=3 → right--
#       left&gt;=right → stop
# Result: [[-1,-1,2], [-1,0,1]] ✅
</code></pre>

<p><strong>时间复杂度 / Time Complexity:</strong> O(n log n) sort + O(n²) = <strong>O(n²)</strong></p>
<p><strong>空间复杂度 / Space Complexity:</strong> O(1) extra (excluding output)</p>

<hr/>

<h2>⚡ 与模版的关键差异 / Key Differences from Template</h2>

<p>| | Two Sum II (Day 11) | 3Sum (Today) |</p>
<p>|--|--|--|</p>
<p>| 目标 | 找2个数之和 = target | 找3个数之和 = 0 |</p>
<p>| 结构 | 单层双指针 | 外层 for + 内层双指针 |</p>
<p>| 去重 | 不需要 | 必须跳过重复元素 |</p>
<p>| 复杂度 | O(n) | O(n²) |</p>

<hr/>

<h2>🔁 举一反三 / Pattern Connections</h2>

<p>- <strong>#11 Container With Most Water (下一题):</strong> 同样外层遍历 + 内层双指针，但优化目标不同（最大面积 vs 零和）</p>
<p>- <strong>#42 Trapping Rain Water (最难题):</strong> 双指针 + 边界最大值，是本模式的终极形态</p>
<p>- <strong>变体：</strong> 4Sum (#18) = 再加一层 for 循环 → O(n³)，同样思路</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>- 🔗 [LeetCode #15 — 3Sum](https://leetcode.com/problems/3sum/)</p>
<p>- 🔗 [NeetCode Video Solution](https://neetcode.io/problems/three-integer-sum)</p>
<p>- 🔗 [Two Pointers Pattern — LeetCode Explore](https://leetcode.com/explore/learn/card/array-and-string/205/array-two-pointer-technique/)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>想象你有一堆正数和负数的磁铁，你要找三块加起来刚好等于零。</p>

<p>先把它们从小到大排好，然后：拿起最左边的那块，再用两只手各从左右两边向中间夹。夹到了就记录下来，没夹到就根据总和太大还是太小来移动手。</p>

<p>这样就不用每三块都试一遍，快很多！</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 11 / Soft Skills Day 11</h2>
<h2>优先级排序 / Prioritization</h2>
<h3>当所有事情都&quot;同样重要&quot;时，你怎么决定做什么？</h3>
<h3>How do you decide what to work on when everything seems equally important?</h3>

<p>&gt; <strong>级别 / Level:</strong> Senior/Staff | <strong>类别 / Category:</strong> Prioritization | <strong>阶段 / Phase:</strong> Growth | <strong>预计时间 / Read time:</strong> 2 min</p>

<hr/>

<h2>💡 为什么这个问题很重要 / Why This Matters</h2>

<p>这是 Senior/Staff 工程师的核心能力之一。初级工程师执行任务；高级工程师<strong>决定做哪些任务</strong>。</p>

<p>面试官想知道：你是否会在信息不完整时做出理性决策，还是会陷入&quot;什么都想做&quot;或&quot;等别人告诉你&quot;的困境。</p>

<p>This is a core Senior/Staff engineer competency. Junior engineers execute tasks; senior engineers <strong>decide which tasks to execute</strong>.</p>

<p>The interviewer wants to know: can you make rational decisions with incomplete information, or do you get paralyzed?</p>

<hr/>

<h2>⭐ STAR 框架拆解 / STAR Breakdown</h2>

<p><strong>Situation（背景）:</strong></p>
<p>&gt; &quot;我加入新团队后第一个季度，我们同时有5个项目被标记为高优先级：两个客户承诺的功能、一个重要的性能优化、一个安全漏洞修复，还有一个技术债务重构。&quot;</p>

<p><strong>Task（任务）:</strong></p>
<p>&gt; &quot;我需要帮助团队决定顺序，但没有人能直接告诉我哪个&#x27;真正&#x27;最重要。&quot;</p>

<p><strong>Action（行动）:</strong></p>
<p>&gt; &quot;我用了一个简单框架：先问每项工作的<strong>影响范围</strong>（多少用户/多少收入受影响）和<strong>紧迫性</strong>（截止日期是硬性的吗），再评估<strong>依赖关系</strong>（哪项工作阻塞了其他事情）。安全漏洞虽然用户感知低，但是合规风险极高，我把它放第一位。客户承诺功能放第二，因为违约有商务影响。性能优化排第三，因为有量化的流失数据支撑。技术债务排最后，但我明确说明了不做的风险累积。&quot;</p>

<p><strong>Result（结果）:</strong></p>
<p>&gt; &quot;这个排序被 PM 和工程 Lead 接受了，我们按序交付，没有出现返工或紧急插队。&quot;</p>

<hr/>

<h2>❌ 糟糕 vs ✅ 优秀回答 / Bad vs Good Answer</h2>

<p><strong>❌ 不好的回答：</strong></p>
<p>&gt; &quot;我会先做最难的事情，这样其他事情就容易了。&quot;</p>

<p><strong>为什么不好？</strong> 没有考虑外部影响，把个人技术偏好凌驾于业务价值之上。</p>

<hr/>

<p><strong>✅ 好的回答结构：</strong></p>
<p>1. <strong>承认复杂性</strong> — &quot;当所有事情看起来都重要时，我的第一步是找出哪些是真正的约束条件（deadline、依赖、风险），而不是表面的紧迫感。&quot;</p>
<p>2. <strong>使用框架</strong> — 提到 Impact × Urgency 矩阵、ICE (Impact/Confidence/Ease)，或 RICE 框架</p>
<p>3. <strong>对齐业务目标</strong> — 把技术工作连接到公司/团队目标</p>
<p>4. <strong>明确沟通取舍</strong> — 说出&quot;如果我选择做 A，B 会推迟到 X 日期，风险是 Y&quot;</p>

<hr/>

<h2>🏆 Senior/Staff 级别加分项 / Senior/Staff Tips</h2>

<p>- <strong>Don&#x27;t just prioritize silently.</strong> 写下你的优先级排序并发给相关人员，这既是对齐，也是自我保护。</p>
<p>- <strong>&quot;No&quot; is a complete sentence, but explain the tradeoff.</strong> 当你说不做某事时，要量化&quot;不做&quot;的成本。</p>
<p>- <strong>Revisit priorities regularly.</strong> 每周或每冲刺开始时重新评估，因为情况会变。</p>
<p>- <strong>Separate urgency from importance.</strong> 紧急 ≠ 重要（艾森豪威尔矩阵）。很多&quot;紧急&quot;任务其实不重要。</p>

<hr/>

<h2>📋 关键要点 / Key Takeaways</h2>

<p>| 原则 | 说明 |</p>
<p>|------|------|</p>
<p>| 🎯 <strong>Impact First</strong> | 先问&quot;谁会受益，影响多大？&quot; |</p>
<p>| ⚡ <strong>Hard Deadlines</strong> | 区分&quot;有人希望早完成&quot;和&quot;晚一天就违约&quot; |</p>
<p>| 🔗 <strong>Unblock Others</strong> | 阻塞其他工程师的事情优先级隐性更高 |</p>
<p>| 🗣️ <strong>Communicate Tradeoffs</strong> | 说出你选择和放弃的理由 |</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>- 🔗 [The Eisenhower Matrix — FarnamStreet](https://fs.blog/eisenhower-matrix/)</p>
<p>- 🔗 [RICE Scoring: A Better Way to Prioritize Your Product Roadmap — Intercom](https://www.intercom.com/blog/rice-simple-prioritization-for-product-managers/)</p>
<p>- 🔗 [Staff Engineer: Leadership beyond the management track — Will Larson](https://staffeng.com/book)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>想象你有一张写满作业的清单，数学、语文、体育都要交。</p>

<p>聪明的做法不是&quot;随便挑&quot;，而是先问：</p>
<p>1. 哪个明天就到期（硬截止日期）？</p>
<p>2. 哪个影响最多分数（重要性）？</p>
<p>3. 哪个不做会影响其他同学（依赖）？</p>

<p>然后按顺序做，同时告诉老师&quot;我今天做 A 和 B，C 推迟到周五，原因是...&quot;</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 11 / Frontend Day 11</h2>
<h2>React useRef — React 的&quot;逃生舱口&quot; / Escape Hatch from React</h2>

<p>&gt; <strong>类别 / Category:</strong> React Hooks | <strong>周 / Week:</strong> 3 | <strong>阶段 / Phase:</strong> Growth | <strong>预计时间 / Read time:</strong> 2 min</p>

<hr/>

<h2>🌍 真实场景 / Real Scenario</h2>

<p>你在做一个视频播放器 dashboard，需要：</p>
<p>1. 当用户点击&quot;开始录制&quot;按钮时，<strong>聚焦</strong>到视频元素</p>
<p>2. 追踪一个<strong>内部计时器 ID</strong>（不需要触发重渲染）</p>
<p>3. <strong>直接调用</strong> DOM 元素的 <code>.play()</code> 方法</p>

<p><code>useState</code> 不适合：每次更新都会触发重渲染，而且不能持有 DOM 引用。这时就需要 <code>useRef</code>。</p>

<p>You&#x27;re building a video player dashboard and need to: focus the video element on button click, track a timer ID without triggering re-renders, and call <code>.play()</code> directly. <code>useState</code> would cause unnecessary re-renders. Enter <code>useRef</code>.</p>

<hr/>

<h2>🧠 useRef 的两大用途 / Two Use Cases</h2>

<h3>用途 1：持有 DOM 引用 / Holding a DOM Reference</h3>

<pre><code>
import { useRef } from &#x27;react&#x27;

function VideoPlayer() {
  // Creates { current: null } — persists across renders
  const videoRef = useRef&lt;HTMLVideoElement&gt;(null)
  
  const handlePlay = () =&gt; {
    // Direct DOM access — no React state involved
    videoRef.current?.play()
  }
  
  const handleFocus = () =&gt; {
    videoRef.current?.focus()
  }
  
  return (
    &lt;div&gt;
      {/* Attach ref to DOM element via ref prop */}
      &lt;video ref={videoRef} src=&quot;/demo.mp4&quot; /&gt;
      &lt;button onClick={handlePlay}&gt;▶ Play&lt;/button&gt;
      &lt;button onClick={handleFocus}&gt;Focus Video&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>

<h3>用途 2：持有可变值（不触发重渲染）/ Mutable Value (No Re-render)</h3>

<pre><code>
function RecordingTimer() {
  const [isRecording, setIsRecording] = useState(false)
  
  // Stores timer ID — changing it does NOT cause a re-render
  const timerIdRef = useRef&lt;ReturnType&lt;typeof setInterval&gt; | null&gt;(null)
  
  const startRecording = () =&gt; {
    setIsRecording(true)
    timerIdRef.current = setInterval(() =&gt; {
      console.log(&#x27;Recording...&#x27;)
    }, 1000)
  }
  
  const stopRecording = () =&gt; {
    setIsRecording(false)
    if (timerIdRef.current) {
      clearInterval(timerIdRef.current)
      timerIdRef.current = null
    }
  }
  
  return (
    &lt;button onClick={isRecording ? stopRecording : startRecording}&gt;
      {isRecording ? &#x27;⏹ Stop&#x27; : &#x27;⏺ Record&#x27;}
    &lt;/button&gt;
  )
}
</code></pre>

<hr/>

<h2>🤔 猜猜输出什么？/ What Does This Output?</h2>

<pre><code>
function Counter() {
  const [count, setCount] = useState(0)
  const renderCount = useRef(0)
  
  renderCount.current += 1  // increment on every render
  
  return (
    &lt;div&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
      &lt;p&gt;Renders: {renderCount.current}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(c =&gt; c + 1)}&gt;+1&lt;/button&gt;
    &lt;/div&gt;
  )
}
// After clicking +1 twice, what does &quot;Renders&quot; show?
</code></pre>

<p><strong>A)</strong> 1（从不更新）</p>
<p><strong>B)</strong> 2（只算点击）</p>
<p><strong>C)</strong> 3（初始渲染 + 2次点击）</p>
<p><strong>D)</strong> 0（ref 不触发重渲染）</p>

<p>&lt;details&gt;&lt;summary&gt;答案 / Answer&lt;/summary&gt;</p>

<p><strong>C) 3</strong> — <code>renderCount.current</code> 在每次渲染时递增。每次 <code>setCount</code> 触发重渲染，都会加 1。初始渲染时为 1，点击两次后为 3。注意：<code>renderCount.current</code> 的变化<strong>不会触发</strong>额外渲染，只是被动记录。</p>

<p>&lt;/details&gt;</p>

<hr/>

<h2>❌ 常见错误 vs ✅ 正确做法 / Common Mistakes vs Correct Approach</h2>

<pre><code>
// ❌ WRONG: Reading ref value during render to display in UI
function BadComponent() {
  const countRef = useRef(0)
  countRef.current += 1
  
  // BUG: React may batch renders or run effects multiple times
  // This count will be unreliable in React 18 Strict Mode (double-invokes)
  return &lt;p&gt;Rendered {countRef.current} times&lt;/p&gt;
}

// ✅ RIGHT: Use useState for values that should be displayed in UI
function GoodComponent() {
  const [renderCount, setRenderCount] = useState(0)
  
  useEffect(() =&gt; {
    setRenderCount(c =&gt; c + 1)
  })
  
  return &lt;p&gt;Rendered {renderCount} times&lt;/p&gt;
}

// ❌ WRONG: Accessing ref.current during render before it&#x27;s assigned
function BadRef() {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null)
  console.log(inputRef.current?.value)  // null on first render!
  return &lt;input ref={inputRef} /&gt;
}

// ✅ RIGHT: Access ref.current inside effects or event handlers
function GoodRef() {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null)
  
  useEffect(() =&gt; {
    // ref is assigned after DOM mounts
    console.log(inputRef.current?.value)
  }, [])
  
  return &lt;input ref={inputRef} /&gt;
}
</code></pre>

<hr/>

<h2>🔀 何时用 useRef vs useState / When to Use useRef vs useState</h2>

<p>| 场景 | 用哪个？ | 原因 |</p>
<p>|------|---------|------|</p>
<p>| 展示在 UI 里的值 | <code>useState</code> | 需要触发重渲染 |</p>
<p>| 计时器 ID / 请求 ID | <code>useRef</code> | 不需要显示，不需要重渲染 |</p>
<p>| DOM 元素访问 | <code>useRef</code> | 直接引用，React 管理 |</p>
<p>| 上一次渲染的值 | <code>useRef</code> | 跨渲染持久化，不触发渲染 |</p>
<p>| 追踪 isMounted | <code>useRef</code> | 副作用清理，不需要展示 |</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>- 🔗 [useRef — React Official Docs](https://react.dev/reference/react/useRef)</p>
<p>- 🔗 [Referencing Values with Refs — React Learn](https://react.dev/learn/referencing-values-with-refs)</p>
<p>- 🔗 [Manipulating the DOM with Refs — React Learn](https://react.dev/learn/manipulating-the-dom-with-refs)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p><code>useState</code> 就像写在白板上的数字——每次改变，班里所有人都重新看一遍（重渲染）。</p>

<p><code>useRef</code> 就像你口袋里的小纸条——你可以随时改上面的内容，但不会打扰到任何人（不触发重渲染）。</p>

<p>DOM ref 就更特别了：它就像一张&quot;通行证&quot;，让你可以直接敲响某个 DOM 元素的门，而不用通过 React 的&quot;前台&quot;。</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 12</h2>
<h2>RLHF — ChatGPT 是怎么学会&quot;有用&quot;的 / How ChatGPT Learned to Be Helpful</h2>

<p>&gt; <strong>类别 / Category:</strong> Training | <strong>模式 / Mode:</strong> Concept | <strong>阶段 / Phase:</strong> Growth | <strong>预计时间 / Read time:</strong> 2 min</p>

<hr/>

<h2>💡 直觉解释 / Intuitive Explanation</h2>

<p>预训练后的语言模型像一个&quot;博学但任性的学生&quot;——它什么都会说，但不一定有帮助、安全或符合人类期望。</p>

<p><strong>RLHF（Reinforcement Learning from Human Feedback，基于人类反馈的强化学习）</strong> 就是让这个学生接受&quot;社会化教育&quot;的过程：通过收集人类对模型输出的偏好评分，训练模型生成更符合人类价值观的回答。</p>

<p>Pre-trained LLMs know a lot but aren&#x27;t inherently helpful or safe. RLHF is the &quot;socialization&quot; step: collect human preferences on model outputs, then train the model to generate responses humans prefer.</p>

<hr/>

<h2>⚙️ 工作原理 / How It Works</h2>

<p>RLHF 分三个阶段：</p>

<h3>阶段 1: 监督微调 (SFT) / Supervised Fine-Tuning</h3>

<pre><code>
人类写示范回答
Input:  &quot;解释黑洞&quot;
Output: [人类写的高质量示范答案]

→ 直接在这些数据上微调基础模型
→ 模型学会&quot;期望的格式和风格&quot;
</code></pre>

<h3>阶段 2: 训练奖励模型 (RM) / Train Reward Model</h3>

<pre><code>
给模型同一个问题的多个回答，让人类排序：

Q: &quot;如何减肥？&quot;
回答A: &quot;节食+运动&quot;          ← 人类排 #1
回答B: &quot;服用减肥药&quot;          ← 人类排 #2  
回答C: &quot;节食即可，不用运动&quot;   ← 人类排 #3

奖励模型学习：给任意回答打分
</code></pre>

<h3>阶段 3: PPO 强化学习 / RL with PPO</h3>

<pre><code>
# Simplified PPO loop concept:
for prompt in training_prompts:
    response = policy_model.generate(prompt)    # current LLM
    reward = reward_model.score(response)        # RM gives score
    
    # Update policy to maximize reward,
    # but stay close to original (KL divergence penalty)
    loss = -reward + kl_penalty * kl(policy, reference)
    policy_model.update(loss)
</code></pre>

<pre><code>
完整流程 Full Pipeline:
基础模型     SFT        RM训练       PPO强化学习
Pre-trained → 学格式 → 学人类偏好 → 最大化人类评分
Base LLM     (SFT)     (Reward Model)  (RLHF)
</code></pre>

<hr/>

<h2>🌍 应用 / Applications</h2>

<p>| 系统 | RLHF 的作用 |</p>
<p>|------|------------|</p>
<p>| <strong>ChatGPT</strong> | 从&quot;预测下一个词&quot;变成&quot;有帮助、无害、诚实&quot; |</p>
<p>| <strong>Claude</strong> | Anthropic 使用 Constitutional AI (CAI)，RLHF 的变体 |</p>
<p>| <strong>Llama 2 Chat</strong> | Meta 开源 RLHF 模型，可本地运行 |</p>
<p>| <strong>Gemini</strong> | Google 的对话模型，同样有 RLHF 阶段 |</p>

<hr/>

<h2>🐍 可运行代码片段 / Runnable Python Snippet</h2>

<p>这里用 <code>trl</code> 库演示 RLHF 的核心思路（无需 GPU 的简化版）：</p>

<pre><code>
# pip install trl transformers torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

# Simulate a reward model scoring responses
# (In real RLHF, this is trained on human preference data)
def simple_reward_model(response: str) -&gt; float:
    &quot;&quot;&quot;Score a response based on simple heuristics.&quot;&quot;&quot;
    score = 0.0
    
    # Reward helpfulness signals
    if len(response) &gt; 50: score += 0.3         # detailed enough
    if &quot;because&quot; in response.lower(): score += 0.2  # gives reasoning
    if &quot;?&quot; not in response: score += 0.1         # not evasive
    
    # Penalize harmful patterns  
    if &quot;i can&#x27;t help&quot; in response.lower(): score -= 0.5
    if &quot;kill&quot; in response.lower(): score -= 1.0
    
    return score

# Test with example responses
responses = [
    &quot;I can&#x27;t help with that.&quot;,
    &quot;Exercise regularly and maintain a balanced diet because consistent habits lead to sustainable weight loss.&quot;,
    &quot;Just eat less.&quot;
]

for r in responses:
    print(f&quot;Score: {simple_reward_model(r):.1f} | {r[:50]}...&quot;)
# Score: -0.5 | I can&#x27;t help with that...
# Score:  0.6 | Exercise regularly and maintain a balanced...
# Score:  0.3 | Just eat less...
</code></pre>

<hr/>

<h2>⚠️ RLHF 的局限性 / Limitations</h2>

<p>1. <strong>奖励黑客 / Reward Hacking:</strong> 模型学会&quot;取悦&quot;奖励模型，而非真正有帮助（如生成冗长但空洞的回答）</p>
<p>2. <strong>人类偏好的偏差 / Human Bias:</strong> 训练数据中的人类标注员有自己的偏见</p>
<p>3. <strong>成本高 / Expensive:</strong> 需要大量人工标注，质量难以扩展</p>
<p>4. <strong>DPO 正在替代 PPO:</strong> Direct Preference Optimization 更简单，目前很多新模型已迁移</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>- 🔗 [InstructGPT Paper (OpenAI) — Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)</p>
<p>- 🔗 [Hugging Face RLHF Blog — Illustrating RLHF](https://huggingface.co/blog/rlhf)</p>
<p>- 🔗 [TRL Library — Transformer Reinforcement Learning](https://github.com/huggingface/trl)</p>
<p>- 🔗 [Direct Preference Optimization (DPO) Paper](https://arxiv.org/abs/2305.18290)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>想象你在教一只小狗（语言模型）坐下。</p>

<p>第一步：你示范给它看（SFT — 监督学习）。</p>
<p>第二步：你训练另一个&quot;评判员&quot;来区分好的坐姿和差的坐姿（奖励模型）。</p>
<p>第三步：小狗每次坐好了，评判员给它零食；坐歪了，没有零食（强化学习）。</p>

<p>经过很多次训练后，小狗学会了&quot;人类喜欢什么样的坐姿&quot;。ChatGPT 就是这样学会&quot;人类喜欢什么样的回答&quot;的。</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-26</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-26</guid>
      <pubDate>Thu, 26 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 10 / System Design Day 10</h2>
<p><strong>Topic: Consistent Hashing (一致性哈希)</strong></p>
<p><em>预计阅读时间 / Estimated reading time: 3 minutes</em></p>

<hr/>

<h2>场景 / Scenario</h2>

<p>想象你在设计一个分布式缓存系统（比如 Redis 集群），有 10 台缓存服务器存储着数百万用户的数据。</p>

<p><em>Imagine you&#x27;re designing a distributed cache (like a Redis cluster) with 10 servers storing millions of users&#x27; data.</em></p>

<p>一天，服务器 #3 宕机了。用系统的普通哈希 <code>key % 10</code>，你要重新分配 <strong>90% 的数据</strong>！</p>

<p><em>One day, server #3 goes down. With simple modulo hashing <code>key % 10</code>, you&#x27;d need to reassign <strong>90% of your data</strong>!</em></p>

<p><strong>一致性哈希只需要重新分配 ~1/N 的数据。这就是它的魔力。</strong></p>

<p><em>Consistent hashing only reassigns ~1/N of data. That&#x27;s the magic.</em></p>

<hr/>

<h2>架构图 / Architecture Diagram</h2>

<pre><code>
                    哈希环 / Hash Ring (0 to 360°)
                         0°
                         │
              Server A   │   Server B
              (90°)      │   (180°)
                    ┌────┴────┐
              ──────┤  RING   ├──────
                    └────┬────┘
              Server D   │   Server C
              (315°)     │   (270°)
                         │
                        360°

  Key &quot;user:123&quot; hashes to 210° → goes to Server C (next clockwise)
  Key &quot;user:456&quot; hashes to 95°  → goes to Server B (next clockwise)

  Virtual Nodes (虚拟节点):
  ┌─────────────────────────────────────────┐
  │  Physical: A  B  C  D                   │
  │  Virtual:  A1 B1 C1 D1 A2 B2 C2 D2 ... │
  │  (150 virtual nodes per physical node)  │
  └─────────────────────────────────────────┘
</code></pre>

<p><strong>数据流 / Data Flow:</strong></p>
<p>1. 计算 key 的哈希值，映射到环上某个角度 → <em>Hash key to a position on the ring</em></p>
<p>2. 顺时针找到第一个服务器节点 → <em>Find next server clockwise</em></p>
<p>3. 读写该服务器 → <em>Read/write from that server</em></p>
<p>4. 服务器宕机：只有它的数据转移到下一个节点 → <em>On failure: only its data migrates to the next node</em></p>

<hr/>

<h2>关键权衡 / Key Tradeoffs</h2>

<p><strong>为什么这样设计？/ Why this design?</strong></p>

<p>| 普通哈希 / Simple Hash | 一致性哈希 / Consistent Hash |</p>
<p>|---|---|</p>
<p>| <code>key % N</code> 简单但脆弱 | 环形映射，容错强 |</p>
<p>| 增减节点 → 大规模重分配 | 增减节点 → 仅影响 ~1/N 数据 |</p>
<p>| 热点不均匀难处理 | 虚拟节点解决负载均衡 |</p>

<p><strong>虚拟节点的作用 / Virtual Nodes:</strong></p>
<p>每个物理节点在环上有多个虚拟位置（通常 100-200 个），解决数据分布不均的问题。<em>Each physical node has many virtual positions on the ring, solving uneven data distribution.</em></p>

<p><strong>CAP 定理视角 / CAP Perspective:</strong></p>
<p>一致性哈希帮助在分区容错（P）下提升可用性（A），但一致性（C）需要额外机制（如 quorum reads）保证。</p>

<hr/>

<h2>别踩这个坑 / Common Mistakes</h2>

<p>❌ <strong>虚拟节点数量太少</strong> — 数据分布会很不均匀，导致热点</p>
<p><em>Too few virtual nodes → uneven distribution → hot spots</em></p>

<p>❌ <strong>不考虑节点权重</strong> — 新服务器内存更大，应承担更多虚拟节点</p>
<p><em>Ignoring node weights → underutilizing powerful servers</em></p>

<p>❌ <strong>哈希函数选错</strong> — 用差的哈希函数（如 MD5）导致聚集</p>
<p><em>Bad hash function → clustering → poor distribution</em></p>

<p>✅ 用 MurmurHash 或 FNV1a，配合 150-200 个虚拟节点，是生产环境的黄金配置。</p>
<p><em>Use MurmurHash or FNV1a with 150-200 virtual nodes in production.</em></p>

<hr/>

<h2>实际使用 / Real-World Usage</h2>

<p>- <strong>Amazon DynamoDB</strong> — 内部分区路由</p>
<p>- <strong>Apache Cassandra</strong> — token-based consistent hashing</p>
<p>- <strong>Memcached / Twemproxy</strong> — 客户端一致性哈希</p>
<p>- <strong>Nginx upstream hash</strong> — <code>hash $request_uri consistent</code></p>

<hr/>

<h2>📚 References</h2>

<p>1. [Consistent Hashing — Tom White&#x27;s original paper explanation](https://www.toptal.com/big-data/consistent-hashing)</p>
<p>2. [Amazon DynamoDB&#x27;s use of consistent hashing](https://aws.amazon.com/blogs/database/amazon-dynamodb-under-the-hood-how-we-built-a-hyper-scale-database/)</p>
<p>3. [Cassandra&#x27;s consistent hashing implementation](https://cassandra.apache.org/doc/latest/cassandra/architecture/dynamo.html)</p>

<hr/>

<h2>🧒 ELI5 (解释给5岁小孩听)</h2>

<p>想象一圈小朋友站成一个圆，每人负责一段颜色。玩具来了，看看玩具是什么颜色，顺时针找到对应颜色的小朋友，就给他。少了一个小朋友，只有他那段颜色的玩具要重新分，其他小朋友不受影响！</p>

<p><em>Imagine kids standing in a circle, each responsible for a color range. A toy arrives — find the next kid clockwise with that color. If one kid leaves, only their toys need reassigning. Everyone else stays put!</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 11 / Algorithms Day 11</h2>
<p><strong>#167 Two Sum II — Input Array Is Sorted · 🟡 Medium</strong></p>
<p><em>预计阅读时间 / Estimated reading time: 4 minutes</em></p>

<hr/>

<h2>🧩 双指针模式 (2/5) — 继承 Day 10 的模版</h2>

<p><em>Building on the Two Pointers template from Day 10</em></p>

<p>今天是双指针模式的<strong>第 2 题</strong>（共 5 题）。上一题 Valid Palindrome 用双指针判断回文；今天我们用<strong>同样的框架</strong>解决&quot;有序数组找配对&quot;问题。</p>

<p><em>This is the 2nd problem in our Two Pointers block (5 total). Yesterday we checked palindromes; today we use the same framework to find pairs in a sorted array.</em></p>

<p><strong>本 block 全部 5 题 / All 5 problems:</strong></p>
<p>1. ✅ #125 Valid Palindrome (Easy) — Day 10</p>
<p>2. 👈 <strong>#167 Two Sum II (Medium) — TODAY</strong></p>
<p>3. #15 3Sum (Medium)</p>
<p>4. #11 Container With Most Water (Medium)</p>
<p>5. #42 Trapping Rain Water (Hard)</p>

<p><strong>通用模版回顾 / Template Recap:</strong></p>
<pre><code>
left, right = 0, len(arr) - 1
while left &lt; right:
    total = arr[left] + arr[right]
    if total == target: return [left, right]
    elif total &lt; target: left += 1   # need bigger sum
    else: right -= 1                  # need smaller sum
</code></pre>

<p><strong>与 Valid Palindrome 的对比 / vs Yesterday:</strong></p>

<p>| | Valid Palindrome | Two Sum II |</p>
<p>|---|---|---|</p>
<p>| 移动条件 / Move when | chars don&#x27;t match | sum ≠ target |</p>
<p>| 收缩方向 / Shrink | both sides toward middle | whichever side adjusts sum |</p>
<p>| 核心逻辑 / Core | compare chars | adjust sum magnitude |</p>

<hr/>

<h2>题目 / Problem</h2>

<p>🔗 [LeetCode #167](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) · 🟡 Medium</p>
<p>📹 [NeetCode Video](https://neetcode.io/problems/two-sum-ii)</p>

<p><strong>现实类比 / Real-World Analogy:</strong></p>

<p>你有一张<strong>已排序</strong>的价目表，要找出恰好等于预算 <code>target</code> 的两件商品。</p>
<p><em>You have a sorted price list and want to find exactly two items that sum to your budget.</em></p>

<p><strong>题目 / Problem:</strong></p>
<p>给一个 1-indexed、<strong>非递减排序</strong>的数组，找两个数相加等于 <code>target</code>，返回它们的下标（1-indexed）。每个输入保证有唯一解。</p>
<p><em>Given a 1-indexed, non-decreasing sorted array, find two numbers that sum to target. Return 1-indexed positions. Exactly one solution exists.</em></p>

<pre><code>
Input:  numbers = [2, 7, 11, 15], target = 9
Output: [1, 2]  (numbers[0] + numbers[1] = 2 + 7 = 9)
</code></pre>

<hr/>

<h2>💡 套用模版 / Mapping to Template</h2>

<p>模版中 <code>arr[left] + arr[right]</code> 对应今天的 <code>numbers[left] + numbers[right]</code>。</p>

<p><strong>为什么有序数组可以用双指针？/ Why does sorting enable two pointers?</strong></p>

<p>关键洞察：数组排序后，如果 <code>sum &lt; target</code>，我们<strong>确定</strong>需要更大的值 → 移动左指针。如果 <code>sum &gt; target</code>，需要更小的值 → 移动右指针。无序数组无法这样推断！</p>

<p><em>Key insight: With a sorted array, if sum &lt; target, we KNOW we need a bigger value → move left. If sum &gt; target, we need smaller → move right. Unsorted arrays can&#x27;t support this reasoning!</em></p>

<hr/>

<h2>🐍 Python 解法 + 逐步追踪 / Solution + Trace</h2>

<pre><code>
def twoSum(numbers: list[int], target: int) -&gt; list[int]:
    left, right = 0, len(numbers) - 1  # 1
    
    while left &lt; right:                 # 2
        current_sum = numbers[left] + numbers[right]  # 3
        
        if current_sum == target:       # 4
            return [left + 1, right + 1]  # convert to 1-indexed
        elif current_sum &lt; target:      # 5
            left += 1   # need bigger number
        else:
            right -= 1  # need smaller number
    
    return []  # guaranteed to find answer, never reaches here
</code></pre>

<p><strong>追踪 / Trace</strong> with <code>numbers = [2, 7, 11, 15], target = 9</code>:</p>

<pre><code>
Step 1: left=0, right=3 → 2+15=17 &gt; 9  → right=2
Step 2: left=0, right=2 → 2+11=13 &gt; 9  → right=1
Step 3: left=0, right=1 → 2+7=9  == 9  → return [1, 2] ✅
</code></pre>

<p><strong>时间/空间复杂度 / Complexity:</strong></p>
<p>- ⏱ Time: <strong>O(n)</strong> — each pointer moves at most n steps total</p>
<p>- 💾 Space: <strong>O(1)</strong> — no extra data structures</p>

<p><strong>vs. Brute Force:</strong> O(n²) with nested loops. Two pointers give a 10-100x speedup on large inputs.</p>

<hr/>

<h2>举一反三 / Pattern Connections</h2>

<p><strong>在本 block 中 / Within this pattern block:</strong></p>

<p>- <strong>#15 3Sum (下一题):</strong> Same two-pointer idea + outer loop. Fix one element, two-pointer the rest.</p>
<p>- <strong>#11 Container With Most Water:</strong> <code>left</code>, <code>right</code> move based on which height is smaller — same structure!</p>
<p>- <strong>#42 Trapping Rain Water:</strong> Two pointers + track running max from each side. Most complex variation.</p>

<p><strong>看到这些信号就想到双指针 / Recognize these signals:</strong></p>
<p>- ✅ Sorted array</p>
<p>- ✅ &quot;Find pair that sums to X&quot;</p>
<p>- ✅ O(1) space required</p>
<p>- ✅ Palindrome check</p>
<p>- ✅ &quot;Remove duplicates in-place&quot;</p>

<hr/>

<h2>📚 References</h2>

<p>1. [LeetCode #167 — Two Sum II](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/)</p>
<p>2. [NeetCode Two Sum II explanation](https://neetcode.io/problems/two-sum-ii)</p>
<p>3. [Two Pointers pattern guide — LeetCode Patterns](https://leetcode.com/discuss/study-guide/1688903/Solved-all-two-pointers-problems-in-100-days)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>你和朋友站在一排数字两端。你喊出你们俩数字的和。太小就让左边的人向右走一步（换更大的数）；太大就让右边的人向左走一步（换更小的数）；正好就赢了！</p>

<p><em>You and a friend stand at opposite ends of a number line. Call out your sum. Too small → left person steps right (bigger number). Too big → right person steps left (smaller). Exact match → win!</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 10 / Soft Skills Day 10</h2>
<p><strong>Topic: Proactiveness — 主动发现问题</strong></p>
<p><em>&quot;Tell me about a time you identified and solved a problem before others noticed&quot;</em></p>
<p><em>预计阅读时间 / Estimated reading time: 2 minutes</em></p>

<hr/>

<h2>为什么这道题很重要 / Why This Matters</h2>

<p>这是区分<strong>普通工程师和高级工程师</strong>的核心问题之一。</p>

<p><em>This is one of the core questions that distinguishes senior from junior engineers.</em></p>

<p>初级工程师：等待任务分配，发现问题后上报。</p>
<p><em>Junior: Waits for tasks, escalates problems when found.</em></p>

<p>高级工程师：主动监控系统健康，提前发现隐患，悄悄修好。</p>
<p><em>Senior: Proactively monitors system health, finds issues before they explode, quietly fixes them.</em></p>

<p>面试官想听到的信号：<strong>主动性、系统思维、影响力量化</strong>。</p>
<p><em>What interviewers want: proactivity, systems thinking, quantified impact.</em></p>

<hr/>

<h2>STAR 拆解 / STAR Breakdown</h2>

<h3>✅ 强回答结构 / Strong Answer Structure</h3>

<p><strong>Situation（情境）:</strong></p>
<p>&gt; &quot;在我们的支付服务中，我在做例行代码审查时注意到一个看起来没问题但实际上很危险的模式——一个在高并发场景下会导致重复扣款的竞态条件。&quot;</p>
<p>&gt;</p>
<p>&gt; <em>&quot;While doing a routine code review of our payment service, I noticed a pattern that looked fine but was actually dangerous — a race condition that would cause duplicate charges under high concurrency.&quot;</em></p>

<p><strong>Task（任务）:</strong></p>
<p>&gt; &quot;没人发现这个问题，线上也没有报警。但我知道如果不处理，在双十一这样的高峰期必然会触发。&quot;</p>
<p>&gt;</p>
<p>&gt; <em>&quot;No one had flagged it, and there were no alerts. But I knew it would absolutely trigger during peak traffic like Black Friday.&quot;</em></p>

<p><strong>Action（行动）:</strong></p>
<p>&gt; &quot;我先写了一个复现脚本，用 k6 模拟并发请求证明了问题存在；然后提出了三种修复方案，评估了各自的性能影响；和 PM 沟通了延迟一个小功能发布来优先修复；最后用数据库级别的幂等锁解决了问题。&quot;</p>
<p>&gt;</p>
<p>&gt; <em>&quot;I wrote a reproduction script using k6 to prove the bug. Then I proposed 3 fix options with their performance tradeoffs, aligned with the PM to delay a minor feature, and fixed it with database-level idempotency locks.&quot;</em></p>

<p><strong>Result（结果）:</strong></p>
<p>&gt; &quot;两周后的峰值流量中，有记录显示有 847 次请求命中了我们的幂等保护。估算避免了 $15K 的退款损失和潜在的支付合规问题。&quot;</p>
<p>&gt;</p>
<p>&gt; <em>&quot;Two weeks later during peak traffic, we recorded 847 requests hitting our idempotency guard. Estimated $15K in prevented chargebacks and potential compliance issues.&quot;</em></p>

<hr/>

<h2>❌ Bad vs ✅ Good</h2>

<p><strong>❌ 弱回答 / Weak:</strong></p>
<p>&gt; &quot;我发现了一个 bug，报告给了我的经理，他们修复了它。&quot;</p>
<p>&gt;</p>
<p>&gt; <em>&quot;I found a bug and reported it to my manager and they fixed it.&quot;</em></p>
<p>→ 没有主动性，没有影响，这是被动行为。</p>
<p><em>No ownership, no impact, this is reactive not proactive.</em></p>

<p><strong>✅ 强回答 / Strong:</strong></p>
<p>&gt; 展示：你如何<strong>主动发现</strong>（不是被告知）→ <strong>量化潜在风险</strong> → <strong>独立推进修复</strong> → <strong>数字化影响</strong></p>
<p>&gt;</p>
<p>&gt; <em>Show: how you proactively discovered (not told) → quantified potential risk → independently drove the fix → measured impact</em></p>

<hr/>

<h2>高级/Staff 的进阶 / Senior/Staff Level Tips</h2>

<p>🔥 <strong>系统性主动 vs 偶发性主动:</strong></p>

<p>普通的&quot;主动&quot;是偶然发现问题。Staff 级别会建立<strong>系统</strong>：</p>
<p>- 定期审查监控告警覆盖率</p>
<p>- 建立技术债 backlog 并推动季度 review</p>
<p>- 主导 Game Day / Chaos Engineering 主动暴露隐患</p>

<p><em>Average proactiveness is accidental. Staff-level proactiveness is systematic: quarterly tech debt reviews, monitoring coverage audits, deliberate chaos engineering.</em></p>

<p>🔥 <strong>提前沟通风险:</strong></p>

<p>找到问题后，不只是&quot;修了&quot;，而是<strong>向上同步风险评估</strong>和修复进度，让决策者知情。</p>

<p><em>Don&#x27;t just fix silently — sync up risk assessment and fix progress with stakeholders. Make decisions visible.</em></p>

<hr/>

<h2>Key Takeaways</h2>

<p>1. 🔍 <strong>主动发现</strong> — 描述你如何发现（代码审查、监控、读日志、直觉）</p>
<p>2. 📊 <strong>量化风险</strong> — &quot;如果不修，会有 X 影响&quot;比&quot;我觉得有问题&quot;有力 10 倍</p>
<p>3. ⚙️ <strong>独立推进</strong> — 展示你能端到端推动，不依赖他人催促</p>
<p>4. 📈 <strong>结果数字化</strong> — 预防的损失 &gt; 修复的技术细节</p>

<hr/>

<h2>📚 References</h2>

<p>1. [Staff Engineer: Leadership beyond the management track — Will Larson](https://staffeng.com/book)</p>
<p>2. [Google SRE Book — Chapter on Monitoring and Alerting](https://sre.google/sre-book/monitoring-distributed-systems/)</p>
<p>3. [The STAR Method for behavioral interviews — Indeed](https://www.indeed.com/career-advice/interviewing/how-to-use-the-star-interview-response-technique)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>就像你在玩游戏，别人都在打怪，但你提前发现了地图上有个陷阱，在队友掉坑之前就绕过去了，还告诉大家这里有坑。这就是主动性！</p>

<p><em>It&#x27;s like being in a game where everyone&#x27;s fighting monsters, but you spotted a hidden trap on the map. You avoided it before your teammates fell in — and told everyone it was there. That&#x27;s proactiveness!</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 10 / Frontend Day 10</h2>
<p><strong>Topic: React useEffect — Side Effects &amp; Cleanup</strong></p>
<p><em>预计阅读时间 / Estimated reading time: 2 minutes</em></p>

<hr/>

<h2>真实场景 / Real Scenario</h2>

<p>你在做一个 <strong>实时股票 dashboard</strong>，需要：</p>
<p>1. 组件加载时订阅 WebSocket 数据流</p>
<p>2. 组件卸载时取消订阅（否则内存泄漏！）</p>
<p>3. 当股票代码（ticker）改变时，切换订阅</p>

<p><em>You&#x27;re building a real-time stock dashboard. You need to:</em></p>
<p>1. <em>Subscribe to WebSocket stream when component mounts</em></p>
<p>2. <em>Unsubscribe when component unmounts (or: memory leak!)</em></p>
<p>3. <em>Switch subscriptions when the ticker changes</em></p>

<p>这就是 <code>useEffect</code> 的经典使用场景。</p>

<hr/>

<h2>代码示例 / Code Example</h2>

<pre><code>
import { useEffect, useState } from &#x27;react&#x27;;

interface StockData {
  price: number;
  change: number;
}

function StockTicker({ symbol }: { symbol: string }) {
  const [data, setData] = useState&lt;StockData | null&gt;(null);

  useEffect(() =&gt; {
    // 1. Setup: runs after render
    console.log(`Subscribing to ${symbol}`);
    const ws = new WebSocket(`wss://stocks.example.com/${symbol}`);
    
    ws.onmessage = (event) =&gt; {
      setData(JSON.parse(event.data));
    };

    // 2. Cleanup: runs before next effect OR on unmount
    return () =&gt; {
      console.log(`Unsubscribing from ${symbol}`);
      ws.close(); // ← THIS IS CRITICAL
    };
  }, [symbol]); // 3. Dependency array: re-run when symbol changes

  if (!data) return &lt;div&gt;Loading...&lt;/div&gt;;
  return &lt;div&gt;{symbol}: ${data.price} ({data.change}%)&lt;/div&gt;;
}
</code></pre>

<hr/>

<h2>猜猜输出？ / What&#x27;s the output order?</h2>

<p><strong>场景：</strong> symbol 从 <code>&quot;AAPL&quot;</code> 改为 <code>&quot;GOOG&quot;</code></p>

<pre><code>
A) &quot;Subscribing to GOOG&quot;
B) &quot;Unsubscribing from AAPL&quot; → &quot;Subscribing to GOOG&quot;
C) &quot;Subscribing to GOOG&quot; → &quot;Unsubscribing from AAPL&quot;
D) 什么都不打印
</code></pre>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>答案是 B</strong> — <code>&quot;Unsubscribing from AAPL&quot;</code> 先打印，然后 <code>&quot;Subscribing to GOOG&quot;</code></p>

<p>React 的执行顺序：</p>
<p>1. <code>symbol</code> prop 变化 → re-render</p>
<p>2. React 运行<strong>上一个 effect 的 cleanup</strong>（关闭旧 WebSocket）</p>
<p>3. React 运行<strong>新的 effect</strong>（打开新 WebSocket）</p>

<p>这就是为什么 cleanup 在 return 里：React 会在正确时机调用它。</p>

<p>&lt;/details&gt;</p>

<hr/>

<h2>❌ 常见错误 vs ✅ 正确做法</h2>

<p><strong>❌ 忘记清理 / Forgetting cleanup:</strong></p>
<pre><code>
useEffect(() =&gt; {
  const interval = setInterval(fetchData, 1000);
  // ❌ No cleanup! Interval runs forever after unmount
}, []);
</code></pre>

<p><strong>✅ 正确清理 / Always clean up:</strong></p>
<pre><code>
useEffect(() =&gt; {
  const interval = setInterval(fetchData, 1000);
  return () =&gt; clearInterval(interval); // ✅
}, []);
</code></pre>

<p><strong>❌ 依赖项缺失 / Missing dependencies:</strong></p>
<pre><code>
useEffect(() =&gt; {
  fetchUser(userId); // ❌ userId used but not in deps
}, []); // stale closure! always fetches original userId
</code></pre>

<p><strong>✅ 正确依赖 / Correct dependencies:</strong></p>
<pre><code>
useEffect(() =&gt; {
  fetchUser(userId); // ✅
}, [userId]); // re-runs whenever userId changes
</code></pre>

<hr/>

<h2>三种 useEffect 形态 / Three Patterns</h2>

<pre><code>
// 1. Run ONCE on mount (componentDidMount equivalent)
useEffect(() =&gt; {
  initAnalytics();
  return () =&gt; cleanup(); // runs on unmount
}, []); // empty deps = run once

// 2. Run on every render (rarely needed)
useEffect(() =&gt; {
  document.title = `Count: ${count}`;
}); // no deps array = every render

// 3. Run when specific values change
useEffect(() =&gt; {
  fetchUserProfile(userId);
}, [userId]); // run when userId changes
</code></pre>

<hr/>

<h2>何时用 / 何时不用 / When to Use / When NOT to</h2>

<p><strong>✅ 适合用 useEffect:</strong></p>
<p>- API 数据获取（推荐用 React Query/SWR 封装）</p>
<p>- WebSocket / 事件监听器</p>
<p>- 第三方库集成（地图、图表）</p>
<p>- 浏览器 API（localStorage, document.title）</p>

<p><strong>❌ 不要用 useEffect:</strong></p>
<p>- 派生状态（用 <code>useMemo</code> 代替）</p>
<p>- 事件处理（直接用 event handler）</p>
<p>- 在 render 期间的数据转换（直接在组件里算）</p>

<p><strong>React 团队的建议 / React Team&#x27;s Take:</strong></p>
<p>&gt; &quot;You might not need an Effect&quot; — 很多 useEffect 可以被消除。</p>
<p>&gt; <em>Many Effects can be eliminated. Think twice before reaching for it.</em></p>

<hr/>

<h2>📚 References</h2>

<p>1. [React Docs: Synchronizing with Effects](https://react.dev/learn/synchronizing-with-effects)</p>
<p>2. [React Docs: You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect)</p>
<p>3. [Dan Abramov: A Complete Guide to useEffect](https://overreacted.io/a-complete-guide-to-useeffect/)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p><code>useEffect</code> 就像给房间装了一个&quot;进房间就开灯、出房间就关灯&quot;的传感器。你进去（组件挂载），灯亮了；你出来（组件卸载），灯自动灭。如果你换了房间（依赖变了），它先把旧房间的灯关掉，再把新房间的灯打开。</p>

<p><em>useEffect is like a room sensor that turns the light on when you enter and off when you leave. When you switch rooms (deps change), it turns off the old light before turning on the new one.</em></p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 11 — News Roundup</h2>
<p><em>2026年3月26日 / March 26, 2026</em></p>
<p><em>预计阅读时间 / Estimated reading time: 2 minutes</em></p>

<hr/>

<h2>📰 本周 AI 大事件 / This Week in AI</h2>

<p><em>Sources: Web search results from March 2026</em></p>

<hr/>

<h3>1. 🚀 OpenAI 发布 GPT-5.4 — AI&quot;数字同事&quot;时代来临</h3>

<p><strong>来源 / Source:</strong> [riskinfo.ai](https://www.riskinfo.ai/post/ai-insights-key-global-developments-in-march-2026) | [juliangoldie.co.uk](https://juliangoldie.co.uk/ai-news-march-2026/)</p>

<p>GPT-5.4 于 3 月 5 日正式发布，最大亮点是<strong>原生计算机使用能力（Computer Use）</strong>——AI 可以直接操作真实软件环境（Excel、文档、网页），而不只是生成文字。在法律文件 benchmark 上达到 91% 准确率。</p>

<p><em>GPT-5.4 launched March 5 with native computer-use capabilities — AI can now directly interact with real software environments like spreadsheets and documents, moving toward a &quot;digital co-worker&quot; role. Achieved 91% on a legal-document benchmark.</em></p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>作为工程师，这意味着 AI agent 正在从&quot;问答工具&quot;变成&quot;能自主操作 GUI 的同事&quot;。未来的 AI 代码助手可能直接在你的 IDE 里操作文件、跑测试、提 PR——而不只是给出建议。</p>

<hr/>

<h3>2. 🛡️ Anthropic 拒绝军事合同，被列为&quot;供应链风险&quot;</h3>

<p><strong>来源 / Source:</strong> [radicaldatascience.wordpress.com](https://radicaldatascience.wordpress.com/2026/03/20/ai-news-briefs-bulletin-board-for-march-2026/)</p>

<p>美国国防部要求 Anthropic 移除 Claude 的安全护栏（禁止自主武器使用），Anthropic 拒绝后被 DoD 列为&quot;供应链风险&quot;。这是 AI 安全与国家安全之间最直接的冲突之一。</p>

<p><em>The US DoD designated Anthropic as a &quot;supply chain risk&quot; after the company refused to remove safety guardrails prohibiting Claude&#x27;s use in autonomous weaponry.</em></p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>AI 公司的价值观选择正在产生真实商业后果。这场博弈将塑造未来 AI 系统的&quot;红线&quot;在哪里划定。</p>

<hr/>

<h3>3. 🎮 NVIDIA GTC 2026：AI 工厂 + 边缘计算引领下一波</h3>

<p><strong>来源 / Source:</strong> [nvidianews.nvidia.com](https://nvidianews.nvidia.com/news/nvidia-and-emerald-ai-join-leading-energy-companies-to-pioneer-flexible-ai-factories-as-grid-assets) | [vtnetzwelt.com](https://www.vtnetzwelt.com/ai-development/latest-ai-technology-news-roundup-march-2026/)</p>

<p>NVIDIA 在 3 月 16 日 GTC 大会上主推&quot;AI 工厂&quot;概念：AI 算力中心既能生产 AI tokens，又能作为<strong>灵活电网资产</strong>调节用电。同时发布 Nemotron 3 Super，专为复杂 agentic 系统设计。</p>

<p><em>NVIDIA&#x27;s GTC 2026 highlighted &quot;AI factories&quot; — compute centers that generate AI tokens AND act as flexible grid assets. Nemotron 3 Super targets complex agentic AI workflows.</em></p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>AI 基础设施成本是行业最大变量之一。AI 工厂与能源网格整合，可能显著降低运算成本，从而让更多 AI 功能变得可行。</p>

<hr/>

<h3>4. 📊 76% 的企业：准备好迎接 AI Agent 了吗？还没有</h3>

<p><strong>来源 / Source:</strong> [ey.com](https://www.ey.com/en_gl/newsroom/2026/03/ey-survey-autonomous-ai-is-no-longer-theoretical-as-adoption-grows-despite-ongoing-trust-concerns)</p>

<p>EY 2026 AI Sentiment Report：76% 的企业承认<strong>运营流程还没准备好</strong>支持 agentic AI。最大障碍是：缺少结构化工作流、上下文传递不清晰、以及信任问题。</p>

<p><em>EY&#x27;s 2026 AI Sentiment Report: 76% of enterprises admit their operations are not yet ready to support agentic AI. Main blockers: unstructured workflows, unclear context handoffs, and trust gaps.</em></p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>这直接影响你作为工程师的工作重点。未来 1-3 年最有价值的技能：<strong>设计能与 AI Agent 协作的系统架构</strong>——清晰的 API 接口、可审计的工作流、幂等操作。</p>

<hr/>

<h2>🔗 本周延伸阅读 / Further Reading</h2>

<p>- [AI Insights: Key Global Developments March 2026](https://www.riskinfo.ai/post/ai-insights-key-global-developments-in-march-2026)</p>
<p>- [NVIDIA AI Factories press release](https://nvidianews.nvidia.com/news/nvidia-and-emerald-ai-join-leading-energy-companies-to-pioneer-flexible-ai-factories-as-grid-assets)</p>
<p>- [EY 2026 AI Sentiment Report](https://www.ey.com/en_gl/newsroom/2026/03/ey-survey-autonomous-ai-is-no-longer-theoretical-as-adoption-grows-despite-ongoing-trust-concerns)</p>

<hr/>

<h2>🧒 ELI5</h2>

<p>AI 这周的新闻就像：有人造了一个超级厉害的机器人助手，会直接帮你操电脑；有家公司不愿意把机器人改成&quot;可以打仗&quot;的，结果被政府不喜欢了；还有调查说大多数公司虽然想用 AI 助手，但家里还没收拾好迎接它。</p>

<p><em>This week in AI: a super robot that can actually USE your computer arrived; one company refused to let their AI be used as a weapon and got in trouble for it; and a survey found most companies want AI workers but haven&#x27;t cleaned their house yet.</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-25</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-25</guid>
      <pubDate>Wed, 25 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>Review</h1>
<h2>🔄 复习日 Day 10 / Review Day 10</h2>

<p><strong>Date:</strong> 2026-03-25 | <strong>Phase:</strong> Foundation</p>

<p>今天是复习日！回顾第 6-9 天的内容。</p>
<p>Today is Review Day! Looking back at Days 6–9.</p>

<hr/>

<p>📊 <strong>回顾范围 / Review Scope (Days 6–9):</strong></p>
<p>- 🏗️ Caching Strategies · Database Types · DB Indexing · DB Replication &amp; Sharding</p>
<p>- 💻 Top K Frequent Elements · Product of Array Except Self · Valid Sudoku · Valid Palindrome</p>
<p>- 🗣️ Balancing priorities · Simplifying complex systems · Ambiguous requirements · Pushing back on features</p>
<p>- 🎨 CSS Specificity · Positioning · Animations &amp; Transitions · React useState</p>
<p>- 🤖 Embeddings · Training vs Fine-Tuning vs Prompting</p>

<hr/>

<h2>📝 Quick Quiz — 3 Mini-Reviews</h2>

<h3>**Q1: [🏗️ System Design] Caching + Replication**</h3>

<p>你在设计一个读多写少的社交平台（Read-heavy social feed）。你决定同时使用 <strong>缓存</strong> 和 <strong>数据库只读副本（Read Replicas）</strong>。</p>

<p>You&#x27;re designing a read-heavy social feed. You use both <strong>caching</strong> and <strong>read replicas</strong>.</p>

<p><strong>问题 / Question:</strong> 这两者的职责分工是什么？什么情况下读副本仍然不够，你必须依赖缓存？</p>

<p><em>What is the distinct role of each? When are read replicas still insufficient and you MUST rely on the cache?</em></p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>Read replicas</strong> 通过分散读请求到多个数据库副本来提升吞吐量，但每次请求仍然执行完整的 SQL 查询，延迟在毫秒级别。<strong>缓存</strong>（Redis/Memcached）则把热点数据存在内存里，延迟在微秒级别，且完全绕开了数据库。</p>

<p>Read replicas scale <em>throughput</em> by distributing SQL queries across copies, but every request still hits disk and parses SQL — latency stays in the millisecond range. A <strong>cache</strong> holds hot data in RAM (microsecond latency) and bypasses the database entirely.</p>

<p><strong>什么时候必须用缓存 / When you MUST cache:</strong></p>
<p>1. 热点数据（Hotspot / Celebrity problem）：单个用户/item 被疯狂读取，单个副本也扛不住</p>
<p>2. 计算昂贵的结果（Aggregations, ranked feeds）：不想每次都重新算</p>
<p>3. 外部 API 响应缓存：副本根本不存这些数据</p>

<p><strong>关键洞察:</strong> Read replica = 水平扩展数据库。Cache = 彻底逃离数据库。两者互补，不是替代关系。</p>

<p>&lt;/details&gt;</p>

<hr/>

<h3>**Q2: [💻 Algorithms] Product of Array Except Self**</h3>

<p>给定数组 <code>[1, 2, 3, 4]</code>，Product of Array Except Self 要求返回 <code>[24, 12, 8, 6]</code>——即每个位置是除自身以外所有元素的乘积。</p>

<p>Given <code>[1, 2, 3, 4]</code>, return <code>[24, 12, 8, 6]</code> — each position is the product of all other elements.</p>

<p><strong>问题 / Question:</strong> 不使用除法、O(n) 时间、O(1) 额外空间（输出数组不算）怎么做？解释 <strong>prefix product + suffix product</strong> 的思路。</p>

<p><em>Without division, O(n) time, O(1) extra space — explain the prefix × suffix approach.</em></p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>核心思路 / Core idea:</strong> 位置 <code>i</code> 的答案 = <code>i</code> 左边所有数的乘积 × <code>i</code> 右边所有数的乘积。</p>

<pre><code>
Array:   [1,  2,  3,  4]
Prefix:  [1,  1,  2,  6]   # prefix[i] = product of all elements to the LEFT of i
Suffix:  [24, 12, 4,  1]   # suffix[i] = product of all elements to the RIGHT of i
Result:  [24, 12, 8,  6]   # prefix[i] * suffix[i]
</code></pre>

<p><strong>两趟扫描 / Two-pass O(1) space:</strong></p>
<p>- Pass 1 (left→right): 用输出数组存 prefix product</p>
<p>- Pass 2 (right→left): 用一个变量 <code>suffix</code> 滚动累乘，直接乘进输出数组</p>

<pre><code>
def productExceptSelf(nums):
    n = len(nums)
    res = [1] * n
    # Pass 1: res[i] = product of everything to the LEFT
    for i in range(1, n):
        res[i] = res[i-1] * nums[i-1]
    # Pass 2: multiply in suffix product on the fly
    suffix = 1
    for i in range(n-1, -1, -1):
        res[i] *= suffix
        suffix *= nums[i]
    return res
</code></pre>

<p><strong>为什么不用除法？</strong> 因为数组可能含 0，除法会出错（0/0）。</p>

<p><strong>举一反三:</strong> 这个 &quot;prefix + suffix scan&quot; 模式在 <strong>Trapping Rain Water</strong> 中也用到了——左边最大值 × 右边最大值决定每格存水量。</p>

<p>&lt;/details&gt;</p>

<hr/>

<h3>**Q3: [🎨 Frontend] React useState**</h3>

<p>看这段代码：</p>

<pre><code>
function Counter() {
  const [count, setCount] = React.useState(0);

  const handleClick = () =&gt; {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  };

  return &lt;button onClick={handleClick}&gt;Count: {count}&lt;/button&gt;;
}
</code></pre>

<p><strong>问题 / Question:</strong> 点击一次按钮后，count 变成几？为什么？如果想让 count 每次点击增加 3，怎么修改？</p>

<p><em>After one click, what is count? Why? How do you fix it to actually add 3?</em></p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>答案是 1，不是 3。</strong></p>

<p><strong>原因 / Why:</strong> React 在同一个事件处理函数中会<strong>批量处理（batch）</strong> setState 调用。<code>count</code> 在整个 <code>handleClick</code> 执行期间都是 <strong>快照值（stale closure）</strong>，始终是 <code>0</code>。所以三次 <code>setCount(0 + 1)</code> 其实是三次设置同一个值 <code>1</code>。</p>

<p><strong>如何修复 / Fix — 使用函数式更新（functional update form）:</strong></p>

<pre><code>
const handleClick = () =&gt; {
  setCount(prev =&gt; prev + 1);  // prev = 0 → 1
  setCount(prev =&gt; prev + 1);  // prev = 1 → 2
  setCount(prev =&gt; prev + 1);  // prev = 2 → 3
};
</code></pre>

<p>传入函数时，React 会把<strong>最新的 state 值</strong>作为参数传入，而不是用闭包里的快照。</p>

<p><strong>黄金法则 / Golden Rule:</strong> 当新 state 依赖旧 state 时，<strong>永远</strong>用 <code>setX(prev =&gt; ...)</code> 形式，而不是 <code>setX(x + 1)</code>。这在并发模式（React 18+ Concurrent Features）下尤其重要。</p>

<p>&lt;/details&gt;</p>

<hr/>

<p>💡 <em>复习巩固记忆，螺旋式上升。每次复习都是在加深神经连接。</em></p>
<p><em>Review strengthens memory through spaced repetition — you&#x27;re literally deepening neural pathways each time.</em></p>

<p>📅 <strong>明天继续新内容！Day 11 starts tomorrow!</strong></p>

<hr/>

<p><em>Generated: 2026-03-25 | byte-by-byte Day 10 Review</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-24</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-24</guid>
      <pubDate>Tue, 24 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 9 / System Design Day 9</h2>

<p><strong>主题 / Topic:</strong> 数据库复制与分片 / Database Replication &amp; Sharding</p>

<hr/>

<h2>🌏 真实场景 / Real-World Scenario</h2>

<p>想象你在设计一个像微信读书或 Goodreads 的阅读应用——用户突破 5000 万，每天产生几亿条阅读记录、笔记和评论。单一数据库服务器已经撑不住了：写操作堵住读操作，单点故障导致整个 App 不可用，数据量超出单机磁盘上限。</p>

<p>你需要两把利器：<strong>复制（Replication）</strong>解决可用性和读性能，<strong>分片（Sharding）</strong>解决写性能和存储规模。</p>

<p>Imagine you&#x27;re designing a reading app like Goodreads at 50M users, with hundreds of millions of reading records daily. A single database server buckles under load. You need <strong>Replication</strong> for availability &amp; read scale, and <strong>Sharding</strong> for write scale &amp; storage capacity.</p>

<hr/>

<h2>🏛️ 架构图 / Architecture Diagram</h2>

<pre><code>
┌─────────────────────────────────────────────────────────┐
│                    应用服务层 / App Layer                │
│         [API Server 1] [API Server 2] [API Server 3]    │
└─────────┬──────────────────────────┬────────────────────┘
          │ Writes                    │ Reads
          ▼                           ▼
┌─────────────────┐        ┌────────────────────────┐
│   Primary DB    │──────► │  Read Replica 1        │
│ (Leader/Master) │──────► │  Read Replica 2        │
│                 │──────► │  Read Replica 3        │
└────────┬────────┘        └────────────────────────┘
         │ Replication Log (WAL / Binlog)
         │
         ▼  [After Replication → Add Sharding]
┌────────────────────────────────────────────────────┐
│                  Shard Router / Proxy              │
│           (e.g. Vitess, ProxySQL, PgBouncer)       │
└────┬──────────────────┬───────────────────┬────────┘
     │                  │                   │
     ▼                  ▼                   ▼
┌─────────┐       ┌─────────┐        ┌─────────┐
│ Shard 0 │       │ Shard 1 │        │ Shard 2 │
│user 0-33M│      │user33-66M│       │user66M+ │
│+Replicas│       │+Replicas│        │+Replicas│
└─────────┘       └─────────┘        └─────────┘
</code></pre>

<hr/>

<h2>⚖️ 关键权衡 / Key Tradeoffs</h2>

<h3>复制 / Replication</h3>

<p>| 方案 | 优点 | 缺点 |</p>
<p>|------|------|------|</p>
<p>| <strong>同步复制</strong> | 强一致性，不丢数据 | 写延迟高（等所有副本确认） |</p>
<p>| <strong>异步复制</strong> | 写延迟低，吞吐高 | 副本可能有延迟（replication lag） |</p>
<p>| <strong>半同步</strong> | 折中：至少 1 个副本确认 | 稍高写延迟，部分一致性 |</p>

<p><strong>为什么这样设计？</strong></p>
<p>- 读多写少的业务（如阅读记录）：异步复制 + 多读副本，读吞吐可水平扩展</p>
<p>- 金融、支付场景：同步复制或 Raft/Paxos 保证强一致</p>

<h3>分片 / Sharding</h3>

<p>| 策略 | 原理 | 适合场景 |</p>
<p>|------|------|------|</p>
<p>| <strong>Range Sharding</strong> | 按 user_id 范围切分 | 范围查询友好，但热点风险高 |</p>
<p>| <strong>Hash Sharding</strong> | <code>shard = hash(user_id) % N</code> | 均匀分布，但范围查询跨 shard |</p>
<p>| <strong>Directory Sharding</strong> | 查表确定归属 shard | 灵活，但查表本身是瓶颈 |</p>

<hr/>

<h2>🚫 常见坑 / Common Mistakes</h2>

<p><strong>坑 1：过早分片</strong></p>
<p>&gt; 分片大幅增加系统复杂度。复制 + 读副本能抗住大多数流量，先用它，真正撑不住再分片。</p>

<p><strong>坑 2：选错 Shard Key</strong></p>
<p>&gt; 按时间分片会导致最新 shard 永远是热点（写都打到最新月份）。按用户 ID hash 分片更均匀。</p>

<p><strong>坑 3：跨 Shard 事务</strong></p>
<p>&gt; 分布式事务极复杂。设计 schema 时尽量让同一用户的数据在同一 shard，避免跨 shard join。</p>

<p><strong>坑 4：忽略 Replication Lag</strong></p>
<p>&gt; 用户刚发评论，立刻刷新却看不到——因为读副本还没同步。对强一致性操作，读 Primary 或使用 read-your-writes 路由。</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>1. [AWS Database Replication — RDS Read Replicas](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html)</p>
<p>2. [Vitess — MySQL Sharding at YouTube Scale](https://vitess.io/docs/concepts/sharding/)</p>
<p>3. [Designing Data-Intensive Applications — Chapter 5 &amp; 6 (Kleppmann)](https://dataintensive.net/)</p>

<hr/>

<h2>🧒 ELI5 / 用小孩能理解的话说</h2>

<p><strong>复制</strong>就像把书抄写多份，放在不同图书馆。每个图书馆都能借给你看（读副本），但只有总馆能修改（Primary）。</p>

<p><strong>分片</strong>就像把全班同学的作业按学号分给 3 个老师批改——不再一个老师批所有作业，每个老师只负责一段。</p>

<p><strong>Replication</strong> = Make copies of the book so more people can read at once.</p>
<p><strong>Sharding</strong> = Split the library into sections so no single librarian is overwhelmed.</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 10 / Algorithms Day 10</h2>

<p><strong>#125 Valid Palindrome (Easy)</strong> — 双指针模式 / Two Pointers</p>

<hr/>

<h2>🧩 新模式 / New Pattern: 双指针模式 (Two Pointers)</h2>

<p>📍 这个模式块共 5 道题 / This block: 5 problems</p>

<p>| # | 题目 | 难度 |</p>
<p>|---|------|------|</p>
<p>| 1 | #125 Valid Palindrome ← <strong>今天 / TODAY</strong> | 🟢 Easy |</p>
<p>| 2 | #167 Two Sum II | 🟡 Medium |</p>
<p>| 3 | #15 3Sum | 🟡 Medium |</p>
<p>| 4 | #11 Container With Most Water | 🟡 Medium |</p>
<p>| 5 | #42 Trapping Rain Water | 🔴 Hard |</p>

<hr/>

<h3>什么时候用 / When to Use</h3>

<p>排序数组中找配对、回文检测、原地操作时，想到双指针。</p>

<p>Use Two Pointers when: sorted array + find a pair, palindrome detection, in-place removal, merging sorted arrays.</p>

<h3>识别信号 / Signals</h3>

<p>&gt; sorted array · find pair with sum · palindrome · remove in-place · merge sorted · container/water problems</p>

<h3>通用模版 / Template</h3>

<pre><code>
def two_pointer_template(arr, target):
    left, right = 0, len(arr) - 1
    
    while left &lt; right:
        current = arr[left] + arr[right]      # or some condition on left/right
        
        if current == target:
            return [left, right]              # found it
        elif current &lt; target:
            left += 1                         # need bigger value → move left pointer right
        else:
            right -= 1                        # need smaller value → move right pointer left
    
    return []                                 # not found
</code></pre>

<p><strong>核心洞察 / Key Insight:</strong> 排序 + 两端逼近，从 O(n²) 嵌套循环降到 O(n) 单次扫描。</p>
<p>Sorted order + converging from both ends → eliminates the need for nested loops.</p>

<hr/>

<h2>📖 今日题目 / Today&#x27;s Problem</h2>

<p>🔗 [LeetCode #125 — Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) 🟢 Easy</p>
<p>📹 [NeetCode 讲解](https://neetcode.io/problems/is-palindrome)</p>

<hr/>

<h3>🌍 现实类比 / Real-World Analogy</h3>

<p>想象你是一个质检员，要验证一条传送带上的字符串&quot;从两头读是否一样&quot;。你派两个检查员分别站在传送带两端，同时向中间走，每步对比字母（跳过非字母数字的字符）。两人相遇时没有发现不同，就通过！</p>

<p>Think of two inspectors walking from both ends of a conveyor belt toward the middle, each checking only alphanumeric items and skipping punctuation/spaces.</p>

<hr/>

<h3>🧩 如何映射到模版 / Mapping to Template</h3>

<p>经典双指针，但有两个变化：</p>
<p>1. <strong>不是排序数组</strong>——我们用双指针做&quot;对比&quot;而不是&quot;求和&quot;</p>
<p>2. <strong>需要跳过非字母数字字符</strong>——在移动指针前先跳过无效字符</p>

<p>Classic Two Pointers, with two modifications:</p>
<p>1. No sorted array → use pointers for <strong>comparison</strong>, not sum-seeking</p>
<p>2. Skip non-alphanumeric chars before comparing</p>

<pre><code>
def isPalindrome(s: str) -&gt; bool:
    left, right = 0, len(s) - 1
    
    while left &lt; right:
        # Skip non-alphanumeric from the left
        while left &lt; right and not s[left].isalnum():
            left += 1
        # Skip non-alphanumeric from the right
        while left &lt; right and not s[right].isalnum():
            right -= 1
        
        # Compare (case-insensitive)
        if s[left].lower() != s[right].lower():
            return False
        
        left += 1
        right -= 1
    
    return True
</code></pre>

<hr/>

<h3>🔍 代码追踪 / Code Trace</h3>

<p>Input: <code>&quot;A man, a plan, a canal: Panama&quot;</code></p>

<pre><code>
left=0  right=29  → &#x27;A&#x27; vs &#x27;a&#x27; → match  → left=1,  right=28
left=1  right=28  → skip &#x27; &#x27;   → left=2
left=2  right=28  → &#x27;m&#x27; vs &#x27;m&#x27; → match  → left=3,  right=27
left=3  right=27  → &#x27;a&#x27; vs &#x27;a&#x27; → match  → left=4,  right=26
left=4  right=26  → &#x27;n&#x27; vs &#x27;n&#x27; → match  → ...
...
→ All chars match → return True ✅
</code></pre>

<p>Input: <code>&quot;race a car&quot;</code></p>

<pre><code>
left=0  right=9   → &#x27;r&#x27; vs &#x27;r&#x27; → match
left=1  right=8   → &#x27;a&#x27; vs &#x27;a&#x27; → match
left=2  right=7   → &#x27;c&#x27; vs &#x27;c&#x27; → match
left=3  right=6   → &#x27;e&#x27; vs &#x27;a&#x27; → ❌ MISMATCH → return False
</code></pre>

<hr/>

<h3>📊 复杂度 / Complexity</h3>

<p>| | Time | Space |</p>
<p>|---|------|-------|</p>
<p>| Two Pointer | <strong>O(n)</strong> | <strong>O(1)</strong> |</p>
<p>| Built-in reverse | O(n) | O(n) — creates new string |</p>

<p><strong>Space O(1) is the win here</strong> — we never create a cleaned copy of the string.</p>

<hr/>

<h3>🔄 举一反三 / Pattern Connections</h3>

<p>这道题是双指针的&quot;热身&quot;——纯粹的左右逼近。接下来的题目会在这个基础上加难度：</p>

<p>| 题目 | 变化 | 核心差异 |</p>
<p>|------|------|---------|</p>
<p>| <strong>#167 Two Sum II</strong> | 有序数组找和 | 移动指针基于 sum vs target |</p>
<p>| <strong>#15 3Sum</strong> | 三数之和 | 固定一个数 + 双指针找剩余两个 |</p>
<p>| <strong>#11 Container With Most Water</strong> | 面积最大化 | 移动较短的那边指针 |</p>
<p>| <strong>#42 Trapping Rain Water</strong> | 复杂水位计算 | 双指针维护左右最大高度 |</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>1. [LeetCode #125 — Valid Palindrome](https://leetcode.com/problems/valid-palindrome/)</p>
<p>2. [NeetCode — Two Pointers Pattern](https://neetcode.io/roadmap)</p>
<p>3. [Python str.isalnum() docs](https://docs.python.org/3/library/stdtypes.html#str.isalnum)</p>

<hr/>

<h2>🧒 ELI5 / 用小孩能理解的话说</h2>

<p>回文就像照镜子——左边和右边要一样。我们用两只手，一只从左摸，一只从右摸，跳过空格和标点，对比每个字母。如果两只手中间相遇了都没发现不同，就是回文！</p>

<p>A palindrome is like a mirror — left side = right side. We use two fingers, one from each end, skip spaces/punctuation, compare each letter. If both fingers meet in the middle without finding a mismatch → palindrome!</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 9 / Soft Skills Day 9</h2>

<p><strong>主题 / Topic:</strong> 利益相关方管理 / Stakeholder Management</p>
<p><strong>问题 / Question:</strong> Describe a time you had to push back on a feature or requirement. Why?</p>

<hr/>

<h2>💡 为什么这道题很重要 / Why This Matters</h2>

<p>在高级工程师面试中，面试官不只想知道你&quot;会写代码&quot;——他们想知道你能不能独立判断、有没有勇气说出&quot;这个需求有问题&quot;。盲目执行坏需求是初级工程师的行为；能够用数据和逻辑推动正确方向，是 Senior/Staff 的核心能力。</p>

<p>Interviewers want to know you&#x27;re not just a &quot;feature factory.&quot; Senior engineers own outcomes, not just outputs. Pushing back constructively — with data, not attitude — is a core competency at L5+.</p>

<hr/>

<h2>⭐ STAR 拆解 / STAR Breakdown</h2>

<h3>Situation（情境）</h3>
<p>&gt; 设置背景：什么团队？什么项目阶段？紧迫程度？</p>

<p>&quot;我们的 PM 要求在发布前两周新增一个实时用户追踪功能，当时系统负载已经接近上限。&quot;</p>

<p>&quot;Our PM requested a real-time user tracking feature two weeks before a major launch, when our system load was already near capacity.&quot;</p>

<h3>Task（任务）</h3>
<p>&gt; 你的职责是什么？你为什么有发言权？</p>

<p>&quot;作为负责后端基础设施的 Senior Engineer，我需要评估这个需求的可行性和风险。&quot;</p>

<p>&quot;As the Senior Engineer owning backend infra, I needed to assess feasibility and surface the technical risk.&quot;</p>

<h3>Action（行动）</h3>
<p>&gt; 这是核心！展示你如何<strong>有据可查地</strong>推回，而不是情绪化地拒绝。</p>

<p>1. <strong>量化风险：</strong> 我跑了负载测试，展示新功能会把 P99 延迟从 120ms 推高到 650ms</p>
<p>2. <strong>提出替代方案：</strong> 建议将实时追踪改为批量日志，延迟 24h 但不影响核心体验</p>
<p>3. <strong>对齐业务目标：</strong> 确认 PM 真正想要的是&quot;数据分析能力&quot;而不是&quot;实时性&quot;——批量方案完全满足</p>
<p>4. <strong>共识达成：</strong> 带着数据找 PM + 工程总监开了 30 分钟会议，最终采用我的方案</p>

<p>&quot;I ran load tests showing P99 latency would spike from 120ms to 650ms. I proposed batch logging instead — same data, 24h delay, zero performance impact. I aligned with PM on the real goal (analytics, not real-time), and brought data to a 30-min meeting with PM and Eng Director. We shipped the batch solution.&quot;</p>

<h3>Result（结果）</h3>
<p>&gt; 用数字说话。</p>

<p>&quot;发布如期进行，零性能事故。批量数据方案在发布后三个月上线，PM 反馈数据质量超出预期。这次经验也推动团队建立了需求评审中的技术可行性评估流程。&quot;</p>

<p>&quot;Launch shipped on time with zero incidents. The batch analytics shipped 3 months post-launch and exceeded data quality expectations. The experience led to establishing a technical feasibility step in our requirements review process.&quot;</p>

<hr/>

<h2>❌ 别这么说 / Bad vs ✅ 这么说 / Good</h2>

<p>| ❌ 踩坑 | ✅ 正解 |</p>
<p>|--------|--------|</p>
<p>| &quot;我直接告诉 PM 这个要求太蠢了&quot; | &quot;我跑了测试，把风险用数据量化&quot; |</p>
<p>| &quot;我认为这不重要，所以不做&quot; | &quot;我先理解他们的真实目标，再提替代方案&quot; |</p>
<p>| &quot;最终我没能阻止，还是做了&quot; | &quot;我确保所有决策者都了解风险，决策有据可查&quot; |</p>
<p>| &quot;我们就这么做了&quot; → 没有结果 | 说清楚结果：上线情况、用户影响、后续改进 |</p>

<hr/>

<h2>🚀 Senior/Staff 加分点 / Senior+ Tips</h2>

<p>1. <strong>系统化推回，而非感情化拒绝。</strong> 数据 &gt; 直觉。Load test、cost model、用户影响分析——让数字说话。</p>
<p>2. <strong>先理解&quot;为什么&quot;，再评估&quot;怎么做&quot;。</strong> 很多&quot;坏需求&quot;背后有合理的业务原因，找到根本目标才能提出真正有价值的替代方案。</p>
<p>3. <strong>建立信任储备。</strong> 平时 deliver 靠谱，关键时刻的推回才会被认真对待。</p>
<p>4. <strong>把决策过程文档化。</strong> 即使你没能推回成功，确保风险已被知晓和记录，保护自己也保护团队。</p>

<hr/>

<h2>🎯 Key Takeaways</h2>

<p>- 推回 ≠ 拒绝。推回 = 用专业判断守护产品质量。</p>
<p>- Push back = professional judgment, not obstruction.</p>
<p>- 永远带着数据和替代方案去谈，而不是空手说&quot;不行&quot;。</p>
<p>- Always come with data + alternatives, never just &quot;no.&quot;</p>
<p>- 好的推回最终是双赢：工程质量 + 业务目标都得到保护。</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>1. [The Engineering Manager&#x27;s Handbook — Pushing Back Effectively](https://www.engmanager.com/)</p>
<p>2. [Staff Engineer: Leadership Beyond the Management Track (Will Larson)](https://staffeng.com/book)</p>
<p>3. [How to Disagree Productively — First Round Review](https://review.firstround.com/how-to-disagree-productively-and-find-common-ground/)</p>

<hr/>

<h2>🧒 ELI5 / 用小孩能理解的话说</h2>

<p>如果你的朋友说&quot;我们现在去游泳吧&quot;，但你知道外面在下大雨，你不是直接说&quot;不去&quot;，而是说&quot;你想游泳吗？那我们去室内游泳池！&quot;——这就是有建设性的推回。</p>

<p>If a friend says &quot;let&#x27;s swim now!&quot; but it&#x27;s raining, you don&#x27;t just say &quot;no&quot; — you say &quot;want to swim? Let&#x27;s go to the indoor pool!&quot; That&#x27;s constructive pushback.</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 9 / Frontend Day 9</h2>

<p><strong>主题 / Topic:</strong> React <code>useState</code> — 触发重渲染的状态 / State That Triggers Re-renders</p>

<hr/>

<h2>🌏 真实场景 / Real Scenario</h2>

<p>你在做一个任务管理 dashboard，点击按钮需要切换&quot;显示已完成任务&quot;的筛选器。你需要一个变量来记住当前状态，而且每次改变时 UI 要自动更新。这就是 <code>useState</code> 的舞台。</p>

<p>You&#x27;re building a task dashboard. Clicking a button should toggle showing completed tasks. You need a variable that remembers its value AND automatically updates the UI when it changes. That&#x27;s <code>useState</code>.</p>

<hr/>

<h2>💻 代码示例 / Code Snippet</h2>

<pre><code>
import { useState } from &#x27;react&#x27;

interface Task {
  id: number
  title: string
  completed: boolean
}

function TaskDashboard() {
  // useState returns [current value, setter function]
  const [showCompleted, setShowCompleted] = useState(false)
  const [tasks] = useState&lt;Task[]&gt;([
    { id: 1, title: &#x27;Review PR&#x27;, completed: true },
    { id: 2, title: &#x27;Write tests&#x27;, completed: false },
    { id: 3, title: &#x27;Deploy staging&#x27;, completed: true },
  ])

  // Derived state — computed from existing state, no useState needed
  const visibleTasks = showCompleted
    ? tasks
    : tasks.filter(t =&gt; !t.completed)

  return (
    &lt;div&gt;
      &lt;button onClick={() =&gt; setShowCompleted(prev =&gt; !prev)}&gt;
        {showCompleted ? &#x27;Hide&#x27; : &#x27;Show&#x27;} Completed ({tasks.filter(t =&gt; t.completed).length})
      &lt;/button&gt;
      &lt;ul&gt;
        {visibleTasks.map(task =&gt; (
          &lt;li key={task.id} style={{ opacity: task.completed ? 0.5 : 1 }}&gt;
            {task.title}
          &lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}
</code></pre>

<hr/>

<h2>🧠 猜猜输出 / What Does This Output?</h2>

<pre><code>
function Counter() {
  const [count, setCount] = useState(0)
  
  const handleClick = () =&gt; {
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
  }
  
  return &lt;button onClick={handleClick}&gt;Count: {count}&lt;/button&gt;
}
</code></pre>

<p>点击一次后，count 是多少？/ After one click, what is <code>count</code>?</p>

<p><strong>A)</strong> 3 — 调用了三次 setCount</p>
<p><strong>B)</strong> 1 — React 批量处理，count 是快照</p>
<p><strong>C)</strong> 0 — setState 是异步的，还没更新</p>
<p><strong>D)</strong> 报错 — 不能在一个函数里多次调用 setCount</p>

<p>&lt;details&gt;&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>答案是 B — count = 1</strong></p>

<p>为什么？因为在同一个事件处理函数中，<code>count</code> 是一个<strong>快照（snapshot）</strong>，值固定为 <code>0</code>。三次 <code>setCount(0 + 1)</code> 都是 <code>setCount(1)</code>，最后只更新一次。</p>

<p>React batches state updates within the same event handler. <code>count</code> is a <strong>snapshot</strong> — it&#x27;s <code>0</code> throughout the whole function. All three calls are <code>setCount(0 + 1)</code> = <code>setCount(1)</code>. Result: 1.</p>

<p><strong>✅ 如果你想累加，用函数式更新 / Use functional updates for increments:</strong></p>
<pre><code>
setCount(prev =&gt; prev + 1) // ✅ prev is always latest value
setCount(prev =&gt; prev + 1) // ✅ prev = 1
setCount(prev =&gt; prev + 1) // ✅ prev = 2 → final count = 3
</code></pre>

<p>&lt;/details&gt;</p>

<hr/>

<h2>❌ 常见错误 / Common Mistakes</h2>

<h3>错误 1：直接修改 state 对象</h3>

<pre><code>
// ❌ WRONG — mutating state directly, React won&#x27;t re-render!
const [user, setUser] = useState({ name: &#x27;Alice&#x27;, age: 25 })
user.age = 26  // ← This doesn&#x27;t trigger a re-render

// ✅ CORRECT — create a new object
setUser({ ...user, age: 26 })
</code></pre>

<h3>错误 2：把可以派生的值放进 state</h3>

<pre><code>
// ❌ WRONG — derived state causes sync issues
const [items, setItems] = useState([...])
const [filteredItems, setFilteredItems] = useState([...]) // ← redundant!

// ✅ CORRECT — compute it during render
const filteredItems = items.filter(item =&gt; item.active) // no useState needed
</code></pre>

<h3>错误 3：忘记函数式更新导致 stale closure</h3>

<pre><code>
// ❌ WRONG in async context or event batching
setCount(count + 1)

// ✅ CORRECT — always use functional update when new value depends on old
setCount(prev =&gt; prev + 1)
</code></pre>

<hr/>

<h2>📐 何时用 / 何时不用 / When to Use vs Not</h2>

<p>| ✅ 用 useState | ❌ 不用 useState |</p>
<p>|--------------|----------------|</p>
<p>| UI 交互状态（开/关、选中、展开） | 可从其他 state/props 计算的值 |</p>
<p>| 表单输入值 | 不需要触发渲染的变量（用 <code>useRef</code>） |</p>
<p>| 组件局部数据（列表项、分页） | 多组件共享状态（用 Context 或状态管理库） |</p>
<p>| 异步请求结果（loading/data/error） | 服务端状态（用 React Query / SWR） |</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>1. [React Docs — useState](https://react.dev/reference/react/useState)</p>
<p>2. [React Docs — State as a Snapshot](https://react.dev/learn/state-as-a-snapshot)</p>
<p>3. [Common useState Mistakes (Kent C. Dodds)](https://kentcdodds.com/blog/dont-sync-state-derive-it)</p>

<hr/>

<h2>🧒 ELI5 / 用小孩能理解的话说</h2>

<p><code>useState</code> 就像一块小白板。你可以在上面写字（set state），每次改变内容，整个教室（组件）都会重新看一遍白板（重渲染）。普通变量就像便利贴——改了 React 不知道，不会重新看。</p>

<p><code>useState</code> is like a whiteboard. When you erase and rewrite it, the whole classroom (component) looks again and updates. A regular variable is like a sticky note only you can see — React doesn&#x27;t know it changed.</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 9 — 本周 AI 大事件 / AI News Roundup</h2>

<p><em>来源：web_search，2026年3月24日 / Sources: web_search, March 24, 2026</em></p>

<hr/>

<h2>📰 Story 1: Agentic AI 成为新主流 / Agentic AI Goes Mainstream</h2>

<p><strong>来源 / Source:</strong> [switas.com — The AI Avalanche: 7 Agentic LLM Breakthroughs](https://www.switas.com/articles/the-ai-avalanche-7-agentic-llm-breakthroughs-reshaping-march-2026)</p>

<p>AI 从&quot;生成文本&quot;进化到&quot;自主完成任务&quot;。Gartner 预测 2026 年底 40% 的企业应用将内嵌任务型 AI Agent，作为真正的&quot;数字同事&quot;自动处理端到端业务流程。Oracle 也宣布了专为 Agentic AI 优化的数据库创新。</p>

<p>AI has evolved from &quot;generate text&quot; to &quot;autonomously complete multi-step tasks.&quot; Gartner predicts 40% of enterprise apps will embed task-specific AI agents by end of 2026. Oracle announced new AI Database innovations purpose-built for agentic workloads.</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>作为工程师，你很快会被要求构建或集成 AI Agent。理解 Agent 的工具调用、状态管理、错误恢复机制，会是核心面试考点和工作技能。</p>

<p>As an engineer, you&#x27;ll soon be asked to build or integrate AI agents. Tool calling, state management, and error recovery for agents are becoming core interview topics.</p>

<hr/>

<h2>📰 Story 2: 模型&quot;认知密度&quot;时代——参数不是唯一指标 / Cognitive Density: Parameters Aren&#x27;t Everything</h2>

<p><strong>来源 / Source:</strong> [blog.mean.ceo — New AI Model Releases March 2026](https://blog.mean.ceo/new-ai-model-releases-news-march-2026/)</p>

<p>2026 年 3 月，AI 竞赛焦点从&quot;谁的参数最多&quot;转向&quot;谁的认知密度最高&quot;。Claude Opus 4.6（Anthropic）引入&quot;自适应思考&quot;——模型根据 prompt 复杂度动态决定是否深度推理，无需用户手动配置。OpenAI 的 GPT-5.4 系列专注于每字节更高的知识密度。</p>

<p>The AI race shifted from &quot;most parameters&quot; to &quot;highest cognitive density.&quot; Claude Opus 4.6 introduced &quot;adaptive thinking&quot; — the model dynamically decides when to engage deeper reasoning without user configuration. OpenAI&#x27;s GPT-5.4 focuses on knowledge density per byte.</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>选模型时，benchmark 分数只是一方面。了解&quot;推理成本 vs 质量&quot;的权衡，帮你在实际项目中做出更聪明的模型选型决策。</p>

<p>When choosing models for production, benchmark scores aren&#x27;t everything. Understanding the reasoning-cost vs quality tradeoff helps you make smarter model selection decisions.</p>

<hr/>

<h2>📰 Story 3: 上下文窗口突破 100 万 Token / Context Windows Break 1M Tokens</h2>

<p><strong>来源 / Source:</strong> [alphacorp.ai — Top 5 LLMs for March 2026](https://www.alphacorp.ai/blog/top-5-llms-for-march-2026-benchmarks-pricing-picks)</p>

<p>多个领先模型的上下文窗口已突破 100 万 token，实验性模型甚至推向 1000 万。这意味着可以在单个 prompt 中塞入整个公司知识库、百万行代码库或多年财报数据。</p>

<p>Several leading models now boast 1M+ token context windows, with experimental models pushing toward 10M. You can now feed an entire company knowledge base, massive codebases, or years of financial records into a single prompt.</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>超长上下文改变了 RAG（检索增强生成）的架构选择。某些场景下，直接 long-context 比构建向量数据库更简单、更准确——但成本和速度的权衡需要你来算。</p>

<p>Long contexts change RAG architecture decisions. Sometimes long-context beats building a vector database — but you need to reason about the cost/latency tradeoffs.</p>

<hr/>

<h2>📰 Story 4: LLM 安全新技术 &amp; &quot;能力校准&quot; / LLM Safety &amp; Capability Calibration</h2>

<p><strong>来源 / Source:</strong> [news.ncsu.edu — New Technique Addresses LLM Safety](https://news.ncsu.edu/2026/03/new-technique-addresses-llm-safety/) · [morningstar.com — Appier Capability Calibration](https://www.morningstar.com/news/pr-newswire/20260324cn17690/stop-ai-from-guessing-appier-enables-agents-to-assess-confidence-before-acting)</p>

<p>NC State 研究人员发明了新技术识别保证安全响应的关键组件，同时将&quot;对齐税&quot;（安全训练带来的性能损失）降到最低。Appier 推出&quot;能力校准&quot;框架，让 AI Agent 在行动前先评估自己是否有能力完成任务，降低幻觉和过度自信。</p>

<p>NC State researchers identified key model components that ensure safe responses while minimizing the &quot;alignment tax.&quot; Appier introduced &quot;Capability Calibration&quot; — AI agents assess their own confidence before taking action, reducing hallucinations and overconfidence in enterprise deployments.</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>在企业 AI 部署中，让模型&quot;知道自己不知道什么&quot;比让它无限自信地输出错误答案更重要。Capability calibration 是 AI 工程中的新兴核心模式。</p>

<p>In enterprise AI, knowing what the model doesn&#x27;t know is more valuable than confident-but-wrong outputs. Capability calibration is an emerging core pattern in AI engineering.</p>

<hr/>

<h2>📰 Story 5: 模型发布速度危机——每 72 小时一个重磅发布 / Model Release Velocity Crisis</h2>

<p><strong>来源 / Source:</strong> [ai-weekly.ai — Newsletter 03-24-2026](https://ai-weekly.ai/newsletter-03-24-2026/)</p>

<p>行业分析师追踪到目前约每 72 小时就有一个重大 AI 模型发布。Gemini 3.1 Pro、Claude Opus 4.6、GPT-5.4、DeepSeek V3.2、Qwen 3.5……价格相比去年同期下降 40-80%，开源权重模型与闭源旗舰的差距正在快速收窄。</p>

<p>Analysts are tracking a major AI release approximately every 72 hours. Prices dropped 40-80% year-over-year. Open-weight models are closing the gap with closed-source flagships rapidly.</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>AI 基础设施成本正在快速商品化。在系统设计中，&quot;用哪个 LLM API&quot;的成本计算将越来越重要，学会对比延迟、成本、质量的三角权衡是工程师的新必备技能。</p>

<p>AI infrastructure is rapidly commoditizing. Cost modeling for LLM API selection — balancing latency, cost, and quality — is becoming a core engineering skill.</p>

<hr/>

<h2>📚 参考资料 / References</h2>

<p>1. [AI Weekly Newsletter — March 24, 2026](https://ai-weekly.ai/newsletter-03-24-2026/)</p>
<p>2. [Top LLMs March 2026 — AlphaCorp](https://www.alphacorp.ai/blog/top-5-llms-for-march-2026-benchmarks-pricing-picks)</p>
<p>3. [NC State LLM Safety Research](https://news.ncsu.edu/2026/03/new-technique-addresses-llm-safety/)</p>

<hr/>

<h2>🧒 ELI5 / 用小孩能理解的话说</h2>

<p>AI 现在不只是&quot;会说话&quot;了，而是开始&quot;帮你做事&quot;（Agentic AI）。同时模型越来越聪明但越来越便宜，就像手机——几年前的旗舰价格，现在买到的性能翻了几倍。</p>

<p>AI isn&#x27;t just &quot;talking&quot; anymore — it&#x27;s &quot;doing things for you&quot; (Agentic AI). Meanwhile models keep getting smarter and cheaper, like smartphones — you get 10x more for the same price year after year.</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-23</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-23</guid>
      <pubDate>Mon, 23 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 8 / System Design Day 8</h2>
<p><strong>主题 / Topic:</strong> 数据库索引与查询优化 / Database Indexing &amp; Query Optimization</p>
<p><strong>分类 / Category:</strong> Fundamentals · Beginner · Foundation Phase</p>

<hr/>

<h2>🌍 真实场景 / Real-World Scenario</h2>

<p>想象你在设计 Twitter 的搜索功能。用户搜索某条推文，数据库里有 <strong>5 亿条记录</strong>——如果没有索引，数据库必须逐行扫描，花几分钟才能返回结果。有了索引，查询可以在 <strong>几毫秒内</strong> 完成。</p>

<p><em>Imagine you&#x27;re designing Twitter&#x27;s search feature. Users search for tweets, and there are 500 million records in the database — without indexing, the database must scan row-by-row, taking minutes. With indexes, queries return in milliseconds.</em></p>

<hr/>

<h2>🏛️ 架构图 / ASCII Architecture Diagram</h2>

<pre><code>
User Query: &quot;SELECT * FROM tweets WHERE user_id = 42 AND created_at &gt; &#x27;2026-01-01&#x27;&quot;

WITHOUT INDEX:                     WITH INDEX:
┌─────────────────────┐           ┌─────────────────────┐
│   Full Table Scan   │           │   B-Tree Index      │
│   Row 1: user_id=1  │           │   (user_id, date)   │
│   Row 2: user_id=15 │           │        Root         │
│   Row 3: user_id=42 │           │       /    \        │
│   ...               │           │    Node    Node     │
│   Row 500M: ???     │           │   /  \    /  \      │
│   ❌ 500M reads     │           │  L1  L2  L3  L4     │
└─────────────────────┘           │  ✅ ~log(N) reads   │
                                  └─────────────────────┘

Index Storage:
┌──────────┬──────────┬─────────────────┐
│ user_id  │   date   │  row_pointer →  │
│    42    │ 2026-01  │  page 1042, r3  │
│    42    │ 2026-02  │  page 2891, r7  │
└──────────┴──────────┴─────────────────┘
</code></pre>

<hr/>

<h2>⚖️ 关键权衡 / Key Tradeoffs (为什么这样设计？)</h2>

<h3>索引加速读，但拖慢写 / Indexes Speed Reads, Slow Writes</h3>

<p>| 指标 / Metric | 无索引 Without Index | 有索引 With Index |</p>
<p>|---|---|---|</p>
<p>| SELECT 查询 | O(N) 全表扫描 | O(log N) B-Tree 遍历 |</p>
<p>| INSERT / UPDATE | 快 ⚡ | 慢（需维护索引）|</p>
<p>| 存储空间 Storage | 小 | 更大（索引占空间）|</p>

<p><strong>为什么用 B-Tree？</strong> B-Tree 保持数据有序，支持范围查询（<code>BETWEEN</code>, <code>&gt;</code>），适合绝大多数业务场景。</p>
<p><em>Why B-Tree? It keeps data sorted, supports range queries (<code>BETWEEN</code>, <code>&gt;</code>), fitting most business use cases.</em></p>

<p><strong>复合索引的列顺序很重要 / Column order in composite indexes matters:</strong></p>
<pre><code>
-- Index on (user_id, created_at)
-- ✅ Can use: WHERE user_id = 42 AND created_at &gt; &#x27;2026-01-01&#x27;
-- ✅ Can use: WHERE user_id = 42
-- ❌ Cannot use: WHERE created_at &gt; &#x27;2026-01-01&#x27; (alone)
-- 最左前缀原则 / Leftmost prefix rule!
</code></pre>

<hr/>

<h2>⚠️ 常见错误 / Common Mistakes (别踩这个坑)</h2>

<p>1. <strong>过度索引 Over-indexing</strong> — 给每列都加索引？写操作会变得极慢。生产中见过 INSERT 耗时 10 秒的案例。</p>
<p><em>Adding an index to every column? Writes become painfully slow.</em></p>

<p>2. <strong>索引列上做函数运算 Function on indexed column</strong> — <code>WHERE YEAR(created_at) = 2026</code> 无法使用索引！改用 <code>WHERE created_at BETWEEN &#x27;2026-01-01&#x27; AND &#x27;2026-12-31&#x27;</code>。</p>
<p><em><code>WHERE YEAR(created_at) = 2026</code> can&#x27;t use the index! Use range instead.</em></p>

<p>3. <strong>忽视 EXPLAIN / Ignoring EXPLAIN</strong> — 不跑 <code>EXPLAIN SELECT ...</code> 怎么知道是否用到了索引？</p>
<p><em>Never running <code>EXPLAIN SELECT ...</code> — how do you even know if the index is used?</em></p>

<p>4. <strong>N+1 查询问题 / N+1 Query Problem</strong> — 循环里查询数据库，100 次查询 vs 1 次 JOIN。</p>
<p><em>Querying inside a loop: 100 queries vs 1 JOIN.</em></p>

<hr/>

<h2>📚 References</h2>

<p>- [PostgreSQL Index Documentation](https://www.postgresql.org/docs/current/indexes.html)</p>
<p>- [Use The Index, Luke — Free SQL Indexing Guide](https://use-the-index-luke.com/)</p>
<p>- [MySQL EXPLAIN Output Format](https://dev.mysql.com/doc/refman/8.0/en/explain-output.html)</p>

<h2>🧒 ELI5 (小朋友也能懂)</h2>

<p>索引就像书的<strong>目录</strong>。没有目录，你要找&quot;索引&quot;这个词，就得从第1页翻到最后。有了目录，直接翻到第 283 页。数据库索引做的是同样的事情——不用&quot;翻遍所有数据&quot;，直接跳到你要找的地方。</p>

<p><em>An index is like a book&#x27;s table of contents. Without it, you&#x27;d flip through every page to find &quot;indexing.&quot; With a table of contents, you jump right to page 283. Database indexes do the same thing — skip straight to what you need.</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 8 / Algorithms Day 8</h2>
<p><strong>题目 / Problem:</strong> #36 Valid Sudoku · 🟡 Medium</p>
<p><strong>模式 / Pattern:</strong> Arrays &amp; Hashing</p>

<p>🔗 [LeetCode #36](https://leetcode.com/problems/valid-sudoku/) · 📹 [NeetCode Video](https://www.youtube.com/watch?v=TjFXEUCMqI8)</p>

<hr/>

<h2>🌍 真实类比 / Real-World Analogy</h2>

<p>想象你是数独游戏的裁判。你不需要<strong>解开</strong>这个数独——只需要检查当前状态是否<strong>合法</strong>（没有行、列、3×3格子里有重复数字）。就像检查停车场：不是找空位，而是确认没有两辆车占同一个格子。</p>

<p><em>Imagine you&#x27;re a Sudoku referee. You don&#x27;t need to solve the puzzle — just verify the current state is valid (no duplicate numbers in any row, column, or 3×3 box). Like checking a parking lot: not finding empty spots, but confirming no two cars share the same space.</em></p>

<hr/>

<h2>🧩 题目 / Problem</h2>

<p>给定一个 9×9 的数独棋盘，判断是否有效。规则：</p>
<p>- 每行数字 1-9 不重复</p>
<p>- 每列数字 1-9 不重复</p>
<p>- 每个 3×3 子格数字 1-9 不重复</p>
<p>- 空格用 <code>&#x27;.&#x27;</code> 表示</p>

<p><em>Given a 9×9 Sudoku board, determine if it is valid. Rules: no duplicates in any row, column, or 3×3 box. Empty cells are <code>&#x27;.&#x27;</code>.</em></p>

<hr/>

<h2>💡 关键洞察 / Key Insight</h2>

<p><strong>用哈希集合跟踪所见数字。</strong> 同时遍历三种结构：行、列、3×3格。</p>
<p><strong>关键公式：</strong> 位于 <code>(r, c)</code> 的格子属于哪个 3×3 格？ → <code>box_id = (r // 3) * 3 + (c // 3)</code></p>

<p><em>Use hash sets to track seen numbers simultaneously across rows, columns, and 3×3 boxes.</em></p>
<p><em>Key formula: which box does cell <code>(r, c)</code> belong to? → <code>box_id = (r // 3) </em> 3 + (c // 3)</code>*</p>

<pre><code>
Box IDs:
┌───────┬───────┬───────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │
│ box 0 │ box 1 │ box 2 │
├───────┼───────┼───────┤
│ box 3 │ box 4 │ box 5 │
├───────┼───────┼───────┤
│ box 6 │ box 7 │ box 8 │
└───────┴───────┴───────┘
r=0,c=0: (0//3)*3+(0//3) = 0*3+0 = box 0 ✅
r=1,c=4: (1//3)*3+(4//3) = 0*3+1 = box 1 ✅
r=4,c=7: (4//3)*3+(7//3) = 1*3+2 = box 5 ✅
</code></pre>

<hr/>

<h2>🐍 Python 解法 / Python Solution</h2>

<pre><code>
from collections import defaultdict

def isValidSudoku(board):
    # Use sets for each row, col, box
    rows = defaultdict(set)   # rows[r] = set of digits seen in row r
    cols = defaultdict(set)   # cols[c] = set of digits seen in col c
    boxes = defaultdict(set)  # boxes[b] = set of digits seen in box b

    for r in range(9):
        for c in range(9):
            val = board[r][c]
            if val == &#x27;.&#x27;:
                continue  # Skip empty cells

            box_id = (r // 3) * 3 + (c // 3)

            # Check for duplicates
            if val in rows[r] or val in cols[c] or val in boxes[box_id]:
                return False

            # Record this value
            rows[r].add(val)
            cols[c].add(val)
            boxes[box_id].add(val)

    return True
</code></pre>

<hr/>

<h2>🔍 代码追踪 / Code Trace</h2>

<p>Using a small example focusing on the top-left 3×3 box (box 0):</p>

<pre><code>
board[0] = [&quot;5&quot;,&quot;3&quot;,&quot;.&quot;,&quot;.&quot;,&quot;7&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;]
board[1] = [&quot;6&quot;,&quot;.&quot;,&quot;.&quot;,&quot;1&quot;,&quot;9&quot;,&quot;5&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;]
board[2] = [&quot;.&quot;,&quot;9&quot;,&quot;8&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;6&quot;,&quot;.&quot;]

Processing r=0, c=0: val=&quot;5&quot;
  box_id = (0//3)*3 + (0//3) = 0
  &quot;5&quot; not in rows[0]={}, cols[0]={}, boxes[0]={} → OK
  rows[0]={&quot;5&quot;}, cols[0]={&quot;5&quot;}, boxes[0]={&quot;5&quot;}

Processing r=0, c=1: val=&quot;3&quot;
  box_id = (0//3)*3 + (1//3) = 0
  &quot;3&quot; not in rows[0]={&quot;5&quot;}, cols[1]={}, boxes[0]={&quot;5&quot;} → OK
  rows[0]={&quot;5&quot;,&quot;3&quot;}, cols[1]={&quot;3&quot;}, boxes[0]={&quot;5&quot;,&quot;3&quot;}

Processing r=1, c=0: val=&quot;6&quot;
  box_id = (1//3)*3 + (0//3) = 0
  &quot;6&quot; not in rows[1]={}, cols[0]={&quot;5&quot;}, boxes[0]={&quot;5&quot;,&quot;3&quot;} → OK
  boxes[0]={&quot;5&quot;,&quot;3&quot;,&quot;6&quot;}

Processing r=2, c=1: val=&quot;9&quot;
  box_id = (2//3)*3 + (1//3) = 0
  &quot;9&quot; not in boxes[0]={&quot;5&quot;,&quot;3&quot;,&quot;6&quot;} → OK

Processing r=2, c=2: val=&quot;8&quot;
  box_id = 0
  &quot;8&quot; not in boxes[0]={&quot;5&quot;,&quot;3&quot;,&quot;6&quot;,&quot;9&quot;} → OK
  boxes[0]={&quot;5&quot;,&quot;3&quot;,&quot;6&quot;,&quot;9&quot;,&quot;8&quot;}

Final result: True ✅ (valid board)
</code></pre>

<hr/>

<h2>⏱️ 复杂度 / Complexity</h2>

<p>| | 时间 Time | 空间 Space |</p>
<p>|---|---|---|</p>
<p>| 复杂度 | O(9²) = <strong>O(81) = O(1)</strong> | O(9²) = <strong>O(1)</strong> |</p>
<p>| 说明 | 固定 81 格，常数时间 | 最多存 81 个数字 |</p>

<p><em>Board is always 9×9 — technically O(1) since input size is fixed!</em></p>

<hr/>

<h2>🔄 举一反三 / Pattern Recognition</h2>

<p>掌握&quot;哈希集合去重&quot;模式后，还能解：</p>
<p><em>Once you master &quot;hash set deduplication,&quot; apply it to:</em></p>

<p>- [#48 Rotate Image](https://leetcode.com/problems/rotate-image/) — in-place matrix transformation</p>
<p>- [#54 Spiral Matrix](https://leetcode.com/problems/spiral-matrix/) — matrix traversal order</p>
<p>- [#289 Game of Life](https://leetcode.com/problems/game-of-life/) — state tracking in grids</p>
<p>- [#73 Set Matrix Zeroes](https://leetcode.com/problems/set-matrix-zeroes/) — flagging with sets</p>

<hr/>

<h2>📚 References</h2>

<p>- [LeetCode #36 Valid Sudoku](https://leetcode.com/problems/valid-sudoku/)</p>
<p>- [NeetCode Solution Video](https://www.youtube.com/watch?v=TjFXEUCMqI8)</p>
<p>- [Python defaultdict docs](https://docs.python.org/3/library/collections.html#collections.defaultdict)</p>

<h2>🧒 ELI5</h2>

<p>想象你有9张纸，每张代表一行。遇到数字就写到对应行的纸上。如果那张纸上<strong>已经有了</strong>这个数字，就说明不合法！同样对列和3×3格子也这样检查。</p>

<p><em>Imagine 9 sheets of paper, one per row. When you see a number, write it on that row&#x27;s sheet. If the sheet already has that number — invalid! Do the same for columns and 3×3 boxes.</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 8 / Soft Skills Day 8</h2>
<p><strong>题目 / Question:</strong> 你如何处理模糊不清的需求？ / How do you approach working with ambiguous requirements?</p>
<p><strong>分类 / Category:</strong> Ambiguity · Senior/Staff Level · Foundation Phase</p>

<hr/>

<h2>🎯 为什么这很重要 / Why This Matters</h2>

<p>在大厂，<strong>模糊是常态，而非例外</strong>。产品经理不可能把每个细节都想清楚，业务方也不总知道自己真正想要什么。能优雅处理模糊需求，是区分 senior 工程师和 staff 工程师的核心能力之一。</p>

<p><em>In big tech, ambiguity is the norm, not the exception. PMs can&#x27;t anticipate every detail, and stakeholders don&#x27;t always know what they really want. Navigating ambiguity gracefully separates senior engineers from staff engineers.</em></p>

<hr/>

<h2>⭐ STAR 拆解 / STAR Breakdown</h2>

<p><strong>情境 Situation:</strong> 描述一个需求不清晰的真实场景</p>
<p><em>Describe a real scenario where requirements were unclear</em></p>

<p><strong>任务 Task:</strong> 你被分配了什么？你需要负责什么？</p>
<p><em>What were you assigned? What were you accountable for?</em></p>

<p><strong>行动 Action:</strong> 你具体做了哪些事来厘清需求、推进工作？</p>
<p><em>What specific steps did you take to clarify and move forward?</em></p>

<p><strong>结果 Result:</strong> 量化影响——节省了多少时间？避免了什么返工？</p>
<p><em>Quantify impact — time saved, rework avoided, team unblocked?</em></p>

<hr/>

<h2>❌ 差的回答 vs ✅ 好的回答 / Bad vs Good Answer</h2>

<h3>❌ 差的回答</h3>
<p>&gt; &quot;需求不清楚的时候，我就等产品经理把需求整理清楚，再开始做。&quot;</p>
<p>&gt; &quot;When requirements are unclear, I wait for the PM to clarify everything before starting.&quot;</p>

<p><strong>问题：</strong> 被动等待，没有主动推进。这在面试中是红牌。</p>
<p><em>Problem: Passive waiting shows no ownership or initiative — a red flag in interviews.</em></p>

<hr/>

<h3>✅ 好的回答（结构化）</h3>

<p><strong>S:</strong> 我们在设计一个新的通知系统，PM 只说&quot;用户应该能收到重要通知&quot;，但没有定义什么是&quot;重要&quot;，也没有给出频率和渠道的要求。</p>

<p><em>S: We were designing a new notification system. The PM only said &quot;users should receive important notifications&quot; — no definition of &quot;important,&quot; no frequency or channel requirements.</em></p>

<p><strong>T:</strong> 我是 tech lead，需要在两周内给出技术方案，但需求太模糊无法开始。</p>

<p><em>T: I was the tech lead, needing to deliver a technical plan in two weeks, but the requirements were too vague to start.</em></p>

<p><strong>A:</strong> 我做了三件事：</p>
<p>1. <strong>先列假设清单</strong>：把我理解的&quot;默认行为&quot;写成文档，发给 PM 确认（&quot;我假设通知包括订单状态变更和系统告警，是否正确？&quot;）</p>
<p>2. <strong>识别 reversible vs irreversible 决策</strong>：渠道选择（邮件/推送）容易改，数据库 schema 难改——对难改的部分花更多时间对齐</p>
<p>3. <strong>用 spike + timebox</strong>：花一天做技术调研验证假设，而不是等两周后再发现方向错了</p>

<p><em>A: I did three things:</em></p>
<p><em>1. Made an assumption document — wrote down my &quot;default understanding,&quot; sent to PM for confirmation</em></p>
<p><em>2. Identified reversible vs irreversible decisions — channel choice is easy to change; DB schema is hard — spent more alignment time on hard decisions</em></p>
<p><em>3. Used a spike + timebox — one day of research to validate assumptions rather than discover wrong direction two weeks later</em></p>

<p><strong>R:</strong> 提前 4 天完成方案，避免了一次因误解&quot;重要通知&quot;而可能导致的数据库重新设计（估计 1.5 周返工）。</p>

<p><em>R: Delivered the plan 4 days early, avoiding a potential DB redesign from misunderstanding &quot;important notifications&quot; (estimated 1.5 weeks of rework).</em></p>

<hr/>

<h2>👑 Senior/Staff 进阶技巧 / Senior/Staff Tips</h2>

<p>1. <strong>区分&quot;紧急不可逆&quot;和&quot;可以先行&quot;</strong> — 并非所有模糊都需要澄清后才能动手。</p>
<p><em>Distinguish &quot;critical &amp; irreversible&quot; ambiguity from &quot;can start anyway.&quot; Not all ambiguity blocks progress.</em></p>

<p>2. <strong>用&quot;约束条件反问&quot;</strong> — 不要问&quot;你想要什么&quot;，而是&quot;有哪些约束条件？&quot;（deadline、预算、不能动哪些系统）</p>
<p><em>Ask about constraints rather than desires: &quot;What can&#x27;t change?&quot; uncovers real requirements faster.</em></p>

<p>3. <strong>两次沟通法则</strong> — 如果你向同一个人问了两次同样的问题还没答案，换个方式：写 RFC，开会对齐，或者向上升级。</p>
<p><em>Two-ask rule: If you&#x27;ve asked the same person twice with no answer, escalate the format — write an RFC, schedule alignment, or escalate.</em></p>

<p>4. <strong>让数据说话</strong> — &quot;我们先做一个小实验来验证假设&quot;比&quot;我不确定需求是什么&quot;更有说服力。</p>
<p><em>Let data clarify: &quot;Let&#x27;s run a small experiment&quot; is more powerful than &quot;I&#x27;m not sure what we need.&quot;</em></p>

<hr/>

<h2>🎯 关键要点 / Key Takeaways</h2>

<p>- 🔑 模糊是工程师的日常，主动澄清是职业素养</p>
<p>- 🔑 区分&quot;必须澄清&quot;和&quot;可以默认处理&quot;的需求</p>
<p>- 🔑 把假设写成文档，发出去确认，保留记录</p>
<p>- 🔑 先行动，后优化——不要等到 100% 清晰</p>

<p><em>Ambiguity is daily life in engineering. Proactive clarification is professional maturity. Document assumptions. Move forward on reversible decisions, pause on irreversible ones.</em></p>

<hr/>

<h2>📚 References</h2>

<p>- [Gergely Orosz: &quot;The Art of Asking Questions&quot;](https://newsletter.pragmaticengineer.com/)</p>
<p>- [StaffEng.com: Staff-level behaviors](https://staffeng.com/guides/engineering-strategy)</p>
<p>- [Amazon LP: &quot;Bias for Action&quot; principle](https://www.amazon.jobs/content/en/our-workplace/leadership-principles)</p>

<h2>🧒 ELI5</h2>

<p>如果老师说&quot;画一幅漂亮的画&quot;，你不知道要画什么。聪明的做法是先问：&quot;可以是动物吗？用什么颜色？多大？&quot; 然后开始画，完成后再调整。而不是坐在那里什么都不做，等老师来告诉你每一步。</p>

<p><em>If a teacher says &quot;draw something beautiful&quot; without details, the smart move is to ask: &quot;Can it be an animal? What colors? How big?&quot; Then start drawing and adjust. Not sit frozen waiting for the teacher to specify every brushstroke.</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 8 / Frontend Day 8</h2>
<p><strong>主题 / Topic:</strong> CSS 动画与过渡 / CSS Animations &amp; Transitions</p>
<p><strong>分类 / Category:</strong> CSS Fundamentals · Week 2 · Foundation Phase</p>

<hr/>

<h2>🤔 猜猜输出 / What&#x27;s the Output?</h2>

<pre><code>
.box {
  width: 100px;
  background: blue;
  transition: width 2s, background 0.5s;
}

.box:hover {
  width: 300px;
  background: red;
}
</code></pre>

<p>鼠标悬停时 (on hover)，先发生什么？</p>
<p><em>On hover, which change happens first (visually)?</em></p>

<p><strong>A.</strong> 宽度先变化 (width changes first — both at same time)</p>
<p><strong>B.</strong> 背景颜色先变化，然后宽度变化 (background changes first, then width)</p>
<p><strong>C.</strong> 两者同时开始，背景先完成 (both start together, background finishes first)</p>
<p><strong>D.</strong> 两者同时开始并同时完成 (both start and finish at the same time)</p>

<p><em>(答案在最后 / Answer at the end)</em></p>

<hr/>

<h2>📐 Transition vs Animation</h2>

<h3>Transition（过渡）— &quot;A 到 B&quot;</h3>

<p>触发式的，从一个状态到另一个状态。<em>Triggered change between two states.</em></p>

<pre><code>
/* 基础语法 / Basic syntax */
.button {
  background: blue;
  transform: scale(1);
  
  /* property | duration | timing-function | delay */
  transition: background 0.3s ease-in-out,
              transform 0.2s ease;
}

.button:hover {
  background: darkblue;
  transform: scale(1.05); /* 轻微放大 / slight scale-up */
}
</code></pre>

<h3>Animation（动画）— &quot;持续循环&quot;</h3>

<p>不需要触发，可以自动运行、重复。<em>Can run automatically, loop indefinitely.</em></p>

<pre><code>
/* Step 1: 定义关键帧 / Define keyframes */
@keyframes pulse {
  0%   { transform: scale(1);    opacity: 1; }
  50%  { transform: scale(1.1);  opacity: 0.7; }
  100% { transform: scale(1);    opacity: 1; }
}

/* Step 2: 应用动画 / Apply animation */
.loading-icon {
  /* name | duration | timing | delay | iteration | direction */
  animation: pulse 1.5s ease-in-out 0s infinite alternate;
}
</code></pre>

<hr/>

<h2>⏱️ Timing Functions — 让动画有灵魂 / Giving Motion Soul</h2>

<pre><code>
ease (default): 慢开始，加速，慢结束
ease-in:        慢开始，快结束 → 适合&quot;进入&quot;
ease-out:       快开始，慢结束 → 适合&quot;退出&quot;
ease-in-out:    两端慢，中间快 → 最自然
linear:         匀速 → 适合旋转加载图标
cubic-bezier(): 完全自定义！

Visual:
ease:       ___/‾‾‾
ease-in:    __/‾‾‾‾
ease-out:   ‾‾‾‾\__
linear:     ///////
</code></pre>

<hr/>

<h2>🔥 实际代码对比 / Real Code Comparison</h2>

<h3>❌ 没有过渡 / No Transition (Jarring)</h3>
<pre><code>
.menu {
  display: none; /* 瞬间消失/出现 — 体验差 / Instant — bad UX */
}
</code></pre>

<h3>✅ 用 opacity + visibility 平滑过渡</h3>
<pre><code>
.menu {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.menu.active {
  opacity: 1;
  visibility: visible;
}
/* 注意：display:none 不能过渡! 用 opacity + visibility 代替 */
/* Note: display:none can&#x27;t transition! Use opacity + visibility instead */
</code></pre>

<h3>⚡ 性能陷阱 / Performance Gotcha</h3>

<pre><code>
/* ❌ 触发 Layout Reflow — 慢！*/
.bad { transition: width 0.3s, height 0.3s, margin 0.3s; }

/* ✅ 只触发 Composite — 快！*/
.good { transition: transform 0.3s, opacity 0.3s; }
/*     transform 和 opacity 在 GPU 上运行，性能最佳 */
/*     transform and opacity run on GPU — best performance */
</code></pre>

<p><strong>规则 / Rule:</strong> 动画时优先使用 <code>transform</code> 和 <code>opacity</code>，避免 <code>width/height/margin/top/left</code>（会触发 reflow）。</p>
<p><em>Prefer <code>transform</code> and <code>opacity</code> for animations; avoid <code>width/height/margin/top/left</code> (triggers layout reflow).</em></p>

<hr/>

<h2>🧩 迷你挑战 / Mini Challenge</h2>

<p>用 CSS 实现一个 loading spinner，只用 <code>@keyframes</code> 和 <code>border-radius</code>：</p>

<pre><code>
/* 试着完成这段代码 / Try completing this */
.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  /* 添加旋转动画 / Add rotation animation here */
  animation: ??? 1s linear infinite;
}

@keyframes ??? {
  /* 定义旋转 / Define rotation */
}

/* 答案 / Answer:
animation: spin 1s linear infinite;
@keyframes spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
*/
</code></pre>

<hr/>

<h2>📝 Quiz 答案解析 / Quiz Answer Explanation</h2>

<p><strong>正确答案：C</strong> — 两者同时开始，背景先完成</p>

<pre><code>
timeline (hover starts at t=0):
t=0ms:   ■ width transition starts (2000ms duration)
t=0ms:   ■ background transition starts (500ms duration)
t=500ms: ✅ background = red (DONE)
t=2000ms:✅ width = 300px (DONE)
</code></pre>

<p><code>transition: width 2s, background 0.5s</code> — 两个属性<strong>同时开始</strong>，但持续时间不同，所以背景 500ms 就完成了，而宽度还要等到 2000ms。</p>

<p><em>Both transitions start at the same moment on hover, but have different durations: background completes in 500ms while width takes 2000ms.</em></p>

<hr/>

<h2>📚 References</h2>

<p>- [MDN: CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions)</p>
<p>- [MDN: CSS Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations)</p>
<p>- [CSS Tricks: Transition vs Animation](https://css-tricks.com/css-transitions-101/)</p>

<h2>🧒 ELI5</h2>

<p>过渡就像灯光调光开关——你拨动开关，灯慢慢变亮或变暗。动画就像旋转的风车——不需要你动它，它自己一直转。</p>

<p><em>Transitions are like a dimmer switch — you flip it and the light slowly changes. Animations are like a spinning pinwheel — it just keeps spinning on its own without you touching it.</em></p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 8</h2>
<p><strong>主题 / Topic:</strong> 训练 vs 微调 vs 提示工程 / Training vs Fine-Tuning vs Prompting</p>
<p><strong>分类 / Category:</strong> Foundations · Foundation Phase</p>

<hr/>

<h2>🌍 直觉理解 / Intuitive Explanation</h2>

<p>把 LLM 想象成一个<strong>刚毕业的大学生</strong>：</p>

<p>- <strong>预训练 Pre-training</strong> = 完成大学本科，学习了世界上几乎所有的知识（读了整个互联网）</p>
<p>- <strong>微调 Fine-tuning</strong> = 参加专业培训项目（比如医学院），让他专门擅长某个领域</p>
<p>- <strong>提示工程 Prompting</strong> = 给这个毕业生发一份工作简报，告诉他今天该做什么、怎么做</p>

<p>三种方式的<strong>成本、灵活性、效果</strong>完全不同。</p>

<p><em>Think of an LLM as a fresh college graduate:</em></p>
<p><em>- Pre-training = completing a full degree (reading the entire internet)</em></p>
<p><em>- Fine-tuning = attending medical school (specializing in a domain)</em></p>
<p><em>- Prompting = giving them a daily briefing (telling them what and how to do today)</em></p>

<hr/>

<h2>⚙️ 工作原理 / How It Works</h2>

<h3>1. 预训练 Pre-Training (从零开始)</h3>

<pre><code>
大量文本数据                    → 基础模型
(互联网+书籍+代码)               (GPT-4, Llama, Claude)
Massive text corpus            → Foundation model

成本：数百万美元 + 数周 GPU 时间
Cost: Millions of dollars + weeks of GPU time

训练目标：预测下一个 token
Objective: Predict the next token
</code></pre>

<h3>2. 微调 Fine-Tuning (在已有模型上继续训练)</h3>

<pre><code>
基础模型 + 专业数据集 → 专用模型
Foundation model + domain data → Specialized model

例子 / Examples:
• GPT-4 + 法律文书 → 法律助手
• Llama + 医疗记录 → 医疗诊断模型
• Code Llama = Llama + 代码数据集

成本：数百到数万美元
Cost: Hundreds to tens of thousands of dollars

关键参数：learning rate 要小 (1e-5 to 1e-4)，防止&quot;灾难性遗忘&quot;
Key: Low learning rate to prevent &quot;catastrophic forgetting&quot;
</code></pre>

<h3>3. 提示工程 Prompting (零成本，零训练)</h3>

<pre><code>
用户: &quot;你是一个专业的法律助手，...&quot;
基础模型通过上下文调整行为
No training — just clever input formatting

技术 / Techniques:
• Zero-shot: 直接问
• Few-shot: 给几个例子
• Chain-of-thought: &quot;一步一步想...&quot;
• RAG: 外挂知识库
</code></pre>

<hr/>

<h2>📊 三者对比 / Comparison Table</h2>

<p>| | 预训练 Pre-train | 微调 Fine-tune | 提示 Prompting |</p>
<p>|---|---|---|---|</p>
<p>| 成本 Cost | 💰💰💰💰 极高 | 💰💰 中等 | 💰 几乎免费 |</p>
<p>| 时间 Time | 数周 weeks | 数小时~天 hours-days | 即时 instant |</p>
<p>| 数据需求 Data | 万亿 tokens | 数百~数万样本 | 0~几个例子 |</p>
<p>| 效果 Performance | 通用基础 general | 专域最优 domain-best | 灵活但有上限 |</p>
<p>| 修改难度 Updates | 极难 very hard | 需重新微调 | 实时修改 instant |</p>
<p>| 使用场景 Use Case | 建大模型 | 专业领域 | 99%的应用场景 |</p>

<hr/>

<h2>💻 可运行代码 / Runnable Python Snippet</h2>

<pre><code>
# pip install openai

from openai import OpenAI

client = OpenAI()  # Reads OPENAI_API_KEY from env

# ============================================================
# Technique 1: Zero-shot prompting
# ============================================================
zero_shot = client.chat.completions.create(
    model=&quot;gpt-4o-mini&quot;,
    messages=[
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Classify this tweet as positive/negative/neutral: &#x27;I love this new feature!&#x27;&quot;}
    ]
)
print(&quot;Zero-shot:&quot;, zero_shot.choices[0].message.content)

# ============================================================
# Technique 2: Few-shot prompting (3 examples teach the format)
# ============================================================
few_shot = client.chat.completions.create(
    model=&quot;gpt-4o-mini&quot;,
    messages=[
        {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;Classify tweets. Answer with just: positive, negative, or neutral.&quot;},
        {&quot;role&quot;: &quot;user&quot;,   &quot;content&quot;: &quot;The food was amazing!&quot;},
        {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;positive&quot;},
        {&quot;role&quot;: &quot;user&quot;,   &quot;content&quot;: &quot;Worst customer service ever.&quot;},
        {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;negative&quot;},
        {&quot;role&quot;: &quot;user&quot;,   &quot;content&quot;: &quot;Package arrived.&quot;},
        {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;neutral&quot;},
        # Now the real question:
        {&quot;role&quot;: &quot;user&quot;,   &quot;content&quot;: &quot;I love this new feature!&quot;},
    ]
)
print(&quot;Few-shot:&quot;, few_shot.choices[0].message.content)
# Output: &quot;positive&quot; — much more consistent format!
</code></pre>

<hr/>

<h2>🎯 什么时候用哪种？/ When to Use Which?</h2>

<pre><code>
你的问题                         推荐方案
Your problem                    Recommended approach

&quot;我要做一个通用聊天机器人&quot;          → Prompting (系统提示词)
General chatbot                 → Prompting (system prompt)

&quot;客服需要理解我们专有术语&quot;          → Fine-tuning (100-1000个示例)
Support bot with domain jargon  → Fine-tuning (100-1000 examples)

&quot;要让模型写代码更像我们团队风格&quot;     → Fine-tuning 或 RAG+Prompting
Code style consistency          → Fine-tuning or RAG+Prompting

&quot;从零做下一个 GPT-4&quot;              → Pre-training (别这么想)
Build next GPT-4 from scratch   → Pre-training (don&#x27;t)
</code></pre>

<p><strong>经验法则 / Rule of thumb:</strong> 先试 prompting → 不行试 fine-tuning → 绝不要自己预训练</p>
<p><em>Try prompting first → fine-tune if needed → never pre-train from scratch</em></p>

<hr/>

<h2>🧠 2026 前沿 / 2026 Cutting Edge</h2>

<p>- <strong>LoRA / QLoRA</strong> — 用极少参数做高效微调，只需一张消费级 GPU（A100 → RTX 4090）</p>
<p>- <strong>RLHF</strong> — 用人类反馈强化训练，让模型更&quot;安全&quot;（ChatGPT 的秘诀之一）</p>
<p>- <strong>DPO</strong> — Direct Preference Optimization，比 RLHF 更简单，效果相当</p>

<hr/>

<h2>📚 References</h2>

<p>- [OpenAI Fine-tuning Guide](https://platform.openai.com/docs/guides/fine-tuning)</p>
<p>- [Hugging Face: LoRA &amp; PEFT Documentation](https://huggingface.co/docs/peft/index)</p>
<p>- [Andrej Karpathy: Let&#x27;s build GPT (YouTube)](https://www.youtube.com/watch?v=kCc8FmEb1nY)</p>

<h2>🧒 ELI5</h2>

<p>想象你要教一条狗学新把戏：</p>
<p>- 预训练 = 把狗从小养大，它学会了所有基本技能</p>
<p>- 微调 = 专门训练它学&quot;装死&quot;这一个把戏，练了一个月</p>
<p>- 提示工程 = 你每次说话前告诉它&quot;你是一只很聪明的狗，我想让你...&quot;</p>

<p>最简单的方法永远是先&quot;说话&quot;，实在不行才去&quot;专门训练&quot;。</p>

<p><em>To teach a dog tricks: pre-training = raising it from a puppy (learns everything). Fine-tuning = a month of &quot;play dead&quot; training. Prompting = just telling it what you want before each command. Always try talking first.</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-21</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-21</guid>
      <pubDate>Sat, 21 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>Deepdive</h1>
<h2>🔬 Saturday Deep Dive: Valid Sudoku (15 min read)</h2>

<p>📊 Day 8/150 · NeetCode: 8/150 · SysDesign: 8/40 · Behavioral: 8/40 · Frontend: 8/50 · AI: 4/30</p>
<p>🔥 8-day streak!</p>

<p>🔗 [LeetCode #36: Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) 🟡 Medium</p>
<p>📹 [NeetCode Solution](https://neetcode.io/problems/valid-sudoku)</p>

<hr/>

<h2>Overview / 概述</h2>

<p>今天我们深入研究一道经典的中等难度题：验证数独盘面的合法性。</p>
<p>Today we deep-dive into a classic Medium problem: checking whether a Sudoku board is valid.</p>

<p>这道题看起来简单，实则考察你对<strong>多维哈希</strong>、<strong>坐标映射</strong>和<strong>边界条件</strong>的掌握。</p>
<p>It looks approachable, but tests your grasp of <strong>multi-dimensional hashing</strong>, <strong>coordinate mapping</strong>, and <strong>edge conditions</strong>.</p>

<p>面试中它频繁出现，因为它考察的不是&quot;你能不能写循环&quot;，而是&quot;你能不能系统地对数据建模&quot;。</p>
<p>It appears often in interviews not to test loop-writing, but to test <strong>systematic data modeling</strong>.</p>

<hr/>

<h2>Part 1: Theory / 理论基础 (5 min)</h2>

<h3>什么是数独验证？/ What Are We Validating?</h3>

<p>一个合法的数独盘面满足三个约束：</p>
<p>A valid Sudoku board satisfies exactly three constraints:</p>

<pre><code>
┌─────────────────────────────────┐
│  CONSTRAINT 1: Each ROW         │
│  每一行：数字1-9不能重复        │
│  No digit 1-9 repeated in a row │
├─────────────────────────────────┤
│  CONSTRAINT 2: Each COLUMN      │
│  每一列：数字1-9不能重复        │
│  No digit 1-9 repeated in col   │
├─────────────────────────────────┤
│  CONSTRAINT 3: Each 3×3 BOX     │
│  每个3×3宫格：数字1-9不能重复   │
│  No digit 1-9 repeated in box   │
└─────────────────────────────────┘
</code></pre>

<p><strong>关键点：我们不需要验证数独能否被解出，只需要验证当前已填数字是否合法。</strong></p>
<p><strong>Key insight: We don&#x27;t need to verify the puzzle is solvable — only that existing digits don&#x27;t violate rules.</strong></p>

<p><code>&#x27;.&#x27;</code> 代表空格，忽略不计。/ <code>&#x27;.&#x27;</code> means empty cell — skip it.</p>

<h3>数据建模：核心思想 / Data Modeling: The Core Idea</h3>

<p>最直观的暴力解法是：分别对每行、每列、每个宫格做验证，嵌套循环九次。</p>
<p>The naive approach: check rows, columns, and boxes separately — nine nested loops.</p>

<p>更优雅的方式：<strong>一次遍历，同时维护三个集合字典</strong>。</p>
<p>Better: <strong>one pass, three sets of dictionaries updated simultaneously</strong>.</p>

<pre><code>
For cell (r, c) containing digit d:

  rows[r]    → must not contain d yet
  cols[c]    → must not contain d yet
  boxes[b]   → must not contain d yet

where b = (r // 3, c // 3)  ← box index!
</code></pre>

<p>这个 <code>(r // 3, c // 3)</code> 是本题最优雅的技巧：将坐标映射为0-8的盒子索引。</p>
<p>The <code>(r // 3, c // 3)</code> trick maps any cell to its 3×3 box index — this is the elegance of the problem.</p>

<pre><code>
Box indices visualized:
┌───┬───┬───┐
│0,0│0,1│0,2│
├───┼───┼───┤
│1,0│1,1│1,2│
├───┼───┼───┤
│2,0│2,1│2,2│
└───┴───┴───┘
row 0-2, col 0-2 → box (0,0)
row 0-2, col 3-5 → box (0,1)
row 3-5, col 6-8 → box (1,2)
... etc.
</code></pre>

<hr/>

<h2>Part 2: Step-by-Step Implementation / 一步一步实现 (8 min)</h2>

<h3>Approach 1: Naive (brute force) — O(9²) time but ugly</h3>

<p>验证每行、每列、每宫格需要9次独立循环。可读性差，容易出错。</p>
<p>Nine separate loops for rows, columns, boxes. Hard to read, error-prone.</p>

<pre><code>
# Approach 1: Naive — DO NOT use in interview (too verbose)
def isValidSudoku_naive(board):
    # Check rows
    for row in board:
        seen = set()
        for c in row:
            if c == &#x27;.&#x27;: continue
            if c in seen: return False
            seen.add(c)
    
    # Check cols (transpose and repeat)
    for col in range(9):
        seen = set()
        for row in range(9):
            c = board[row][col]
            if c == &#x27;.&#x27;: continue
            if c in seen: return False
            seen.add(c)
    
    # Check 3x3 boxes
    for box_row in range(3):
        for box_col in range(3):
            seen = set()
            for r in range(box_row*3, box_row*3+3):
                for c in range(box_col*3, box_col*3+3):
                    val = board[r][c]
                    if val == &#x27;.&#x27;: continue
                    if val in seen: return False
                    seen.add(val)
    return True
# Time: O(81) = O(1) technically since board is fixed 9x9
# Space: O(9) per iteration
# Problem: 3 separate passes, harder to extend, verbose
</code></pre>

<h3>Approach 2: Optimal — Single Pass, Three Dicts ✅</h3>

<p><strong>这是面试中应该给出的答案。</strong> / <strong>This is the answer you should give in an interview.</strong></p>

<pre><code>
from collections import defaultdict

def isValidSudoku(board):
    # Three dictionaries: rows, columns, boxes
    # Each maps an index to a set of seen digits
    rows = defaultdict(set)   # rows[r] = {digits seen in row r}
    cols = defaultdict(set)   # cols[c] = {digits seen in col c}
    boxes = defaultdict(set)  # boxes[(r//3, c//3)] = {digits seen in box}

    for r in range(9):
        for c in range(9):
            val = board[r][c]
            
            # Skip empty cells
            if val == &#x27;.&#x27;:
                continue
            
            # Calculate box index — THE KEY TRICK
            box_key = (r // 3, c // 3)
            
            # Check all three constraints simultaneously
            if (val in rows[r] or
                val in cols[c] or
                val in boxes[box_key]):
                return False  # Duplicate found — invalid!
            
            # Add to all three tracking sets
            rows[r].add(val)
            cols[c].add(val)
            boxes[box_key].add(val)
    
    return True  # No violations found
</code></pre>

<h3>Visual Trace / 逐步追踪</h3>

<p>Let&#x27;s trace through a small part of the board:</p>

<pre><code>
Board (first 3 rows shown):
[&quot;5&quot;,&quot;3&quot;,&quot;.&quot;,  &quot;.&quot;,&quot;7&quot;,&quot;.&quot;,  &quot;.&quot;,&quot;.&quot;,&quot;.&quot;]
[&quot;6&quot;,&quot;.&quot;,  &quot;.&quot;,  &quot;1&quot;,&quot;9&quot;,&quot;5&quot;,  &quot;.&quot;,&quot;.&quot;,&quot;.&quot;]
[&quot;.&quot;,&quot;9&quot;,&quot;8&quot;,  &quot;.&quot;,&quot;.&quot;,  &quot;.&quot;,  &quot;.&quot;,&quot;6&quot;,&quot;.&quot;]
</code></pre>

<p><strong>Step-by-step trace:</strong></p>

<pre><code>
(r=0, c=0) val=&#x27;5&#x27;:
  box_key = (0//3, 0//3) = (0,0)
  rows[0]={}, cols[0]={}, boxes[(0,0)]={}  → no conflict
  Add: rows[0]={&#x27;5&#x27;}, cols[0]={&#x27;5&#x27;}, boxes[(0,0)]={&#x27;5&#x27;}

(r=0, c=1) val=&#x27;3&#x27;:
  box_key = (0,0)
  rows[0]={&#x27;5&#x27;}, &#x27;3&#x27; not in it ✓
  cols[1]={},    &#x27;3&#x27; not in it ✓
  boxes[(0,0)]={&#x27;5&#x27;}, &#x27;3&#x27; not in it ✓
  Add: rows[0]={&#x27;5&#x27;,&#x27;3&#x27;}, cols[1]={&#x27;3&#x27;}, boxes[(0,0)]={&#x27;5&#x27;,&#x27;3&#x27;}

(r=0, c=2) val=&#x27;.&#x27;: SKIP

(r=0, c=4) val=&#x27;7&#x27;:
  box_key = (0//3, 4//3) = (0,1)  ← different box!
  Add to rows[0], cols[4], boxes[(0,1)]

(r=1, c=0) val=&#x27;6&#x27;:
  box_key = (1//3, 0//3) = (0,0)  ← same box as row 0!
  rows[1]={}, &#x27;6&#x27; not in it ✓
  cols[0]={&#x27;5&#x27;}, &#x27;6&#x27; not in it ✓
  boxes[(0,0)]={&#x27;5&#x27;,&#x27;3&#x27;}, &#x27;6&#x27; not in it ✓
  Add successfully.

Imagine if we had another &#x27;5&#x27; at (1,0):
  cols[0] already has &#x27;5&#x27; → return False immediately! ✅
</code></pre>

<hr/>

<h2>Part 3: Edge Cases &amp; Gotchas / 边界情况 (2 min)</h2>

<h3>Edge Case 1: Empty Board</h3>
<pre><code>
board = [[&#x27;.&#x27;]*9 for _ in range(9)]
# Result: True — empty board is valid
# Our code handles this: all &#x27;.&#x27; cells are skipped
</code></pre>

<h3>Edge Case 2: Same digit in same box, different row AND column</h3>
<pre><code>
# Trap: this is invalid even though row 0 and col 3 look different
# &#x27;5&#x27; at (0,0) and &#x27;5&#x27; at (2,2) — same 3×3 box!
# The box_key trick catches this: both map to (0,0)
</code></pre>

<h3>Edge Case 3: The &quot;it looks valid row-wise&quot; trap</h3>
<pre><code>
# Each row and column could be valid individually,
# but a 3×3 box might have duplicates
# Always check all THREE constraints — don&#x27;t shortcut!
</code></pre>

<h3>常见面试坑 / Common Interview Traps</h3>
<pre><code>
❌ Trap 1: Using (r // 3) * 3 + (c // 3) as box index
   → This creates 0-8 integer keys, works but harder to read
   → Tuple (r//3, c//3) is cleaner and equally correct

❌ Trap 2: Validating the full 1-9 range
   → We only need to check for duplicates among present digits
   → If a row has [1,2,3,...,8] with one empty, it&#x27;s still valid!

❌ Trap 3: Modifying the input board
   → Never do this. The problem says &quot;determine if valid&quot;, not &quot;solve it&quot;

✅ Best practice: defaultdict(set) avoids KeyError on first access
✅ Best practice: check all 3 constraints before adding — order matters!
</code></pre>

<hr/>

<h2>Part 4: Real-World Application / 实际应用 (2 min)</h2>

<p>数独验证器不只是一道面试题——它的底层思路在很多地方都有应用。</p>
<p>Valid Sudoku isn&#x27;t just an interview puzzle — its core pattern appears everywhere.</p>

<h3>Pattern: Multi-Key Set Membership Check</h3>

<p><strong>1. Database Uniqueness Constraints</strong></p>
<pre><code>
In databases, a UNIQUE constraint across multiple columns is the same idea:
  UNIQUE(user_id, date) — same as &quot;no two rows share both values&quot;
  
PostgreSQL internally uses hash indexes (like our sets) per constraint.
</code></pre>

<p><strong>2. Spreadsheet Validation</strong></p>
<pre><code>
Google Sheets &quot;no duplicates in range&quot; validation:
  Checks each row, column, named range simultaneously
  Exact same three-constraint structure
</code></pre>

<p><strong>3. Compiler Symbol Tables</strong></p>
<pre><code>
A compiler tracks variable names per scope:
  rows    → local scope
  cols    → class scope  
  boxes   → module scope
  
&quot;Variable already declared&quot; = duplicate in the same scope (set)
</code></pre>

<p><strong>4. Form Validation in UI</strong></p>
<pre><code>
// Same pattern: check uniqueness across multiple dimensions
const seen = { byEmail: new Set(), byUsername: new Set() };
users.forEach(user =&gt; {
  if (seen.byEmail.has(user.email)) throw Error(&quot;duplicate email&quot;);
  seen.byEmail.add(user.email);
});
</code></pre>

<hr/>

<h2>Part 5: Interview Simulation / 面试模拟 (3 min)</h2>

<p>假设你已经给出了最优解。面试官可能会问这些问题：</p>
<p>Assume you&#x27;ve presented the optimal solution. Here are follow-up questions interviewers typically ask:</p>

<hr/>

<p><strong>Q1: &quot;What&#x27;s the time and space complexity?&quot;</strong></p>

<pre><code>
Time: O(81) = O(1)
  → Fixed 9×9 board: exactly 81 cells, constant work per cell
  → In general: O(n²) for an n×n Sudoku variant

Space: O(81) = O(1) 
  → At most 81 entries across all sets (each cell appears once per set)
  → In general: O(n²)

⚠️ Interviewer follow-up: &quot;But you&#x27;re using three defaultdicts...&quot;
→ Still O(1) because board size is fixed. For variable n, it&#x27;s O(n²).
</code></pre>

<p><strong>Q2: &quot;Could you do this without any extra space?&quot;</strong></p>

<pre><code>
不完全能，但可以压缩。/ Not quite, but you can compress.

You could encode sets as bitmasks (bit 1 = digit 1 seen, bit 2 = digit 2, etc.):
  rows = [0] * 9   # 9 integers, each bit i = digit i+1 seen
  cols = [0] * 9
  boxes = [0] * 9

  For digit d (1-9), check: if rows[r] &amp; (1 &lt;&lt; d): return False
                            rows[r] |= (1 &lt;&lt; d)

Space: O(27 integers) = O(1). Faster in practice due to cache locality.
</code></pre>

<p><strong>Q3: &quot;How would you extend this to validate a 16×16 Sudoku (Hexadoku)?&quot;</strong></p>

<pre><code>
# Same code, parameterized:
def isValidSudokuNxN(board, n=9, box_size=3):
    rows = defaultdict(set)
    cols = defaultdict(set)
    boxes = defaultdict(set)
    for r in range(n):
        for c in range(n):
            val = board[r][c]
            if val == &#x27;.&#x27;: continue
            box_key = (r // box_size, c // box_size)
            if val in rows[r] or val in cols[c] or val in boxes[box_key]:
                return False
            rows[r].add(val); cols[c].add(val); boxes[box_key].add(val)
    return True
# For 16×16: n=16, box_size=4 → same logic, bigger constants
</code></pre>

<p><strong>Q4: &quot;What if you needed to not just validate but also solve the Sudoku?&quot;</strong></p>

<pre><code>
验证是求解的子问题。/ Validation is a sub-problem of solving.

Solving requires backtracking:
1. Find an empty cell
2. Try digits 1-9
3. For each digit: call isValid (our function!) to check constraints
4. If valid, place digit, recurse
5. If recursion fails, backtrack (remove digit, try next)

Time: O(9^m) where m = number of empty cells — exponential worst case
Typical Sudoku (m~50 empty cells) solves in milliseconds due to pruning.
</code></pre>

<p><strong>Q5: &quot;If this were a production system checking millions of Sudoku boards per second, what would you optimize?&quot;</strong></p>

<pre><code>
1. Bitmask instead of sets → 9 ints vs 27 sets, better cache performance
2. Early termination → return False as soon as first conflict found (already done!)
3. SIMD/vectorization → process 4 rows simultaneously with 256-bit registers
4. GPU parallelism → each 81-cell check is independent; batch 10k boards on GPU
5. Pre-compute valid digit sets → use lookup tables for box → allowed digits

In practice: bitmask version on CPU is 3-5x faster than set version.
</code></pre>

<hr/>

<h2>Complexity Summary / 复杂度总结</h2>

<pre><code>
┌─────────────────┬──────────┬──────────────┐
│ Approach        │ Time     │ Space        │
├─────────────────┼──────────┼──────────────┤
│ Naive (3 loops) │ O(81)    │ O(27) sets   │
│ Single pass     │ O(81)    │ O(27) sets   │  ← Best readability
│ Bitmask         │ O(81)    │ O(27) ints   │  ← Best performance
└─────────────────┴──────────┴──────────────┘

All O(1) for fixed 9×9 board.
For general n×n Sudoku: O(n²) time and space.
</code></pre>

<hr/>

<h2>📚 References / 深入学习</h2>

<p>• 🔗 [LeetCode #36: Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) 🟡 Medium</p>
<p>• 📹 [NeetCode Solution Video](https://neetcode.io/problems/valid-sudoku)</p>
<p>• 📖 [NeetCode Arrays &amp; Hashing Roadmap](https://neetcode.io/roadmap) — Visual learning path for this pattern</p>
<p>• 🔗 [LeetCode #37: Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) — Natural follow-up (backtracking)</p>
<p>• 🔗 [LeetCode #187: Repeated DNA Sequences](https://leetcode.com/problems/repeated-dna-sequences/) — Same multi-key hashing pattern</p>

<hr/>

<p>🧒 <strong>ELI5:</strong> Checking if a Sudoku is valid is like making sure no two kids in the same row, column, or team have the same number on their shirt — you write down each number as you see it, and yell &quot;STOP!&quot; if you see it twice in the same group.</p>

<hr/>

<p><em>Saturday Deep Dive — Day 8 · Generated 2026-03-21</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-20</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-20</guid>
      <pubDate>Fri, 20 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 7 (3 min read) / System Design Day 7</h2>
<h2>Database Types: SQL vs NoSQL — 数据库类型：关系型 vs 非关系型</h2>

<hr/>

<p>想象你在设计一个新的社交媒体平台…</p>

<p>你的用户数据整齐划一：每人都有 ID、用户名、邮箱、注册时间。这很适合用 <strong>SQL 数据库</strong> — 就像一张结构清晰的 Excel 表格，行和列整整齐齐。</p>

<p>但是，用户发的帖子呢？有人只写文字，有人附图片，有人嵌入视频，有人加了位置标签… 每条帖子的结构都不同。这时候 <strong>NoSQL</strong> 就大放异彩 — 像一个灵活的 JSON 文档，想放什么字段就放什么。</p>

<hr/>

<h3>架构对比 / Architecture Comparison</h3>

<pre><code>
         SQL Database                    NoSQL Database
    ┌─────────────────────┐         ┌─────────────────────────┐
    │      USERS TABLE    │         │    users collection     │
    ├──────┬──────┬───────┤         │                         │
    │  id  │ name │ email │         │ { id: 1,                │
    ├──────┼──────┼───────┤         │   name: &quot;Alice&quot;,        │
    │  1   │Alice │a@x.com│         │   email: &quot;a@x.com&quot;,     │
    │  2   │ Bob  │b@y.com│         │   preferences: {...},   │
    └──────┴──────┴───────┘         │   badges: [&quot;🏆&quot;,&quot;⭐&quot;] } │
                                    └─────────────────────────┘
    Schema enforced upfront          Schema flexible / per doc
    JOIN across tables               Embed related data
    ACID transactions                Eventual consistency (often)
    Scale: vertical (bigger server)  Scale: horizontal (more servers)
</code></pre>

<hr/>

<h3>核心概念 / Key Concepts</h3>

<p><strong>SQL (关系型数据库) — MySQL, PostgreSQL, SQLite</strong></p>
<p>- <strong>结构化数据</strong>: 表、行、列，schema 固定</p>
<p>- <strong>ACID 事务</strong>: Atomicity（原子性）, Consistency（一致性）, Isolation（隔离性）, Durability（持久性）— 银行转账不能丢数据！</p>
<p>- <strong>JOIN 操作</strong>: 多表关联查询，数据不冗余</p>
<p>- <strong>强一致性</strong>: 写入后立即可读</p>

<p><strong>NoSQL — MongoDB (文档), Redis (键值), Cassandra (列族), Neo4j (图)</strong></p>
<p>- <strong>灵活 schema</strong>: 每条记录结构可以不同</p>
<p>- <strong>水平扩展</strong>: 分片（sharding）轻松加机器</p>
<p>- <strong>最终一致性</strong>: 写入后可能有短暂延迟才全局可见</p>
<p>- <strong>高吞吐量</strong>: 读写速度极快（尤其键值存储）</p>

<hr/>

<h3>为什么这样设计？How to Choose?</h3>

<p>| 场景 | 推荐 | 原因 |</p>
<p>|------|------|------|</p>
<p>| 用户账户、订单、财务 | SQL | 需要 ACID，数据关系明确 |</p>
<p>| 用户会话、缓存、排行榜 | Redis (NoSQL) | 极速读写，TTL 支持 |</p>
<p>| 产品目录、内容管理 | MongoDB (NoSQL) | 结构多变，嵌套文档 |</p>
<p>| 社交图谱、推荐系统 | Neo4j (Graph) | 关系查询是核心需求 |</p>
<p>| 日志、时序数据 | Cassandra (NoSQL) | 海量写入，时间范围查询 |</p>

<p><strong>经验法则</strong>: 先问&quot;我的数据关系是否复杂？事务是否关键？&quot; → 是则 SQL。&quot;数据量是否巨大？结构是否多变？&quot; → 是则 NoSQL。</p>

<p><strong>现实中：两者共存！</strong></p>
<p>- Instagram: PostgreSQL（用户/帖子关系） + Cassandra（活动 feed） + Redis（缓存）</p>
<p>- 单一数据库解决所有问题是反模式</p>

<hr/>

<h3>别踩这个坑 / Common Mistakes</h3>

<p>❌ <strong>&quot;NoSQL 比 SQL 更快&quot;</strong> — 错！取决于使用场景。复杂 JOIN 查询 SQL 更高效；简单键值查找 Redis 秒杀一切。</p>

<p>❌ <strong>&quot;NoSQL 不支持事务&quot;</strong> — MongoDB 4.0+ 已支持多文档 ACID 事务，只是使用场景不同。</p>

<p>❌ <strong>&quot;选了就不能换&quot;</strong> — 实践中，随着业务演进经常需要引入第二种数据库。提前规划数据访问层（DAL）让切换更容易。</p>

<p>❌ <strong>过早优化</strong> — 99% 的创业公司用 PostgreSQL 就够了。等真正有 scale 问题再引入 NoSQL。</p>

<hr/>

<p>📚 <strong>深入学习 / Learn More:</strong></p>
<p>- [Uber Engineering: Postgres to MySQL Migration](https://www.uber.com/blog/postgres-to-mysql-migration/) — 真实案例：为什么 Uber 从 PostgreSQL 迁移到 MySQL</p>
<p>- [ByteByteGo: SQL vs NoSQL](https://www.youtube.com/watch?v=_Ss42Vb1SU4) — 可视化对比讲解（YouTube）</p>
<p>- [Designing Data-Intensive Applications, Ch. 2](https://dataintensive.net/) — Martin Kleppmann 的权威参考书</p>

<p>🧒 <strong>ELI5:</strong> A SQL database is like a super organized binder with divider tabs where everything goes in exactly the right slot; a NoSQL database is like a big backpack where you can throw in anything — a book, a lunchbox, a basketball — whatever shape it is.</p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 7 (4 min read) / Algorithms Day 7</h2>
<h2>#238 Product of Array Except Self (Medium) — Arrays &amp; Hashing / Prefix Products</h2>

<p>🔗 [LeetCode #238: Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/) 🟡 Medium</p>
<p>📹 [NeetCode Solution](https://neetcode.io/problems/products-of-array-discluding-self)</p>

<hr/>

<h3>🌎 Real-World Analogy / 现实类比</h3>

<p>想象你是一家工厂的质检员，流水线上有 N 个零件，每个都有一个重量。你的任务是：对于每个零件，快速计算出<strong>其他所有零件的总重量之积</strong>（不能把该零件自己算进去）。</p>

<p>最笨的方法？每次都把其他所有零件重新乘一遍 — O(n²)，太慢了。</p>
<p>聪明的方法？先从左到右算一遍&quot;前缀积&quot;，再从右到左算一遍&quot;后缀积&quot;，两个一乘就得到答案！</p>

<hr/>

<h3>📋 Problem Statement / 题目</h3>

<p>Given an integer array <code>nums</code>, return an array <code>answer</code> such that <code>answer[i]</code> is equal to the product of all elements of <code>nums</code> except <code>nums[i]</code>.</p>

<p>给定整数数组 <code>nums</code>，返回数组 <code>answer</code>，使得 <code>answer[i]</code> 等于 <code>nums</code> 中除 <code>nums[i]</code> 之外所有元素的乘积。</p>

<p><strong>Constraint</strong>: Must run in O(n) time, <strong>without using division</strong> (不能用除法).</p>

<p><strong>Example:</strong></p>
<pre><code>
Input:  nums = [1, 2, 3, 4]
Output:       [24, 12,  8,  6]

Check: 
  answer[0] = 2 * 3 * 4 = 24  ✓
  answer[1] = 1 * 3 * 4 = 12  ✓
  answer[2] = 1 * 2 * 4 =  8  ✓
  answer[3] = 1 * 2 * 3 =  6  ✓
</code></pre>

<hr/>

<h3>🔍 Step-by-Step Walkthrough / 逐步分析</h3>

<p><strong>Key Insight</strong>: For position <code>i</code>, the answer = (product of everything to the LEFT of i) × (product of everything to the RIGHT of i)</p>

<pre><code>
nums =   [ 1,   2,   3,   4 ]
index:     0    1    2    3

Left prefix products (prefix[i] = product of nums[0..i-1]):
prefix = [ 1,   1,   2,   6 ]
          ^     ^    ^    ^
          i=0   1*1  1*2  1*2*3
         (nothing (only (1,2   (1,2,3
          left)   nums[0])  left) left)

Right suffix products (suffix[i] = product of nums[i+1..end]):
suffix = [24,  12,   4,   1 ]
          ^     ^    ^    ^
        2*3*4  3*4   4   (nothing
                          right)

answer[i] = prefix[i] * suffix[i]:
  [0]: 1 * 24 = 24
  [1]: 1 * 12 = 12
  [2]: 2 *  4 =  8
  [3]: 6 *  1 =  6
</code></pre>

<p><strong>Space Optimization</strong>: We can do this in O(1) extra space (output array doesn&#x27;t count) by computing prefix in the output array first, then multiplying suffix on-the-fly from right to left.</p>

<hr/>

<h3>🐍 Python Solution / Python 解法</h3>

<pre><code>
def productExceptSelf(nums: list[int]) -&gt; list[int]:
    n = len(nums)
    answer = [1] * n
    
    # Pass 1: Fill answer[i] with the PREFIX product (product of everything LEFT of i)
    # After this pass: answer = [1, 1, 2, 6] for input [1, 2, 3, 4]
    prefix = 1
    for i in range(n):
        answer[i] = prefix      # store product of everything before i
        prefix *= nums[i]       # update running prefix
    
    # Pass 2: Multiply answer[i] by the SUFFIX product (product of everything RIGHT of i)
    # We traverse right-to-left, tracking running suffix product
    suffix = 1
    for i in range(n - 1, -1, -1):
        answer[i] *= suffix     # multiply in the suffix product
        suffix *= nums[i]       # update running suffix

    return answer

# Test it:
print(productExceptSelf([1, 2, 3, 4]))    # → [24, 12, 8, 6]
print(productExceptSelf([-1, 1, 0, -3, 3]))  # → [0, 0, 9, 0, 0]
</code></pre>

<p><strong>Trace with [1, 2, 3, 4]:</strong></p>
<pre><code>
After Pass 1 (prefix):
  i=0: answer[0] = 1,  prefix = 1
  i=1: answer[1] = 1,  prefix = 2
  i=2: answer[2] = 2,  prefix = 6
  i=3: answer[3] = 6,  prefix = 24
  answer = [1, 1, 2, 6]

After Pass 2 (suffix, right to left):
  i=3: answer[3] = 6  * 1 = 6,   suffix = 4
  i=2: answer[2] = 2  * 4 = 8,   suffix = 12
  i=1: answer[1] = 1  * 12 = 12, suffix = 24
  i=0: answer[0] = 1  * 24 = 24, suffix = 24
  answer = [24, 12, 8, 6]  ✓
</code></pre>

<hr/>

<h3>⏱️ Complexity / 复杂度</h3>

<p>| | Complexity |</p>
<p>|---|---|</p>
<p>| <strong>Time</strong> | O(n) — two linear passes |</p>
<p>| <strong>Space</strong> | O(1) extra (output array doesn&#x27;t count per problem rules) |</p>

<hr/>

<h3>举一反三 / Pattern Recognition</h3>

<p><strong>The Prefix/Suffix Pattern</strong> unlocks many problems:</p>
<p>- Any time you need &quot;everything except me&quot; → think prefix × suffix</p>
<p>- <strong>Variant</strong>: [LeetCode #42: Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) — uses max-prefix and max-suffix arrays</p>
<p>- <strong>Variant</strong>: [LeetCode #152: Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/) — track both max and min prefix (negatives flip signs!)</p>
<p>- <strong>Variant</strong>: [LeetCode #724: Find Pivot Index](https://leetcode.com/problems/find-pivot-index/) — prefix sum version of the same idea</p>

<p><strong>Follow-up interview questions:</strong></p>
<p>1. &quot;What if the array contains zeros?&quot; → The code already handles it correctly (the zero propagates into the suffix/prefix)</p>
<p>2. &quot;Can you solve it with O(n²) first, then optimize?&quot; → Always a good way to start</p>
<p>3. &quot;What about overflow?&quot; → Use Python (arbitrary precision) or modular arithmetic</p>

<hr/>

<p>📚 <strong>深入学习 / Learn More:</strong></p>
<p>- 📹 [NeetCode Solution Video](https://neetcode.io/problems/products-of-array-discluding-self) — best visual explanation of the prefix/suffix approach</p>
<p>- [Arrays &amp; Hashing Pattern Guide — NeetCode Roadmap](https://neetcode.io/roadmap) — see the Arrays &amp; Hashing section for this pattern</p>
<p>- Related: [LeetCode #42: Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) 🔴 Hard | [LeetCode #152: Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/) 🟡 Medium</p>

<p>🧒 <strong>ELI5:</strong> If you have 4 friends and you want to know how many handshakes happen when you&#x27;re NOT included, you count all the handshakes to your left, then all the handshakes to your right, and multiply them together — that&#x27;s your answer!</p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 7 (2 min read) / Soft Skills Day 7</h2>
<h2>Technical Leadership: &quot;Tell me about a time you simplified a complex system&quot;</h2>
<h2>技术领导力：&quot;讲一个你简化复杂系统的经历&quot;</h2>

<hr/>

<h3>为什么这很重要 / Why This Matters</h3>

<p>这道题考查的不是&quot;你删了多少行代码&quot;，而是：</p>
<p>1. <strong>你能识别真正的复杂性来源</strong>（accidental vs essential complexity）</p>
<p>2. <strong>你有勇气说&quot;这个可以更简单&quot;</strong>，并推动改变</p>
<p>3. <strong>你理解简化的代价</strong> — 有时候&quot;复杂&quot;是有原因的</p>

<p>Senior/Staff 工程师最重要的技能之一：<strong>抵抗系统熵增</strong>，不让复杂性悄悄积累。</p>

<p><em>This question tests whether you can identify the root of complexity, have the courage to push for change, and understand the tradeoffs of simplification.</em></p>

<hr/>

<h3>STAR Framework Breakdown / STAR 框架拆解</h3>

<p><strong>Situation (情境):</strong></p>
<p>描述系统状态 + 为什么它变复杂了</p>
<p>- 关键信息：系统规模、团队背景、复杂性的历史原因</p>
<p>- 例：&quot;我们的支付服务经历了 3 年迭代，有 7 个微服务处理同一笔交易的不同阶段…&quot;</p>

<p><strong>Task (任务):</strong></p>
<p>你的角色 + 为什么这个简化很重要</p>
<p>- 不只是&quot;我负责这个&quot; — 说清楚商业影响</p>
<p>- 例：&quot;每次新支付方式上线需要 6 周，竞对只需 2 周。我主导了简化工作。&quot;</p>

<p><strong>Action (行动):</strong></p>
<p>这是最重要的部分！展示技术深度：</p>
<p>- 你如何诊断复杂性来源（画架构图？追踪请求链路？）</p>
<p>- 你如何区分哪些复杂性可以去掉，哪些必须保留</p>
<p>- 你如何获得团队 buy-in（技术评审、数据支撑、渐进迁移）</p>
<p>- 你如何降低风险（feature flags、灰度发布、监控）</p>

<p><strong>Result (结果):</strong></p>
<p>量化影响，不要含糊：</p>
<p>- ✅ &quot;上线时间从 6 周降至 1.5 周&quot;</p>
<p>- ✅ &quot;代码行数减少 40%，P99 延迟从 800ms 降到 200ms&quot;</p>
<p>- ✅ &quot;新工程师上手时间从 2 周缩短到 3 天&quot;</p>

<hr/>

<h3>❌ Bad Approach vs ✅ Good Approach</h3>

<p><strong>❌ Bad:</strong></p>
<p>&gt; &quot;我们的旧系统很乱，我重写了它。现在好多了，代码更干净，大家都很满意。&quot;</p>

<p>问题所在：</p>
<p>- 没说清楚复杂性的来源</p>
<p>- &quot;重写&quot;是危险词（没提风险管理）</p>
<p>- 结果模糊，没有数据</p>
<p>- 听起来像个人英雄主义，不像团队领导力</p>

<p><strong>✅ Good:</strong></p>
<p>&gt; &quot;我们有一个支付编排服务，最初设计是 2021 年给 3 种支付方式用的，到 2023 年已经支持 12 种，服务里充满了 if/else 分支和特殊 case 处理。每次新方式上线，QA 要测试所有 12 种，因为改动影响面不可预测。</p>
<p>&gt;</p>
<p>&gt; 我花了两周时间梳理请求流，发现核心问题：所有支付方式被平等对待，但实际上 80% 的代码只和 2 种高复杂度方式有关。我提出用策略模式（Strategy Pattern）重构，让每种支付方式封装自己的逻辑。</p>
<p>&gt;</p>
<p>&gt; 说服团队是最难的部分 — 大家怕改出 bug。我做了一个 spike，证明可以在不改变任何外部 API 的情况下完成重构，并用 feature flags 控制灰度。我们花了 6 周分批迁移，每批覆盖 2 种支付方式。</p>
<p>&gt;</p>
<p>&gt; 最终：新支付方式上线时间从 6 周降至 1.5 周，QA 测试范围减少 60%，事故率下降了 35%。&quot;</p>

<hr/>

<h3>Scenario Template to Adapt / 可复用场景模板</h3>

<pre><code>
Context: [系统名] had grown from [原始状态] to [当前状态] over [时间],
         resulting in [具体问题].

My Role: As [你的角色], I was responsible for [范围].
         The business impact was [影响] — [量化].

Diagnosis: I [诊断方式 — 画图/追链路/分析指标], and identified that
           the core source of complexity was [根本原因].

Solution: I proposed [方案], which addressed [核心问题] while
          preserving [必须保留的复杂性原因].

Risk Management: To validate, I [验证方法]. For rollout, I [迁移策略].

Result: [定量结果 1], [定量结果 2], [定量结果 3].
</code></pre>

<hr/>

<h3>Senior/Staff Level Tips / Senior/Staff 级别加分点</h3>

<p>🎯 <strong>区分 accidental vs essential complexity</strong></p>
<p>- Essential: 业务本身就是复杂的（监管要求、多租户架构）— 必须接受</p>
<p>- Accidental: 历史债务、过度工程、沟通问题导致的 — 可以消除</p>
<p>- 在回答中明确说&quot;这部分复杂性是必要的，我们保留了它&quot;</p>

<p>🎯 <strong>说清楚你是如何 sell 这个方案的</strong></p>
<p>Staff 工程师的简化工作往往需要跨团队协作。说说你如何：</p>
<p>- 用数据/可视化说服怀疑者</p>
<p>- 处理&quot;如果没坏为什么要修&quot;的反对声</p>
<p>- 建立渐进迁移计划让团队安心</p>

<p>🎯 <strong>提到你保留了什么</strong></p>
<p>最好的答案会说&quot;我们考虑过把 X 也简化掉，但决定保留，因为…&quot; — 这体现了成熟的判断力。</p>

<hr/>

<h3>关键要点 / Key Takeaways</h3>

<p>1. <strong>简化不是删代码，是降低认知负担</strong> — 衡量标准是新工程师理解系统需要多久</p>
<p>2. <strong>诊断先于方案</strong> — 先说&quot;我如何找到问题根源&quot;，再说方案</p>
<p>3. <strong>量化一切</strong> — 上线时间、延迟、事故率、代码规模</p>
<p>4. <strong>展示工程领导力</strong> — 技术判断 + 团队推动 + 风险管理</p>

<hr/>

<p>📚 <strong>深入学习 / Learn More:</strong></p>
<p>- [The Wrong Abstraction — Sandi Metz](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction) — classic post on when simplification actually makes things worse</p>
<p>- [A Philosophy of Software Design — John Ousterhout](https://web.stanford.edu/~ouster/cgi-bin/book.php) — the definitive book on managing complexity in software</p>
<p>- [Simple Made Easy — Rich Hickey (Strange Loop Talk)](https://www.youtube.com/watch?v=SxdOUGdseq4) — legendary talk distinguishing &quot;simple&quot; from &quot;easy&quot;</p>

<p>🧒 <strong>ELI5:</strong> Simplifying a complex system is like cleaning your messy backpack — you take everything out, throw away what you don&#x27;t need, and put the rest back in a way that makes it easy to find your pencil without dumping everything on the floor.</p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 7 (2 min read) / Frontend Day 7</h2>
<h2>CSS Positioning: relative, absolute, fixed, sticky</h2>
<h2>CSS 定位：相对、绝对、固定、粘性</h2>

<hr/>

<h3>猜猜这段代码输出什么？/ What does this code output?</h3>

<pre><code>
&lt;style&gt;
  .container {
    position: relative;
    width: 200px;
    height: 200px;
    background: lightblue;
  }

  .box {
    position: absolute;
    top: 20px;
    left: 30px;
    width: 60px;
    height: 60px;
    background: coral;
  }
&lt;/style&gt;

&lt;div class=&quot;container&quot;&gt;
  &lt;div class=&quot;box&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</code></pre>

<p><strong>Where does the coral <code>.box</code> appear?</strong></p>
<p>A) 20px from the top of the <strong>page</strong>, 30px from the left of the <strong>page</strong></p>
<p>B) 20px from the top of <code>.container</code>, 30px from the left of <code>.container</code> ← ✅</p>
<p>C) 20px from the top of the <strong>viewport</strong>, 30px from the left of the <strong>viewport</strong></p>
<p>D) It won&#x27;t move — absolute positioning only works without a parent</p>

<p><em>Answer: B — because <code>.container</code> has <code>position: relative</code>, it becomes the <strong>containing block</strong> for <code>.box</code>.</em></p>

<hr/>

<h3>🗺️ Visual Map of All 4 Positioning Modes</h3>

<pre><code>
┌─────────────────────────────────────────────────────────────┐
│                         WEBPAGE                             │
│                                                             │
│  ┌─────────────────────────────────────┐                    │
│  │   position: relative                │                    │
│  │   └─ stays in flow                 │                    │
│  │   └─ offset from WHERE IT WOULD BE │                    │
│  └─────────────────────────────────────┘                    │
│                                                             │
│  ┌─────────────────────────────────────┐                    │
│  │   position: absolute                │                    │
│  │   ┌─────────────────────────────┐  │                    │
│  │   │ nearest positioned ancestor │  │                    │
│  │   │      ← offsets from HERE   │  │                    │
│  │   └─────────────────────────────┘  │                    │
│  └─────────────────────────────────────┘                    │
│                                                             │
│  ╔══════════════════════════════════════╗ ← VIEWPORT TOP    │
│  ║   position: fixed                    ║                   │
│  ║   └─ always relative to VIEWPORT    ║                   │
│  ║   └─ stays even when you scroll     ║                   │
│  ╚══════════════════════════════════════╝                   │
│                                                             │
│  ┌─────────────────────────────────────┐                    │
│  │   position: sticky                  │                    │
│  │   └─ relative UNTIL you scroll     │                    │
│  │      past threshold → then FIXED   │                    │
│  └─────────────────────────────────────┘                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
</code></pre>

<hr/>

<h3>Code Examples / 代码示例</h3>

<pre><code>
/* 1. RELATIVE — offset from its normal position, stays in document flow */
.badge {
  position: relative;
  top: -2px;   /* nudge up 2px from where it would normally sit */
  /* still takes up its original space in the layout! */
}

/* 2. ABSOLUTE — removed from flow, positioned relative to nearest 
   positioned ancestor (or &lt;html&gt; if none exists) */
.tooltip {
  position: absolute;
  top: 100%;     /* just below the parent */
  left: 50%;
  transform: translateX(-50%);  /* center it */
  /* KEY: the parent must have position: relative! */
}

/* 3. FIXED — always relative to the viewport, never scrolls away */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;  /* stay above everything */
}

/* 4. STICKY — hybrid: relative until threshold, then fixed */
.section-header {
  position: sticky;
  top: 60px;   /* becomes &quot;fixed&quot; 60px from top once scrolled past */
  background: white;
  z-index: 10;
}
</code></pre>

<hr/>

<h3>你可能不知道 / You Might Not Know</h3>

<p>⚠️ <strong><code>absolute</code> 的&quot;陷阱&quot;：containing block 是谁？</strong></p>

<p><code>absolute</code> 定位是相对于&quot;最近的已定位祖先&quot;（nearest positioned ancestor = any element with <code>position</code> other than <code>static</code>）。</p>

<p>如果没有任何祖先设置了 <code>position</code>，那就相对于 <code>&lt;html&gt;</code> 根元素！</p>

<pre><code>
/* Common bug: forgot to add position: relative to parent */
.card {
  /* position: relative; ← forgot this! */
}
.card .badge {
  position: absolute;
  top: 10px;
  right: 10px;
  /* This badge will now position relative to the page, not .card! */
}
</code></pre>

<p>⚠️ <strong><code>sticky</code> 需要高度限制！</strong></p>

<p><code>sticky</code> 只在其<strong>父容器的范围内</strong>有效。如果父容器太短，或者 <code>overflow: hidden</code> 被设置，<code>sticky</code> 会&quot;不工作&quot;。这是最常见的 sticky bug！</p>

<pre><code>
/* sticky won&#x27;t work if parent has overflow: hidden/scroll/auto */
.parent {
  overflow: hidden;  /* this BREAKS sticky! */
}
.child {
  position: sticky;
  top: 0;  /* won&#x27;t stick — parent clips it */
}
</code></pre>

<hr/>

<h3>🎯 Mini Challenge</h3>

<p>Create a <strong>notification badge</strong> that sits in the top-right corner of an avatar image, like this:</p>

<pre><code>
┌──────────┐
│  👤      │🔴  ← red dot badge, top-right corner
│          │
└──────────┘
</code></pre>

<p>Write the HTML/CSS. (Hint: you need ONE element with <code>position: relative</code> and ONE with <code>position: absolute</code>.)</p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;Solution / 答案&lt;/summary&gt;</p>

<pre><code>
&lt;div class=&quot;avatar-wrapper&quot;&gt;
  &lt;img src=&quot;avatar.png&quot; alt=&quot;User&quot; /&gt;
  &lt;span class=&quot;badge&quot;&gt;3&lt;/span&gt;
&lt;/div&gt;
</code></pre>

<pre><code>
.avatar-wrapper {
  position: relative;  /* containing block for the badge */
  display: inline-block;
  width: 60px;
  height: 60px;
}

.avatar-wrapper img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.badge {
  position: absolute;
  top: -4px;
  right: -4px;
  background: red;
  color: white;
  font-size: 11px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
</code></pre>
<p>&lt;/details&gt;</p>

<hr/>

<p>📚 <strong>深入学习 / Learn More:</strong></p>
<p>- [MDN: CSS position property](https://developer.mozilla.org/en-US/docs/Web/CSS/position) — the authoritative reference with live examples</p>
<p>- [CSS-Tricks: Absolute, Relative, Fixed Positioning: How Do They Differ?](https://css-tricks.com/absolute-relative-fixed-positioining-how-do-they-differ/) — clear visual explainer</p>
<p>- [Sticky CSS Headers](https://css-tricks.com/position-sticky-2/) — CSS-Tricks deep dive on the sticky gotchas</p>

<p>🧒 <strong>ELI5:</strong> CSS positioning is like choosing where to put your toys — <code>relative</code> means &quot;move a little from where you already are,&quot; <code>absolute</code> means &quot;go to a specific spot inside your room,&quot; <code>fixed</code> means &quot;stay on the door no matter where I walk,&quot; and <code>sticky</code> means &quot;follow me around, but only once I&#x27;ve passed a certain point.&quot;</p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 7 (2 min read) — AI 新闻速递 / AI News Roundup</h2>

<p><em>Week of March 17–20, 2026</em></p>

<hr/>

<h3>📰 Story 1: Meta 用 AI 替代内容审核员</h3>

<p><strong>What happened:</strong> Meta 宣布大规模推出 AI 内容支持助手，将大幅减少对第三方内容审核承包商的依赖。Meta 表示 AI 系统将处理&quot;重复性的图形内容审核&quot;以及诈骗、毒品销售等对抗性内容的识别。</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>这不只是 Meta 的内部决策 — 它标志着 AI 在<strong>高风险判断任务</strong>中正式进入规模化应用。争议点在于：AI 审核仍然会有偏差和漏判，而当人类被从循环中移除，谁来负责？对于工程师而言，这意味着 AI 安全和内容策略将成为未来几年的关键工程挑战。</p>

<hr/>

<h3>📰 Story 2: Samsung 宣布 730 亿美元 AI 芯片扩张计划</h3>

<p><strong>What happened:</strong> 三星宣布 2026 年将 AI 相关投入增加 22%，押注 Agentic AI 的算力需求激增。目标是超越 SK Hynix，成为 Nvidia 最大的 HBM（高带宽内存）供应商。</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>GPU 之争的下一个战场是 <strong>HBM 内存</strong>。LLM 推理的瓶颈越来越不是算力本身，而是内存带宽 — 这也是为什么 Agentic AI（需要长上下文、多轮推理）会带来如此巨大的内存需求。关注 Samsung vs SK Hynix 的竞争，本质上是在关注 AI 基础设施的下一个瓶颈。</p>

<hr/>

<h3>📰 Story 3: Signal 创始人与 Meta 合作加密 AI</h3>

<p><strong>What happened:</strong> Signal 创始人 Moxie Marlinspike 宣布其加密 AI 聊天机器人 Confer 将与 Meta AI 集成，为 Meta 的 AI 提供端到端隐私保护技术。目标：让 AI 对话内容对 Meta 服务器本身也不可见。</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p><strong>隐私保护 AI（Privacy-Preserving AI）</strong> 是下一个重要技术方向。目前几乎所有 AI 推理都在云端完成，服务商可以看到你的所有对话。Moxie 提出的方向（结合 TEE/可信执行环境 + 同态加密）如果成功，将彻底改变 AI 应用的隐私模型。这对医疗 AI、法律 AI 等敏感场景意义巨大。</p>

<hr/>

<h3>📰 Story 4: Microsoft 发布 MAI-Image-2 图像生成模型</h3>

<p><strong>What happened:</strong> 微软发布第二代 AI 图像生成模型 MAI-Image-2，主打&quot;增强照片真实感&quot;和&quot;图像内文字生成更可靠&quot;。现已在 Copilot 和 Bing Image Creator 上线。</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>文字渲染（text rendering in images）一直是图像 AI 的&quot;耻辱角落&quot; — GPT-4 画出来的招牌字往往是乱码。MAI-Image-2 将文字生成列为核心改进点，这对营销、设计、UI 素材生成有直接影响。同时，这也表明微软正在摆脱对 OpenAI DALL-E 的依赖，建立自己的图像模型能力。</p>

<hr/>

<h3>📰 Story 5: Amazon Alexa Plus 登陆英国 — Agentic AI 助手首个欧洲落地</h3>

<p><strong>What happened:</strong> 亚马逊宣布 Alexa Plus（搭载 agentic AI 能力的升级版）在英国上线，早期访问阶段免费，之后每月 £19.99（约 $26.50），Prime 会员免费。亚马逊特别强调其&quot;genuinely British&quot;——理解&quot;cuppa&quot;、&quot;knackered&quot;、&quot;nippy&quot;等英式表达。</p>

<p><strong>为什么你应该关心 / Why you should care:</strong></p>
<p>AI 助手本地化（localization）不只是翻译，还包括文化理解和方言处理。Alexa Plus 的&quot;Agentic&quot;定位意味着它不再只是问答，而是可以执行多步任务（预订、购物、控制智能家居）。这是 AI 助手从&quot;聊天机器人&quot;向&quot;数字代理人&quot;演进的关键节点，也是 OpenAI、Google、Apple 都在争夺的市场。</p>

<hr/>

<p>📚 <strong>深入学习 / Learn More:</strong></p>
<p>- [Meta AI Content Moderation Announcement](https://about.fb.com/news/2026/03/boosting-your-support-and-safety-on-metas-apps-with-ai/) — 官方博客原文</p>
<p>- [Moxie on Confer + Meta AI Integration](https://confer.to/blog/2026/03/encrypted-meta/) — 技术细节和隐私保护 AI 原理</p>
<p>- [Microsoft MAI-Image-2 Announcement](https://microsoft.ai/news/introducing-MAI-Image-2/) — 官方发布博客</p>

<p>🧒 <strong>ELI5:</strong> This week in AI: robots are learning to be internet safety guards, phone chips are getting a huge upgrade to handle smarter AI, someone figured out how to make AI chats private like a secret code, and AI assistants are learning to speak in different accents — even British English!</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-19</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-19</guid>
      <pubDate>Thu, 19 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<p>🏗️ <strong>系统设计 Day 6 / System Design Day 6</strong></p>
<p><strong>CDN (Content Delivery Network) — 内容分发网络</strong></p>

<hr/>

<p><strong>想象你在设计... / Imagine You&#x27;re Building...</strong></p>

<p>你在上海开了一家咖啡豆网店，客户遍布全球。每次有人从纽约访问你的网站，请求要飞越太平洋到上海服务器取图片、CSS、JS，再飞回去。往返 200ms+，用户等得花都谢了 🌸。</p>

<p>解决方案？在纽约、伦敦、东京都放一份你网站的静态资源副本。用户访问时，就近取货。</p>

<p>这就是 <strong>CDN（内容分发网络）</strong>。</p>

<p>You run a coffee bean shop in Shanghai with global customers. Every request from NYC flies across the Pacific and back — 200ms+ round trip. Solution? Cache copies of your static assets in NYC, London, Tokyo. Users get served from the nearest copy. That&#x27;s a <strong>CDN</strong>.</p>

<hr/>

<p><strong>架构图 / Architecture Diagram</strong></p>

<pre><code>
                     用户请求 User Request
                           │
                     ┌──────┴──────┐
                     │  DNS 解析    │
                     │ (返回最近的  │
                     │  CDN 节点)   │
                     └──────┬──────┘
                            │
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
  ┌──────────┐       ┌──────────┐       ┌──────────┐
  │ CDN Edge │       │ CDN Edge │       │ CDN Edge │
  │  纽约    │       │  伦敦    │       │  东京    │
  │ ████████ │       │ ████     │       │ ██████   │
  │ (cached) │       │ (cached) │       │ (cached) │
  └────┬─────┘       └────┬─────┘       └────┬─────┘
       │                   │                   │
       └───────────────────┼───────────────────┘
                           │ cache miss 时回源
                           ▼
                    ┌──────────────┐
                    │ Origin Server│
                    │  源站 (上海)  │
                    └──────────────┘
</code></pre>

<hr/>

<p><strong>核心概念 / Key Concepts</strong></p>

<p><strong>1. Cache Hit vs Cache Miss</strong></p>
<p>- <strong>Hit（命中）：</strong> CDN 节点有缓存 → 直接返回，超快（&lt; 50ms）</p>
<p>- <strong>Miss（未命中）：</strong> CDN 没有 → 回源站取，存一份，下次就 Hit 了</p>

<p><strong>2. TTL (Time To Live)</strong></p>
<p>- 缓存过期时间。太短 → 频繁回源；太长 → 用户看到旧内容</p>
<p>- 静态资源（图片/CSS/JS）：TTL 长（1天-1年），文件名带 hash（<code>app.a3f2b1.js</code>）</p>
<p>- API 响应：TTL 短（几秒-几分钟）或不缓存</p>

<p><strong>3. Cache Invalidation（缓存失效）</strong></p>
<p>- 发布新版本时需要清除旧缓存</p>
<p>- 方法 1：Purge API（主动清除指定 URL）</p>
<p>- 方法 2：文件名 hash（新版本 = 新文件名 = 自动绕过旧缓存）✅ 推荐</p>

<p><strong>4. Push vs Pull CDN</strong></p>
<p>- <strong>Pull：</strong> CDN 节点在第一次请求时从源站拉取（大多数 CDN 默认行为）</p>
<p>- <strong>Push：</strong> 你主动上传内容到 CDN（适合大文件、已知内容）</p>

<hr/>

<p><strong>别踩这个坑 / Don&#x27;t Fall Into This Trap</strong></p>

<p>❌ 把带用户个人信息的 API 响应（如 <code>/api/me</code>）也放 CDN 缓存</p>
<p>→ 用户 A 看到用户 B 的数据！</p>

<p>✅ 只缓存公开、不含个人信息的内容。私有内容设 <code>Cache-Control: private, no-store</code>。</p>

<p>⚠️ CDN 缓存了错误的 response（比如 500 错误页面）</p>
<p>→ 设置：只缓存 2xx 响应，或设置很短的负缓存 TTL</p>

<p>⚠️ 源站宕机时 CDN 还能服务（这是优点！）但如果 TTL 过了且源站还没恢复 → &quot;stale-while-revalidate&quot; 策略可以继续用旧缓存</p>

<hr/>

<p><strong>面试要点 / Interview Key Points</strong></p>

<p>1. CDN 降低延迟（地理距离）+ 减轻源站压力</p>
<p>2. 适合静态资源；动态内容需要谨慎（考虑 Edge Computing）</p>
<p>3. 常见 CDN：CloudFront (AWS), Cloudflare, Akamai, Fastly</p>
<p>4. CDN + Load Balancer + Cache = 高性能系统的三驾马车</p>

<hr/>

<p><em>Day 6 / 150 — 系统设计基础系列</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<p>💻 <strong>算法 Day 6 / Algorithms Day 6 — #271 Encode and Decode Strings (Medium) — Arrays &amp; Hashing</strong></p>
<p>🔗 https://leetcode.com/problems/encode-and-decode-strings/</p>

<hr/>

<p><strong>现实类比 / Real-World Analogy</strong></p>

<p>你要把一堆购物清单放进一个信封里寄出去。问题是：收件人怎么知道哪里是一个清单的结束、另一个的开始？</p>

<p>笨办法：用逗号分隔 → 但如果清单内容本身有逗号呢？</p>
<p>聪明办法：每个清单前面写上它的长度 → <code>&quot;5#Hello3#Bye&quot;</code> → 无歧义！</p>

<hr/>

<p><strong>题目 / Problem Statement</strong></p>

<p>设计一个算法，将字符串列表编码为单个字符串，再解码回原始列表。</p>
<p>Design an algorithm to encode a list of strings into a single string, and decode it back.</p>

<pre><code>
Input:  [&quot;Hello&quot;, &quot;World&quot;]
Encode: &quot;5#Hello5#World&quot;
Decode: [&quot;Hello&quot;, &quot;World&quot;]
</code></pre>

<p>编码后的字符串可以包含任何字符（包括 <code>#</code>、空字符串等），必须无歧义。</p>

<hr/>

<p><strong>追踪过程 / Trace Through</strong></p>

<p>编码 <code>[&quot;Hi&quot;, &quot;&quot;, &quot;a#b&quot;]</code>：</p>

<pre><code>
&quot;Hi&quot;  → len=2  → &quot;2#Hi&quot;
&quot;&quot;    → len=0  → &quot;0#&quot;
&quot;a#b&quot; → len=3  → &quot;3#a#b&quot;

encoded = &quot;2#Hi0#3#a#b&quot;
</code></pre>

<p>解码 <code>&quot;2#Hi0#3#a#b&quot;</code>：</p>

<pre><code>
i=0: find &#x27;#&#x27; at index 1 → length=2 → read &quot;Hi&quot; → i=4
i=4: find &#x27;#&#x27; at index 5 → length=0 → read &quot;&quot; → i=6
i=6: find &#x27;#&#x27; at index 7 → length=3 → read &quot;a#b&quot; → i=11
Done! → [&quot;Hi&quot;, &quot;&quot;, &quot;a#b&quot;] ✅
</code></pre>

<p>注意 <code>&quot;a#b&quot;</code> 内部的 <code>#</code> 不会造成混淆，因为我们是按<strong>长度</strong>读取的，不是按分隔符！</p>

<hr/>

<p><strong>Python 解法 / Python Solution</strong></p>

<pre><code>
class Codec:
    def encode(self, strs: list[str]) -&gt; str:
        # Format: &quot;length#string&quot; for each string
        result = []
        for s in strs:
            result.append(f&quot;{len(s)}#{s}&quot;)
        return &quot;&quot;.join(result)

    def decode(self, s: str) -&gt; list[str]:
        result = []
        i = 0
        while i &lt; len(s):
            # Find the &#x27;#&#x27; delimiter
            j = s.index(&#x27;#&#x27;, i)
            # Characters before &#x27;#&#x27; are the length
            length = int(s[i:j])
            # Read exactly &#x27;length&#x27; characters after &#x27;#&#x27;
            result.append(s[j + 1 : j + 1 + length])
            # Move pointer past the string
            i = j + 1 + length
        return result
</code></pre>

<hr/>

<p><strong>复杂度 / Complexity</strong></p>

<p>- <strong>时间 Time:</strong> O(n) — n 是所有字符串总长度</p>
<p>- <strong>空间 Space:</strong> O(1) — 不算输出空间（编码和解码都是线性扫描）</p>

<hr/>

<p><strong>边界情况 / Edge Cases</strong></p>

<pre><code>
codec = Codec()

# Empty list
codec.decode(codec.encode([])) == []  # ✅

# List with empty string
codec.decode(codec.encode([&quot;&quot;])) == [&quot;&quot;]  # ✅ &quot;0#&quot; → [&quot;&quot;]

# Strings containing &#x27;#&#x27;
codec.decode(codec.encode([&quot;a#b&quot;, &quot;#&quot;])) == [&quot;a#b&quot;, &quot;#&quot;]  # ✅

# Very long string
codec.decode(codec.encode([&quot;a&quot; * 10000])) == [&quot;a&quot; * 10000]  # ✅
</code></pre>

<hr/>

<p><strong>举一反三 / Pattern Recognition</strong></p>

<p><strong>模式：长度前缀编码（Length-Prefixed Encoding）</strong></p>

<p>这和网络协议（TCP、HTTP/2）、序列化格式（Protocol Buffers）用的是同一个思路：先告诉你&quot;接下来有多少字节&quot;，然后精确读取。</p>

<p>为什么不用特殊分隔符（如 <code>|</code> 或 <code>\0</code>）？</p>
<p>→ 字符串可能包含任何字符！长度前缀永远不会歧义。</p>

<p><strong>相关题目：</strong></p>
<p>- #443 String Compression（类似的编码思维）</p>
<p>- 序列化/反序列化（#297 Serialize and Deserialize Binary Tree）</p>

<hr/>

<p><em>Day 6 / 150 — Arrays &amp; Hashing 系列</em></p>
<p><em>昨天 Day 4：Group Anagrams | 明天 Day 7：Product of Array Except Self</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<p>🗣️ <strong>软技能 Day 6 / Soft Skills Day 6</strong></p>
<p><strong>Leadership — 领导力（不靠头衔）</strong></p>

<p><strong>问题 / Question:</strong></p>
<p>&gt; &quot;Describe how you&#x27;ve mentored or grown other engineers.&quot;</p>
<p>&gt; &quot;描述你是如何指导或培养其他工程师的。&quot;</p>

<hr/>

<p><strong>为什么这很重要 / Why This Matters</strong></p>

<p>Senior/Staff 工程师的核心职责之一不是写更多代码，而是<strong>让团队里每个人都变得更强</strong>。面试官问这题是想看：</p>
<p>1. 你有没有&quot;利他&quot;意识（不只关心自己的 output）</p>
<p>2. 你的指导方式是否有结构、有结果</p>
<p>3. 你能不能识别他人的成长需求</p>

<p>At senior+ levels, your impact is measured by the engineers you&#x27;ve grown, not just the code you&#x27;ve shipped.</p>

<hr/>

<p><strong>❌ 糟糕的回答 / Bad Approach</strong></p>

<p>&gt; &quot;我经常帮初级工程师 review 代码，给他们指出问题，告诉他们怎么改。&quot;</p>

<p>问题：</p>
<p>- 这只是日常工作，不算 mentoring</p>
<p>- &quot;告诉他们怎么改&quot; = 给答案，不是教思考</p>
<p>- 没有具体故事、没有成长轨迹</p>

<hr/>

<p><strong>✅ 好的回答框架 / Good Approach (STAR)</strong></p>

<p><strong>Situation:</strong> &quot;团队来了一位从 bootcamp 毕业的新工程师 Alex。他代码能 work，但 PR 里经常缺乏边界处理和测试，review 来回很多轮。&quot;</p>

<p><strong>Task:</strong> &quot;作为他的 buddy engineer，我的目标不只是帮他过 PR，而是让他在 3 个月内能独立负责一个模块。&quot;</p>

<p><strong>Action:</strong></p>
<p>- &quot;我没有直接告诉他&#x27;加个 null check&#x27;，而是在 PR comment 里问引导性问题：&#x27;如果这个参数是 undefined 会发生什么？&#x27;让他自己发现问题&quot;</p>
<p>- &quot;每周 1:1 花 30 分钟，前 15 分钟他讲本周遇到的困难，后 15 分钟我分享一个设计决策的背景（为什么我们用 Redis 而不是 Memcached）&quot;</p>
<p>- &quot;第 6 周开始让他 lead 一个小 feature 的设计 doc，我只 review 不动手&quot;</p>

<p><strong>Result:</strong> &quot;3 个月后，他的 PR 首次 review 通过率从 20% 提升到 75%。第 4 个月他独立 ship 了用户通知系统。半年后他成了团队里 on-call 最可靠的人之一。&quot;</p>

<hr/>

<p><strong>Senior/Staff 级别技巧 / Senior/Staff Tips</strong></p>

<p>🎯 <strong>从&quot;给答案&quot;到&quot;问问题&quot;</strong></p>
<p>- 初级：直接告诉他怎么做（他需要 unblock）</p>
<p>- 中级：给方向，让他自己找路</p>
<p>- 高级：只问问题，让他自己发现问题和答案</p>

<p>🎯 <strong>Mentoring ≠ 只有 1:1</strong></p>
<p>- 写好的设计文档 = 一次写，教会所有人</p>
<p>- 做技术分享 / lunch &amp; learn = 批量 mentoring</p>
<p>- 建立团队 wiki / onboarding guide = 可扩展的知识传播</p>

<p>🎯 <strong>跟踪成长，不只是感觉</strong></p>
<p>- 用具体指标说话（PR 通过率、独立完成的 feature 数、on-call 表现）</p>
<p>- &quot;他成长了&quot;太模糊，&quot;他从需要 3 轮 review 到 1 轮&quot;才有说服力</p>

<hr/>

<p><strong>关键要点 / Key Takeaways</strong></p>

<p>1. Mentoring 的核心是<strong>授人以渔</strong> — 教思考方式，不只是给答案</p>
<p>2. 好的 mentor 会<strong>有意识地退后</strong> — 让 mentee 犯可控的错误并从中学习</p>
<p>3. 用<strong>具体的成长指标</strong>证明你的 mentoring 有效果</p>
<p>4. 最高级的 leadership：建立系统和文化，让 mentoring 自然发生（不依赖你个人）</p>

<hr/>

<p><em>Day 6 / 150 — 软技能系列</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<p>🎨 <strong>前端 Day 6 / Frontend Day 6</strong></p>
<p><strong>CSS Variables &amp; Modern CSS Features — CSS 自定义属性</strong></p>

<hr/>

<p><strong>猜猜这段代码输出什么？/ What does this layout look like?</strong></p>

<pre><code>
:root {
  --primary: #3b82f6;
  --spacing: 16px;
  --radius: 8px;
}

.card {
  --primary: #ef4444;  /* 局部覆盖 */
  padding: var(--spacing);
  border: 2px solid var(--primary);
  border-radius: var(--radius);
}

.card .badge {
  background: var(--primary);
  color: white;
  padding: calc(var(--spacing) / 4) calc(var(--spacing) / 2);
  border-radius: var(--radius);
}
</code></pre>

<p>🤔 <code>.badge</code> 的 <code>background</code> 是蓝色 <code>#3b82f6</code> 还是红色 <code>#ef4444</code>？</p>

<p>✅ <strong>答案：红色 <code>#ef4444</code>！</strong></p>

<p>CSS Variables 遵循<strong>继承规则</strong> — <code>.card</code> 里重新定义了 <code>--primary</code>，它的所有后代元素（包括 <code>.badge</code>）都会继承这个新值。这和 Sass/Less 变量完全不同（它们是编译时替换，不支持继承）。</p>

<hr/>

<p><strong>CSS Variables 核心 / Key Concepts</strong></p>

<p><strong>1. 定义与使用</strong></p>
<pre><code>
/* 定义：用 -- 前缀 */
:root {
  --color-text: #1a1a1a;
  --font-size-base: 16px;
}

/* 使用：用 var() 函数 */
body {
  color: var(--color-text);
  font-size: var(--font-size-base);
}

/* 带 fallback 值 */
p {
  color: var(--color-accent, #666);  /* 如果 --color-accent 未定义，用 #666 */
}
</code></pre>

<p><strong>2. 运行时 vs 编译时</strong></p>
<pre><code>
Sass:   $primary: blue → 编译后变成 → color: blue（写死了）
CSS:    --primary: blue → 运行时读取 → 可以随时改！
</code></pre>

<p>这意味着你可以：</p>
<p>- 用 JS 动态改变 → <code>document.documentElement.style.setProperty(&#x27;--primary&#x27;, &#x27;red&#x27;)</code></p>
<p>- 根据 media query 改变 → 深色模式只需重新定义变量</p>
<p>- 组件内局部覆盖 → 不影响全局</p>

<p><strong>3. Dark Mode 只需几行</strong></p>
<pre><code>
:root {
  --bg: #ffffff;
  --text: #1a1a1a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #f0f0f0;
  }
}

body {
  background: var(--bg);
  color: var(--text);
}
</code></pre>

<hr/>

<p><strong>你可能不知道 / You Might Not Know</strong></p>

<p>CSS Variables 可以用在 <code>calc()</code> 里做数学运算：</p>

<pre><code>
:root {
  --base: 8px;
}

.component {
  padding: calc(var(--base) * 2);      /* 16px */
  margin: calc(var(--base) * 3);       /* 24px */
  border-radius: calc(var(--base) / 2); /* 4px */
}
</code></pre>

<p>这就是 <strong>Design Token</strong> 的基础 — 用一个基数推导出整个间距系统（4px, 8px, 12px, 16px, 24px, 32px...），改一个值就改全部。Tailwind CSS 内部就是这个原理。</p>

<hr/>

<p><strong>⚠️ 常见陷阱 / Gotcha</strong></p>

<pre><code>
.box {
  --size: 100;
  width: var(--size)px;  /* ❌ 不行！不会变成 100px */
  width: calc(var(--size) * 1px);  /* ✅ 这样才行 */
}
</code></pre>

<p>CSS Variables 存的是<strong>字符串</strong>，<code>var(--size)px</code> 会变成 <code>100 px</code>（中间有空格），无效。必须用 <code>calc()</code> 做单位转换。</p>

<hr/>

<p><strong>Mini Challenge 🧩</strong></p>

<p>不看上面的内容，回答：</p>

<pre><code>
:root { --gap: 10px; }
.parent { --gap: 20px; }
.parent .child { margin: var(--gap); }
</code></pre>

<p><code>.child</code> 的 <code>margin</code> 是多少？</p>

<p>答案：<strong>20px</strong> — <code>.parent</code> 覆盖了 <code>--gap</code>，<code>.child</code> 作为后代继承 <code>.parent</code> 的值。</p>

<hr/>

<p><em>Day 6 / 150 — CSS 基础系列</em></p>
<p><em>昨天 Day 4：Responsive Design &amp; Media Queries | 明天 Day 7：JavaScript DOM 基础</em></p>

<hr/>
<h1>🤖 AI</h1>
<p>🤖 <strong>AI Day 6 — 新闻速递 / News Roundup</strong></p>
<p><em>2026年3月 | March 2026</em></p>

<p>&gt; ⚠️ 以下为基于公开报道的摘要，具体数字以原始来源为准。</p>
<p>&gt; Based on public reporting; verify specifics at original sources.</p>

<hr/>

<p><strong>📰 1. Claude 4 发布 — Anthropic 的新旗舰</strong></p>

<p>Anthropic 发布了 Claude 4 系列模型，包含 Claude 4 Opus（旗舰推理模型）和 Claude 4 Sonnet（平衡性能/速度）。据报道在编码、数学推理和长文档分析上有显著提升。</p>

<p><strong>为什么你应该关心：</strong></p>
<p>Claude 4 是 GPT-5 之后的又一个&quot;代际跳跃&quot;。如果你在做 AI 产品，现在有两个顶级推理模型可选。对开发者来说，更强的编码能力意味着 AI pair programming 又升了一个台阶。</p>

<hr/>

<p><strong>📰 2. 开源模型追赶闭源 — Llama 4 / Qwen 3</strong></p>

<p>Meta 的 Llama 4 和阿里的 Qwen 3 系列在基准测试上接近 GPT-4 级别。Llama 4 Scout（17B 参数）在 MMLU 上接近 GPT-4o 水平。</p>

<p><strong>为什么你应该关心：</strong></p>
<p>开源模型的质量已经到了&quot;够用&quot;的临界点。对创业公司意味着：不再被 OpenAI/Anthropic 的定价绑定。对大公司意味着：可以在私有基础设施上部署，数据不出内网。如果你在选模型，先评估开源方案——可能省 90% 的 API 成本。</p>

<hr/>

<p><strong>📰 3. AI Agent 框架爆发</strong></p>

<p>2026年初，AI Agent（智能体）从概念变成了产品。OpenAI 的 Operator、Anthropic 的 Computer Use、以及开源的 browser-use 等项目让 AI 能直接操作浏览器、写代码、管理文件。</p>

<p><strong>为什么你应该关心：</strong></p>
<p>&quot;AI 能帮你查东西&quot;和&quot;AI 能帮你做事&quot;是两个完全不同的级别。Agent 意味着 AI 从&quot;回答问题&quot;升级到&quot;执行任务&quot;。工程师需要思考：你的产品/API 是否对 Agent 友好？是否有好的错误提示让 Agent 能自动恢复？</p>

<hr/>

<p><strong>📰 4. 欧盟 AI Act 正式执行</strong></p>

<p>欧盟的 AI 法案（AI Act）开始分阶段执行。高风险 AI 系统（医疗诊断、招聘筛选、信用评分）需要通过合规审查，包括数据来源透明度、偏见测试、人工审核机制。</p>

<p><strong>为什么你应该关心：</strong></p>
<p>如果你的产品面向欧洲用户，这不是&quot;以后再说&quot;——它现在就是法律了。即使你在美国，欧盟用户的 GDPR 合规 + AI Act 合规 = 你需要知道你的模型用了什么数据、做了什么决策。文档化不是可选的。</p>

<hr/>

<p><strong>📰 5. Vibe Coding 成为主流开发方式</strong></p>

<p>&quot;Vibe Coding&quot;——用自然语言描述需求、让 AI 生成代码、开发者做审查和架构决策——在 2026 年成为被广泛接受的开发方式。Cursor、Copilot、Claude Code 等工具的日活用户据报道已超百万。</p>

<p><strong>为什么你应该关心：</strong></p>
<p>不是&quot;AI 会不会取代工程师&quot;，而是&quot;用 AI 的工程师会不会取代不用的&quot;。核心变化：<strong>写代码的成本降低了，但设计正确系统的能力反而更值钱了</strong>。算法理解、系统设计、代码审查能力变得更重要，因为你要能审查 AI 写的代码。</p>

<hr/>

<p><em>Day 6 / 150 — AI 新闻系列</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-18</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-18</guid>
      <pubDate>Wed, 18 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>Review</h1>
<h2>🔄 复习日 Day 5 / Review Day 5</h2>

<p>📊 Day 5/150 · NeetCode: 4/150 · SysDesign: 4/40 · Behavioral: 4/40 · Frontend: 4/50 · AI: 2/30</p>
<p>🔥 3-day streak!</p>

<p>今天是复习日！回顾过去4天的内容。</p>
<p>Today is a review day! Let&#x27;s revisit the past 4 days.</p>

<hr/>

<h3>📝 Quick Quiz — 3 Mini-Reviews</h3>

<p>从系统设计、算法、前端三个板块各出一题——先别看答案，想想你记得多少！</p>
<p>One question each from System Design, Algorithms, and Frontend — try to answer before peeking!</p>

<hr/>

<p><strong>Q1: [🏗️ System Design]</strong> From Day 4 — Load Balancing</p>

<p>你有一个 Load Balancer 后面跟着 3 台服务器。用户登录后，Session 存在 Server 1 的内存里。下一个请求被路由到 Server 2——用户发现自己被登出了。</p>

<p>You have a load balancer in front of 3 servers. User logs in, session stored in Server 1&#x27;s memory. Next request routes to Server 2 — user is logged out.</p>

<p><strong>问 / Question:</strong> 这个问题叫什么？列出两种解决方案，并说明各自的取舍。</p>
<p>What is this problem called? Name two solutions and explain the tradeoff of each.</p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>这叫 Session Affinity（会话粘性）问题，或 Sticky Session 问题。</strong></p>

<p>服务器无状态（Stateless）是 REST 的核心原则，但当状态存在单台服务器内存时，负载均衡就破坏了这个假设。</p>

<p><strong>解决方案 1：Sticky Sessions（粘性会话）</strong></p>
<p>Load Balancer 记住&quot;用户 A → 永远去 Server 1&quot;。</p>
<p>- ✅ 简单，无需改动应用层</p>
<p>- ❌ 如果 Server 1 宕机，所有绑定它的用户 session 丢失；负载不均衡</p>

<p><strong>解决方案 2：Shared Session Store（共享 Session 存储）</strong></p>
<p>Session 不存在各服务器内存，而是存在 Redis 等共享缓存里。所有服务器读写同一个 Redis。</p>
<p>- ✅ 任何服务器都能处理任何请求，真正无状态</p>
<p>- ❌ 引入 Redis 作为额外依赖；Redis 本身需要高可用设计</p>

<p><strong>关键洞察（From Day 4）：</strong> 负载均衡解决了&quot;一台服务器扛不住&quot;的问题，但同时暴露了&quot;状态共享&quot;的问题。真正可扩展的系统需要无状态的应用层 + 独立的状态层（数据库/缓存）。</p>

<p>&lt;/details&gt;</p>

<hr/>

<p><strong>Q2: [💻 Algorithms]</strong> From Days 2–4 — Arrays &amp; Hashing Pattern</p>

<p>下面这段代码是什么算法的思路？时间复杂度是多少？它和 Day 2 的 Valid Anagram 有什么共同的核心思想？</p>

<p>What algorithm pattern does this code skeleton represent? What&#x27;s the time complexity? How does it share a core idea with Day 2&#x27;s Valid Anagram?</p>

<pre><code>
def mystery(nums):
    seen = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:
            return [seen[complement], i]
        seen[num] = i
</code></pre>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong>这是 Day 3 的 Two Sum 解法。</strong></p>

<p><strong>时间复杂度：O(n)</strong> — 只遍历一次数组，<code>dict</code> 的查找是 O(1)。</p>
<p><strong>空间复杂度：O(n)</strong> — 最坏情况存储 n 个元素。</p>

<p><strong>和 Valid Anagram 的共同核心思想：</strong></p>

<p>Two Sum 和 Valid Anagram 都是 <strong>Arrays &amp; Hashing</strong> 模式的经典题。核心思想是：</p>
<p>&gt; <em>&quot;用哈希表把 O(n²) 的&quot;两层循环查找&quot;压缩成 O(n) 的&quot;一次遍历 + 字典查询&quot;。&quot;</em></p>
<p>&gt; <em>Use a hash map to trade O(n²) nested search for O(n) single-pass lookup.</em></p>

<p>- <strong>Valid Anagram</strong>：用哈希表统计字符频率，把&quot;逐个比对&quot;变成&quot;对比频率表&quot;</p>
<p>- <strong>Two Sum</strong>：用哈希表记录&quot;已见过的数&quot;，把&quot;两层循环找配对&quot;变成&quot;一次扫描找补集&quot;</p>

<p>这个思路是 NeetCode 150 中出现频率最高的模式之一——遇到&quot;在数组里找某种关系&quot;的题，先想哈希表！</p>

<p>&lt;/details&gt;</p>

<hr/>

<p><strong>Q3: [🎨 Frontend]</strong> From Day 4 — Responsive Design &amp; Media Queries</p>

<p>不看代码，回答：<code>@media (min-width: 768px)</code> 这条规则，在什么情况下<strong>生效</strong>，在什么情况下<strong>不生效</strong>？</p>

<p>Without looking at code: when does <code>@media (min-width: 768px)</code> apply, and when does it not?</p>

<p>然后：如果同时有 <code>min-width: 768px</code> 和 <code>min-width: 1200px</code> 两条媒体查询，都在 1400px 宽度下，会发生什么？</p>

<p>Then: if you have both <code>min-width: 768px</code> and <code>min-width: 1200px</code> rules, what happens at 1400px width?</p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;显示答案 / Show Answer&lt;/summary&gt;</p>

<p><strong><code>@media (min-width: 768px)</code> 生效条件：</strong></p>
<p>- 视口宽度 <strong>≥ 768px</strong> → 规则生效（应用样式）</p>
<p>- 视口宽度 <strong>&lt; 768px</strong> → 规则不生效（忽略样式）</p>
<p>- 这是&quot;Mobile-First&quot;写法的基础——默认样式给手机，<code>min-width</code> 逐步增强给更大屏幕。</p>

<p><strong>两条规则同时存在时（1400px 宽度）：</strong></p>

<p>1400px ≥ 768px → 第一条生效 ✅</p>
<p>1400px ≥ 1200px → 第二条也生效 ✅</p>

<p><strong>两条都生效！CSS 的层叠规则决定最终样式：</strong></p>
<p>- 两条规则中相同的属性，<strong>后写的（声明顺序靠后的）优先</strong></p>
<p>- 所以应该总是把 <code>min-width</code> 从小到大排列写：先 768px，再 1200px</p>
<p>- 这样大屏幕的样式会自然覆盖中等屏幕的样式</p>

<p><strong>关键记忆（From Day 4）：</strong> Media queries don&#x27;t &quot;turn off&quot; — they cascade. The last rule wins. Order matters!</p>

<p>&lt;/details&gt;</p>

<hr/>

<p>💡 <em>复习巩固记忆，螺旋式上升。</em></p>
<p><em>Review reinforces memory — spiral upward.</em></p>

<p>📅 明天继续新内容！/ New content resumes tomorrow!</p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-17</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-17</guid>
      <pubDate>Tue, 17 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 4 / System Design Day 4 — 负载均衡 / Load Balancing</h2>

<p>&gt; <strong>基础阶段 Foundation Phase</strong> | 预计阅读时间 ~3-4 分钟</p>

<hr/>

<h2>场景引入 / Scenario</h2>

<p>想象你开了一家超级火爆的奶茶店 🧋。第一天只有一个收银台，没问题。但第二天去大众点评上了热搜，突然排队排到门外一百米。怎么办？你开了第二个、第三个收银台，还派了一个引导员在门口，告诉每个客人该去哪个收银台排队。</p>

<p>这个&quot;引导员&quot;就是<strong>负载均衡器（Load Balancer）</strong>。</p>

<p>Imagine you open a super-popular boba tea shop. Day one: one cashier, no problem. Day two: you go viral, and there&#x27;s a 100-meter queue outside. Solution? You open more cashier lanes and station someone at the entrance directing each customer to the shortest line.</p>

<p>That person at the entrance is your <strong>Load Balancer</strong>.</p>

<hr/>

<h2>架构图 / Architecture Diagram</h2>

<pre><code>
                        ┌─────────────────────────────┐
                        │         用户请求             │
                        │     Incoming Requests        │
                        └──────────────┬──────────────┘
                                       │
                                       ▼
                        ┌─────────────────────────────┐
                        │       Load Balancer          │
                        │     负载均衡器               │
                        │  (Nginx / AWS ALB / HAProxy) │
                        └───────┬──────┬──────┬───────┘
                                │      │      │
                     ┌──────────┘      │      └──────────┐
                     ▼                 ▼                  ▼
              ┌────────────┐  ┌────────────┐  ┌────────────┐
              │  Server 1  │  │  Server 2  │  │  Server 3  │
              │  服务器 1  │  │  服务器 2  │  │  服务器 3  │
              │  ████████  │  │  ████      │  │  ██        │
              │ (80% load) │  │ (40% load) │  │ (20% load) │
              └────────────┘  └────────────┘  └────────────┘
                                       │
                        ┌──────────────┘
                        │
                        ▼
              ┌─────────────────────┐
              │   Shared Database   │
              │    共享数据库        │
              └─────────────────────┘
</code></pre>

<hr/>

<h2>核心概念 / Key Concepts</h2>

<h3>负载均衡算法 / Load Balancing Algorithms</h3>

<p><strong>1. Round Robin（轮询）</strong></p>
<p>- 依次把请求分给每台服务器，循环往复</p>
<p>- 类比：收银台依次叫号</p>
<p>- 适用：服务器性能相同、请求处理时间相近的场景</p>

<p><strong>2. Weighted Round Robin（加权轮询）</strong></p>
<p>- 性能强的服务器分配更多请求（权重更高）</p>
<p>- 类比：有个收银员超快，就多给她排队</p>
<p>- 适用：服务器配置不均匀的场景</p>

<p><strong>3. Least Connections（最少连接）</strong></p>
<p>- 把新请求分给当前连接数最少的服务器</p>
<p>- 类比：去排队最短的那个收银台</p>
<p>- 适用：请求处理时长差异大的场景（如文件上传 vs 简单查询）</p>

<p><strong>4. IP Hash（IP 哈希）</strong></p>
<p>- 根据客户端 IP 地址决定路由到哪台服务器</p>
<p>- 同一个用户总是被路由到同一台服务器</p>
<p>- 适用：需要会话粘性（Session Stickiness）的场景</p>

<h3>为什么这样设计？/ Why This Design?</h3>

<p>| 目标 Goal | 解决方案 Solution |</p>
<p>|----------|-----------------|</p>
<p>| 高可用 High Availability | 一台服务器挂了，流量自动转移 |</p>
<p>| 水平扩展 Horizontal Scaling | 加新服务器，不改代码 |</p>
<p>| 性能 Performance | 避免单点瓶颈，减少响应时间 |</p>
<p>| 健康检查 Health Checks | LB 自动剔除故障节点 |</p>

<h3>两种类型 / Two Types</h3>

<p><strong>Layer 4 (Transport Layer) LB</strong></p>
<p>- 基于 IP + TCP/UDP 端口路由</p>
<p>- 速度快，但&quot;看不懂&quot;请求内容</p>
<p>- 类比：只看信封地址，不看信的内容</p>

<p><strong>Layer 7 (Application Layer) LB</strong></p>
<p>- 基于 HTTP 头、URL、Cookie 等路由</p>
<p>- 更智能（可以把 <code>/api</code> 路由到 API 服务器，把 <code>/static</code> 路由到 CDN）</p>
<p>- 类比：根据信的内容决定投递给哪个部门</p>
<p>- 性能稍低，但灵活得多</p>

<hr/>

<h2>别踩这个坑 / Don&#x27;t Fall Into This Trap</h2>

<h3>坑 1：有状态的服务器（Stateful Servers）</h3>

<p>❌ <strong>错误做法：</strong> 把用户 Session 存在单台服务器的内存里</p>
<pre><code>
用户第1次请求 → Server 1 (Session 存在这里)
用户第2次请求 → Server 2 (找不到 Session！用户被登出)
</code></pre>

<p>✅ <strong>正确做法：</strong> Session 外置到共享存储</p>
<pre><code>
用户第1次请求 → Server 1 → 把 Session 写入 Redis
用户第2次请求 → Server 2 → 从 Redis 读 Session ✓
</code></pre>

<p><strong>关键原则：服务器要做到&quot;无状态&quot;(Stateless)，所有状态都存外部！</strong></p>

<h3>坑 2：负载均衡器自身成为单点故障</h3>

<p>如果 LB 本身挂了怎么办？</p>

<p>✅ <strong>解决方案：</strong> 部署主备 LB（Active-Passive）或使用 DNS 轮询 + 多 LB</p>

<h3>坑 3：健康检查不够频繁</h3>

<p>LB 依赖健康检查（Health Check）来知道哪台服务器挂了。如果检查间隔太长（比如 60s），可能有 1 分钟的流量打到死服务器上。</p>

<p>✅ 生产环境通常设置：每 5-10 秒一次健康检查。</p>

<hr/>

<h2>延伸阅读 / Going Deeper</h2>

<p>- 昨天（Day 3）我们聊了 HTTP/REST，现在你知道 LB 就工作在 HTTP 这一层之上</p>
<p>- 下周我们会聊<strong>数据库扩展</strong>，届时 LB 的概念还会出现（读写分离 + 连接池）</p>
<p>- 如果你听说过 <strong>Nginx、HAProxy、AWS ALB/NLB</strong>，它们都是负载均衡器的具体实现</p>

<hr/>

<p><em>Day 4 / 100 — 系统设计基础系列 System Design Foundations</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 4 / Algorithms Day 4 — #49 Group Anagrams (Medium) — Arrays &amp; Hashing</h2>

<p>&gt; <strong>基础阶段 Foundation Phase</strong> | 预计阅读时间 ~3-4 分钟</p>

<hr/>

<h2>现实类比 / Real-World Analogy</h2>

<p>想象你在邮局分拣包裹。每个包裹上写的是打乱顺序的地址，比如 &quot;acts&quot;, &quot;cats&quot;, &quot;tacs&quot; 其实都是同一个地方（字母相同，顺序不同）。你的任务是把所有&quot;相同地址&quot;的包裹放到同一个箱子里。</p>

<p>怎么判断两个地址&quot;本质相同&quot;？把字母排个序，如果排序后一样，就是同一个地址！</p>

<p>At the post office, sorting packages with scrambled addresses: &quot;acts&quot;, &quot;cats&quot;, &quot;tacs&quot; all go to the same place. Your job: group them together. The trick? Sort the letters — if they match after sorting, they&#x27;re anagrams!</p>

<hr/>

<h2>题目 / Problem Statement</h2>

<p><strong>给定一个字符串数组，将所有字母异位词（anagram）分组在一起。</strong></p>

<p>Given an array of strings, group the anagrams together.</p>

<pre><code>
输入 Input:  [&quot;eat&quot;,&quot;tea&quot;,&quot;tan&quot;,&quot;ate&quot;,&quot;nat&quot;,&quot;bat&quot;]
输出 Output: [[&quot;bat&quot;],[&quot;nat&quot;,&quot;tan&quot;],[&quot;ate&quot;,&quot;eat&quot;,&quot;tea&quot;]]
</code></pre>

<p>字母异位词 = 由相同字母以不同顺序构成的单词</p>
<p>Anagram = words with the same letters in different order</p>

<hr/>

<h2>思路解析 / Step-by-Step Walkthrough</h2>

<p><strong>关键洞察 Key Insight：</strong></p>
<p>两个字符串互为 anagram，当且仅当它们排序后相同。</p>
<p>Two strings are anagrams if and only if their sorted versions are identical.</p>

<p>所以我们用排序后的字符串作为<strong>哈希表的 key</strong>，把所有 anagram 归到同一个桶（bucket）里。</p>

<h3>追踪过程 / Trace Through the Example</h3>

<p>输入：<code>[&quot;eat&quot;,&quot;tea&quot;,&quot;tan&quot;,&quot;ate&quot;,&quot;nat&quot;,&quot;bat&quot;]</code></p>

<p>| 当前单词 Word | 排序后 Sorted Key | 哈希表状态 HashMap State |</p>
<p>|------------|----------------|----------------------|</p>
<p>| &quot;eat&quot; | &quot;aet&quot; | {&quot;aet&quot;: [&quot;eat&quot;]} |</p>
<p>| &quot;tea&quot; | &quot;aet&quot; | {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;]} |</p>
<p>| &quot;tan&quot; | &quot;ant&quot; | {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;], &quot;ant&quot;: [&quot;tan&quot;]} |</p>
<p>| &quot;ate&quot; | &quot;aet&quot; | {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;,&quot;ate&quot;], &quot;ant&quot;: [&quot;tan&quot;]} |</p>
<p>| &quot;nat&quot; | &quot;ant&quot; | {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;,&quot;ate&quot;], &quot;ant&quot;: [&quot;tan&quot;,&quot;nat&quot;]} |</p>
<p>| &quot;bat&quot; | &quot;abt&quot; | {&quot;aet&quot;: [...], &quot;ant&quot;: [...], &quot;abt&quot;: [&quot;bat&quot;]} |</p>

<p>最终取哈希表的所有 values → <code>[[&quot;eat&quot;,&quot;tea&quot;,&quot;ate&quot;], [&quot;tan&quot;,&quot;nat&quot;], [&quot;bat&quot;]]</code></p>

<hr/>

<h2>Python 解法 / Python Solution</h2>

<pre><code>
from collections import defaultdict

def groupAnagrams(strs: list[str]) -&gt; list[list[str]]:
    # Use a defaultdict so we can append without checking if key exists
    # 用 defaultdict，省去手动判断 key 是否存在的麻烦
    anagram_map = defaultdict(list)
    
    for word in strs:
        # Sort the word&#x27;s characters to create the canonical key
        # 对字母排序，得到这组 anagram 的&quot;标准形式&quot;
        # e.g., &quot;eat&quot; -&gt; sorted(&quot;eat&quot;) -&gt; [&#x27;a&#x27;,&#x27;e&#x27;,&#x27;t&#x27;] -&gt; &quot;aet&quot;
        key = &quot;&quot;.join(sorted(word))
        
        # Append this word to the bucket for its key
        # 把当前单词放入对应的桶
        anagram_map[key].append(word)
    
    # Return all the groups (values of the map)
    # 返回所有分组
    return list(anagram_map.values())
</code></pre>

<h3>手动验证 / Manual Verification</h3>

<p>让我们用 <code>[&quot;eat&quot;,&quot;tea&quot;,&quot;tan&quot;,&quot;ate&quot;,&quot;nat&quot;,&quot;bat&quot;]</code> 逐步跑代码：</p>

<p>1. <code>word = &quot;eat&quot;</code> → <code>sorted(&quot;eat&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;e&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;aet&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [&quot;eat&quot;]}</code></p>
<p>2. <code>word = &quot;tea&quot;</code> → <code>sorted(&quot;tea&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;e&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;aet&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;]}</code></p>
<p>3. <code>word = &quot;tan&quot;</code> → <code>sorted(&quot;tan&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;n&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;ant&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [...], &quot;ant&quot;: [&quot;tan&quot;]}</code></p>
<p>4. <code>word = &quot;ate&quot;</code> → <code>sorted(&quot;ate&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;e&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;aet&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [&quot;eat&quot;,&quot;tea&quot;,&quot;ate&quot;], &quot;ant&quot;: [&quot;tan&quot;]}</code></p>
<p>5. <code>word = &quot;nat&quot;</code> → <code>sorted(&quot;nat&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;n&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;ant&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [...], &quot;ant&quot;: [&quot;tan&quot;,&quot;nat&quot;]}</code></p>
<p>6. <code>word = &quot;bat&quot;</code> → <code>sorted(&quot;bat&quot;)</code> = <code>[&#x27;a&#x27;,&#x27;b&#x27;,&#x27;t&#x27;]</code> → <code>key = &quot;abt&quot;</code> → <code>anagram_map = {&quot;aet&quot;: [...], &quot;ant&quot;: [...], &quot;abt&quot;: [&quot;bat&quot;]}</code></p>

<p>最终返回 <code>[[&quot;eat&quot;,&quot;tea&quot;,&quot;ate&quot;], [&quot;tan&quot;,&quot;nat&quot;], [&quot;bat&quot;]]</code> ✅ 与题目期望输出一致！</p>

<hr/>

<h2>时间/空间复杂度 / Complexity Analysis</h2>

<p><strong>时间复杂度 Time Complexity: O(n × k log k)</strong></p>
<p>- n = 字符串数量（number of strings）</p>
<p>- k = 最长字符串的长度（max string length）</p>
<p>- 对每个字符串排序 = O(k log k)，总共 n 个字符串</p>

<p><strong>空间复杂度 Space Complexity: O(n × k)</strong></p>
<p>- 哈希表存储所有字符串的副本</p>
<p>- 最坏情况：所有字符串都不是 anagram，每个单独一个桶</p>

<hr/>

<h2>边界情况 / Edge Cases</h2>

<pre><code>
# 空数组 Empty input
groupAnagrams([])  # → []

# 单个字符串 Single string
groupAnagrams([&quot;a&quot;])  # → [[&quot;a&quot;]]

# 所有字符串都是 anagram All are anagrams
groupAnagrams([&quot;abc&quot;,&quot;bca&quot;,&quot;cab&quot;])  # → [[&quot;abc&quot;,&quot;bca&quot;,&quot;cab&quot;]]

# 没有 anagram 对 No anagram pairs
groupAnagrams([&quot;abc&quot;,&quot;def&quot;,&quot;ghi&quot;])  # → [[&quot;abc&quot;],[&quot;def&quot;],[&quot;ghi&quot;]]
</code></pre>

<hr/>

<h2>举一反三 / Pattern Recognition</h2>

<p><strong>这道题的模式：用排序/哈希创建&quot;规范形式&quot;（Canonical Form）</strong></p>

<p>当你需要&quot;把等价的东西归类&quot;时，找到一个好的 key 是关键。</p>

<p><strong>同类变体 / Follow-up Variations:</strong></p>

<p>1. <strong>#242 Valid Anagram（Day 2 做过！）</strong> — 判断两个字符串是否互为 anagram，现在你应该更理解为什么用哈希表了</p>

<p>2. <strong>用字符计数作 key（优化版）</strong> — 不排序，而是统计 26 个字母的频率，构成一个 tuple 作 key。时间复杂度降到 O(n × k)：</p>
<pre><code>
   key = tuple(Counter(word).values())  # 不推荐，顺序不固定
   # 更好的方式：
   count = [0] * 26
   for c in word:
       count[ord(c) - ord(&#x27;a&#x27;)] += 1
   key = tuple(count)  # e.g., &quot;eat&quot; -&gt; (1,0,0,0,1,0,...,1,...) [a=1,e=1,t=1]
</code></pre>

<p>3. <strong>思考扩展：</strong> 如果字符串包含 Unicode 字符怎么办？用 <code>Counter</code> 而非固定 26 位数组</p>

<hr/>

<p><em>Day 4 / 100 — Arrays &amp; Hashing 系列</em></p>
<p><em>昨天 Day 3：Two Sum | 明天 Day 5：Top K Frequent Elements</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 4 / Soft Skills Day 4 — 失败与成长 / Failure &amp; Growth</h2>

<p>&gt; <strong>基础阶段 Foundation Phase</strong> | 预计阅读时间 ~2-3 分钟</p>

<hr/>

<h2>今日问题 / Today&#x27;s Question</h2>

<p>&gt; &quot;Describe a project that failed or didn&#x27;t meet expectations. What did you learn?&quot;</p>
<p>&gt; 描述一个失败的或未达到预期的项目。你从中学到了什么？</p>

<hr/>

<h2>为什么这很重要 / Why This Matters</h2>

<p>这个问题是面试官用来区分<strong>普通候选人</strong>和<strong>优秀候选人</strong>的分水岭。</p>

<p>大多数人要么：</p>
<p>- 找借口（&quot;那个产品经理需求一直在变…&quot;）</p>
<p>- 给出假失败（&quot;我太追求完美了！&quot;）</p>

<p>真正优秀的工程师知道：失败是学习的压缩包。能清晰复盘失败，说明你有自我意识、有成长心态、有责任感。</p>

<p>This question separates candidates who are self-aware from those who aren&#x27;t. Great engineers treat failure as dense learning. If you can articulate a real failure with clarity, you signal maturity, accountability, and a growth mindset.</p>

<hr/>

<h2>STAR 框架拆解 / STAR Framework Breakdown</h2>

<p><strong>Situation（情境）:</strong> 设置背景 — 项目是什么？团队多大？时间线如何？</p>

<p><strong>Task（任务）:</strong> 你的角色 — 你负责什么？期望是什么？</p>

<p><strong>Action（行动）:</strong> 你做了什么 — 包括那些事后回想起来的&quot;错误决定&quot;</p>

<p><strong>Result（结果）:</strong> 真实的结果 — 项目延期？功能被砍？用户不买账？</p>

<p><strong>+Learning（学习）:</strong> ⭐ 这是整个回答的精华 — 你具体学到了什么？有什么改变？</p>

<hr/>

<h2>❌ 糟糕的回答 / Bad Approach</h2>

<p>&gt; &quot;我们做了一个推荐系统，但效果没有预期好。这让我意识到我应该更仔细地沟通需求。&quot;</p>

<p><strong>问题在哪？</strong></p>
<p>- 没有具体细节（什么推荐系统？多大的影响？）</p>
<p>- &quot;更仔细地沟通&quot;——太泛了，面试官听到这句话已经睡着了</p>
<p>- 没有说明<strong>你个人</strong>在失败中的角色</p>
<p>- 学到的教训没有被<strong>后续行动</strong>验证</p>

<hr/>

<h2>✅ 好的回答 / Good Approach</h2>

<p>&gt; &quot;In my second year at [Company], I led a migration of our user notification system from a monolithic service to an event-driven architecture. The business goal was to reduce notification latency from ~3 seconds to under 500ms and improve reliability.</p>
<p>&gt;</p>
<p>&gt; 我的任务是设计新架构并协调三个团队的迁移工作，预期 Q2 上线。</p>
<p>&gt;</p>
<p>&gt; 我犯的关键错误：我低估了消息幂等性（idempotency）的问题。我假设下游消费者已经处理好了重复消息，但实际上没有。上线后，部分用户在一次事件中收到了 3-5 条重复通知，引发了大量投诉，我们不得不回滚。</p>
<p>&gt;</p>
<p>&gt; We rolled back within 6 hours, which itself was a success — but the original go-live failed.</p>
<p>&gt;</p>
<p>&gt; 从这次失败中，我有两个具体改变：</p>
<p>&gt; 1. <strong>我开始在所有 event-driven 设计的 design doc 里加一节 &#x27;Idempotency Guarantees&#x27;</strong>，明确列出哪一层负责去重</p>
<p>&gt; 2. <strong>我们建立了 chaos testing 流程</strong>，在 staging 环境模拟消息重投递，在那以后我们再没有出现类似问题</p>
<p>&gt;</p>
<p>&gt; 三个月后，同一个团队完成了迁移，延迟确实降到了 420ms。我认为能拿到这个结果，部分原因就是第一次的失败让我们想清楚了真正的难点。&quot;</p>

<hr/>

<h2>为什么这个回答好？/ Why This Works</h2>

<p>✅ <strong>具体技术细节</strong> — 幂等性问题，不是模糊的&quot;沟通问题&quot;</p>
<p>✅ <strong>诚实承担责任</strong> — &quot;我犯的关键错误&quot;，没有推锅</p>
<p>✅ <strong>量化影响</strong> — 3-5 条重复通知，回滚 6 小时内完成</p>
<p>✅ <strong>学习有证据</strong> — 不是说&quot;我学会了要考虑幂等性&quot;，而是说&quot;我在所有后续 design doc 里加了这一节&quot;</p>
<p>✅ <strong>故事有结尾</strong> — 三个月后成功了，说明学习真的有效</p>

<hr/>

<h2>Senior/Staff 级别加分项 / Senior/Staff Level Tips</h2>

<p>如果你是 Senior 或 Staff 候选人，面试官想听到更多的是<strong>系统性改变</strong>，而非个人教训：</p>

<p>- &quot;我把这个 checklist 推广到了整个团队&quot; (team impact)</p>
<p>- &quot;我们更新了 runbook，现在新 engineer onboarding 时会学到这个&quot; (process change)</p>
<p>- &quot;这次失败推动了我们建立 incident review 文化&quot; (cultural change)</p>

<p><strong>层级越高，你的学习边界越大。</strong> Junior 学到的是&quot;我应该更仔细地测试&quot;；Staff 学到的是&quot;我们整个组织的测试文化需要改变&quot;。</p>

<hr/>

<h2>你可以改编的模板 / Scenario Template</h2>

<pre><code>
情境: 我在 [公司/项目] 负责 [技术项目]，目标是 [业务目标]。
错误: 我的关键判断失误是 [具体技术/流程错误]。
影响: 导致了 [具体后果，数字化]。
学习1: 从此以后，我 [具体新习惯/流程，可验证]。
学习2: 我把这个教训 [推广/文档化] 到了 [范围]。
后续: [N 个月后，最终的结果是...]。
</code></pre>

<hr/>

<h2>关键要点 / Key Takeaways</h2>

<p>1. <strong>选真实的失败</strong> — 面试官能辨别出假失败。真失败才有说服力</p>
<p>2. <strong>从个人行动出发</strong> — &quot;我们失败了&quot;比&quot;我做了错误决定&quot;弱 10 倍</p>
<p>3. <strong>学习要有后续行动</strong> — &quot;我意识到&quot;不够，&quot;我从那以后改变了&quot;才有力量</p>
<p>4. <strong>结局不一定非要成功</strong> — &quot;项目被取消，但我建立的系统依然在生产环境跑着&quot;也是好结尾</p>

<hr/>

<p><em>Day 4 / 100 — 行为面试系列 Behavioral Interview Series</em></p>
<p><em>昨天 Day 3：与领导意见相左 | 明天 Day 5：时间管理与优先级</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 4 / Frontend Day 4 — 响应式设计与媒体查询 / Responsive Design &amp; Media Queries</h2>

<p>&gt; <strong>基础阶段 Foundation Phase</strong> | 预计阅读时间 ~2-3 分钟</p>

<hr/>

<h2>猜猜这段代码的行为？/ What Does This Code Do?</h2>

<pre><code>
.container {
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;
}

@media (min-width: 768px) {
  .container {
    max-width: 960px;
    margin: 0 auto;
    padding: 0 40px;
  }
}

@media (min-width: 1200px) {
  .container {
    max-width: 1140px;
    padding: 0 60px;
  }
}
</code></pre>

<p><strong>问：当浏览器宽度是 800px 时，<code>.container</code> 的实际宽度和 padding 是多少？</strong></p>
<p><strong>Q: When the browser is 800px wide, what are the actual width and padding?</strong></p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;点击查看答案 / Click for Answer&lt;/summary&gt;</p>

<p><strong>答案：宽度 = 800px（100% of viewport），padding = 0 40px</strong></p>

<p>理由：</p>
<p>- 800px &gt; 768px → 第一个 <code>@media</code> 规则生效 ✓</p>
<p>- 800px &lt; 1200px → 第二个 <code>@media</code> 规则不生效 ✗</p>
<p>- <code>max-width: 960px</code> 生效，但 800px &lt; 960px，所以实际宽度被 <code>width: 100%</code> 控制 = 800px</p>
<p>- <code>margin: 0 auto</code> 生效（container 会居中，但由于宽度是 100%，看不出来）</p>
<p>- <code>padding</code> 被第一个媒体查询覆盖为 <code>0 40px</code></p>

<p><strong>所以：width = 800px，padding = 0 40px</strong> ✅</p>
<p>&lt;/details&gt;</p>

<hr/>

<h2>核心概念 / Core Concepts</h2>

<h3>什么是响应式设计？/ What Is Responsive Design?</h3>

<p>同一套 HTML/CSS，在手机、平板、电脑上都好看好用。</p>
<p>不是做三个不同的页面，而是用<strong>弹性布局 + 媒体查询</strong>适配所有屏幕。</p>

<p>One codebase, all screen sizes. Not three separate pages — flexible layouts + media queries.</p>

<h3>媒体查询语法 / Media Query Syntax</h3>

<pre><code>
/* 基础语法 Basic syntax */
@media [media-type] [and/not/only] (condition) {
  /* CSS rules */
}

/* 常见断点 Common breakpoints */
/* Mobile first approach (推荐!) */
/* Base styles: mobile */
.element { font-size: 14px; }

@media (min-width: 576px)  { /* sm - Large phones */ }
@media (min-width: 768px)  { /* md - Tablets */ }
@media (min-width: 992px)  { /* lg - Desktops */ }
@media (min-width: 1200px) { /* xl - Large desktops */ }
</code></pre>

<hr/>

<h2>两种策略 / Two Approaches</h2>

<pre><code>
Mobile First (推荐 ✓)          Desktop First (常见但不推荐)
────────────────────           ──────────────────────────
先写手机样式，                   先写桌面样式，
用 min-width 往上覆盖            用 max-width 往下覆盖

Base → small screens            Base → large screens
↑ override for larger           ↓ override for smaller

好处:                            缺点:
✅ 性能更好 (mobile loads less)  ❌ 移动端加载冗余样式
✅ 优先考虑移动端体验            ❌ 思维反直觉
✅ Progressive enhancement       ❌ Graceful degradation
</code></pre>

<hr/>

<h2>实战代码 / Practical Example</h2>

<pre><code>
/* Mobile First: 先写最小屏幕的样式 */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;  /* 1 column on mobile */
  gap: 16px;
  padding: 16px;
}

/* 平板: 2 列 */
@media (min-width: 768px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);  /* 2 columns */
    gap: 24px;
    padding: 24px;
  }
}

/* 桌面: 3 列 */
@media (min-width: 1200px) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);  /* 3 columns */
    gap: 32px;
    padding: 32px;
  }
}
</code></pre>

<pre><code>
手机 (&lt; 768px)    平板 (768-1199px)    桌面 (≥ 1200px)
──────────────    ─────────────────    ───────────────
┌──────────┐      ┌─────┐ ┌─────┐     ┌───┐ ┌───┐ ┌───┐
│  Card 1  │      │Card1│ │Card2│     │ 1 │ │ 2 │ │ 3 │
├──────────┤      ├─────┤ ├─────┤     ├───┤ ├───┤ ├───┤
│  Card 2  │      │Card3│ │Card4│     │ 4 │ │ 5 │ │ 6 │
├──────────┤      └─────┘ └─────┘     └───┘ └───┘ └───┘
│  Card 3  │
└──────────┘
</code></pre>

<hr/>

<h2>你可能不知道 / You Might Not Know</h2>

<h3>Viewport Meta Tag — 必不可少！</h3>

<pre><code>
&lt;!-- 没有这个，媒体查询在手机上不会正常工作！ --&gt;
&lt;!-- Without this, media queries won&#x27;t work on mobile! --&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
</code></pre>

<p><strong>为什么？</strong> 手机浏览器默认假装自己是 980px 宽的桌面（为了渲染老网站）。加了这个 meta tag，才告诉它&quot;就用设备真实宽度&quot;。</p>

<p>Why? Mobile browsers default to pretending they&#x27;re a ~980px desktop viewport (legacy web compatibility). This tag tells them: use the real device width.</p>

<h3>媒体查询也能针对其他特性 / Other Media Features</h3>

<pre><code>
/* 暗色模式 Dark mode */
@media (prefers-color-scheme: dark) {
  body { background: #1a1a1a; color: #f0f0f0; }
}

/* 减少动画（无障碍）Reduced motion (accessibility) */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* 横竖屏 Orientation */
@media (orientation: landscape) {
  .sidebar { display: block; }
}
</code></pre>

<hr/>

<h2>Mini Challenge 小挑战</h2>

<pre><code>
/* 这段代码中，如果屏幕宽度是 600px，
   .box 的背景色是什么？ */
/* What is the background color of .box at 600px? */

.box { background: red; }

@media (min-width: 500px) {
  .box { background: blue; }
}

@media (max-width: 700px) {
  .box { background: green; }
}

@media (min-width: 550px) and (max-width: 650px) {
  .box { background: yellow; }
}
</code></pre>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;答案 / Answer&lt;/summary&gt;</p>

<p><strong>答案：yellow（黄色）</strong></p>

<p>在 600px 时：</p>
<p>1. <code>red</code> — 基础样式 ✓</p>
<p>2. <code>min-width: 500px</code> → 600 ≥ 500 → 覆盖为 <code>blue</code> ✓</p>
<p>3. <code>max-width: 700px</code> → 600 ≤ 700 → 覆盖为 <code>green</code> ✓</p>
<p>4. <code>min-width: 550px and max-width: 650px</code> → 550 ≤ 600 ≤ 650 → 覆盖为 <code>yellow</code> ✓</p>

<p>CSS 层叠规则：后声明的规则（同优先级时）覆盖先声明的。最后生效的是 <code>yellow</code>。</p>
<p>&lt;/details&gt;</p>

<hr/>

<p><em>Day 4 / 100 — CSS 基础系列 CSS Fundamentals</em></p>
<p><em>昨天 Day 3：CSS Grid | 明天 Day 5：CSS 动画与 Transitions</em></p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 4 — 分词（Tokenization）：LLM 眼中的文字 / Tokenization: How LLMs See Text</h2>

<p>&gt; <strong>基础阶段 Foundation Phase</strong> | 预计阅读时间 ~2-3 分钟</p>

<hr/>

<h2>直觉解释 / Intuitive Explanation</h2>

<p>你有没有想过，为什么 ChatGPT 有时候连字都数不清楚？比如问它&quot;&#x27;strawberry&#x27; 这个词里有几个 r？&quot;，它可能回答&quot;2 个&quot;而不是正确答案&quot;3 个&quot;？</p>

<p>这就是<strong>分词（Tokenization）</strong>在背后作怪。</p>

<p>LLM 看到的世界，不是一个个字母，也不是一个个单词——而是<strong>tokens（词元）</strong>。一个 token 可能是：</p>
<p>- 一个完整的英语单词（如 <code>hello</code>）</p>
<p>- 一个常见单词的一部分（如 <code>token</code> → <code>tok</code> + <code>en</code>）</p>
<p>- 一个标点符号或空格</p>
<p>- 一个汉字（通常 1 个汉字 = 1 个 token）</p>

<p>Have you ever wondered why ChatGPT struggles to count letters? Ask &quot;how many &#x27;r&#x27;s in strawberry?&quot; and it might say 2 instead of 3. That&#x27;s tokenization at work — the model never sees individual letters.</p>

<hr/>

<h2>Tokenization 是怎么工作的？/ How Does It Work?</h2>

<h3>主流算法：BPE（Byte Pair Encoding）</h3>

<p><strong>训练阶段（一次性，在模型训练前）:</strong></p>

<p>1. 从字母级别开始：把所有文字拆成单个字符</p>
<p>2. 统计最常出现的相邻字符对，合并成一个新 token</p>
<p>3. 重复 N 次（N 通常是几万次），直到词汇表大小达标</p>

<p><strong>效果：</strong>常见的词/词根保持完整，罕见的词被拆开。</p>

<pre><code>
&quot;tokenization&quot; 可能被拆成：
&quot;token&quot; + &quot;ization&quot;
或
&quot;token&quot; + &quot;iz&quot; + &quot;ation&quot;
（取决于训练数据中这些片段的频率）
</code></pre>

<hr/>

<h2>可视化追踪 / Visual Trace</h2>

<pre><code>
原始文本 Raw Text:
&quot;LLMs are transformers&quot;

          ↓ Tokenizer

Token IDs:   [  47,    2860,    527,   83386 ]
             &quot;LL&quot;  &quot;Ms are&quot;  &quot; trans&quot;  &quot;formers&quot;
              ↑
           注意 &quot;LLMs&quot; 被拆成多个 token！

(以上是示意，实际 token 边界取决于具体模型的词汇表)
</code></pre>

<pre><code>
模型看到的不是:   L L M s   a r e   t r a n s f o r m e r s
而是:            [token1] [token2] [token3] [token4]

就像把句子看成 LEGO 块，而不是沙粒
Like seeing sentences as LEGO bricks, not individual grains of sand
</code></pre>

<hr/>

<h2>为什么这很重要？/ Why Does This Matter?</h2>

<h3>1. 解释了&quot;数字母&quot;的 bug 🔢</h3>

<p><code>&quot;strawberry&quot;</code> → 可能被 token 化为 <code>&quot;straw&quot;</code> + <code>&quot;berry&quot;</code></p>
<p>模型数 r 时是数 token 里的 r，不是逐字母数。</p>
<p>这是已知的 LLM 局限，不是 bug，而是架构的内在特性。</p>

<h3>2. 解释了 Token 计费 💰</h3>

<p>OpenAI 按 token 而非单词收费：</p>
<p>- 英文：1 token ≈ 0.75 个单词</p>
<p>- 中文：1 个汉字 ≈ 1 个 token（有时更多）</p>
<p>- 代码：注释和空格也算 token，很&quot;贵&quot;</p>

<pre><code>
&quot;Hello, world!&quot; = 4 tokens: [&quot;Hello&quot;, &quot;,&quot;, &quot; world&quot;, &quot;!&quot;]
&quot;你好世界&quot; = 4 tokens: [&quot;你&quot;,&quot;好&quot;,&quot;世&quot;,&quot;界&quot;]（每个汉字一个）
</code></pre>

<h3>3. 解释了 Context Window 的实际限制 📏</h3>

<p>GPT-4 的 128K context window 是 token 数，不是字数。</p>
<p>如果你的提示词里中文很多，等效&quot;容量&quot;就比英文少。</p>

<hr/>

<h2>代码片段 / Code Snippet</h2>

<pre><code>
# 用 tiktoken 查看 GPT 系列模型如何分词
# pip install tiktoken
import tiktoken

# Load GPT-4&#x27;s tokenizer
enc = tiktoken.encoding_for_model(&quot;gpt-4&quot;)

# Encode a string into token IDs
tokens = enc.encode(&quot;tokenization is fascinating&quot;)
print(tokens)
# Output: [4037, 2065, 374, 27387] (actual IDs may vary)

# Decode back
decoded = [enc.decode([t]) for t in tokens]
print(decoded)
# Output: [&#x27;token&#x27;, &#x27;ization&#x27;, &#x27; is&#x27;, &#x27; fascinating&#x27;]
# Notice: &quot;tokenization&quot; is split into 2 tokens!

# Count tokens in a prompt
text = &quot;strawberry&quot;
count = len(enc.encode(text))
print(f&quot;&#x27;{text}&#x27; = {count} token(s)&quot;)
# Output: &#x27;strawberry&#x27; = 1 token(s)
# 所以 GPT-4 看 &quot;strawberry&quot; 是 1 整个 token，
# 难怪数不出来里面有 3 个 r！
</code></pre>

<hr/>

<h2>实际应用 / Applications</h2>

<p>| 场景 Use Case | Tokenization 的影响 Impact |</p>
<p>|-------------|--------------------------|</p>
<p>| Prompt Engineering | 压缩 token 数 → 降低成本、扩大 context 空间 |</p>
<p>| RAG 系统 | Chunking 时要按 token 数切，不按字符数切 |</p>
<p>| 微调 Fine-tuning | 训练数据的 token 分布影响模型性能 |</p>
<p>| 多语言支持 | 不同语言的 token 效率差异很大（英文 &gt; 中文 &gt; 某些小语种）|</p>

<hr/>

<h2>有趣彩蛋 / Fun Fact</h2>

<p>GPT-4 的词汇表有 ~100,256 个 token（cl100k_base encoding）。</p>
<p>其中包括：整个英文单词、代码关键词，甚至 <code> Python</code>（带前置空格）和 <code>Python</code> 是<strong>不同的 token</strong>！</p>

<p>空格有时候是 token 的一部分，这就是为什么 prompt 里的格式细节可能影响输出——模型在 token 层面感知到了差异。</p>

<hr/>

<p><em>Day 4 / 100 — AI 基础系列 AI Foundations</em></p>
<p><em>昨天 Day 3：AI News Roundup | 明天 Day 5：Embeddings — 语义的数学表达</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-16</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-16</guid>
      <pubDate>Mon, 16 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 3 / System Design Day 3</h2>
<p><strong>HTTP/HTTPS &amp; REST APIs</strong></p>
<p><em>Category: Fundamentals | Difficulty: Beginner | Phase: Foundation</em></p>

<hr/>

<h2>想象你在设计... / Imagine You&#x27;re Building...</h2>

<p>想象你是一家餐厅的服务员。客人（浏览器）坐下来，告诉你他想要什么（HTTP请求），你去厨房（服务器）取回食物，然后送回来（HTTP响应）。这就是HTTP的本质——一种双方约定好的&quot;点餐协议&quot;。</p>

<p>Imagine you&#x27;re a waiter at a restaurant. The customer (browser) tells you what they want (HTTP request), you go to the kitchen (server), and bring back the food (HTTP response). That&#x27;s HTTP in a nutshell — a standardized &quot;ordering protocol&quot; both sides agree on.</p>

<hr/>

<h2>架构图 / Architecture Diagram</h2>

<pre><code>
  CLIENT (Browser/App)
        │
        │  HTTP Request
        │  GET /api/users/123
        │  Headers: {Authorization: &quot;Bearer token...&quot;}
        ▼
  ┌─────────────────────┐
  │    LOAD BALANCER     │
  │  (distributes load)  │
  └──────┬──────┬────────┘
         │      │
         ▼      ▼
  ┌──────────┐ ┌──────────┐
  │ Server 1 │ │ Server 2 │   ← Stateless REST servers
  └──────┬───┘ └──────┬───┘
         │             │
         └──────┬──────┘
                ▼
        ┌───────────────┐
        │   DATABASE    │
        │  (source of   │
        │    truth)     │
        └───────────────┘
                │
        HTTP Response
        200 OK
        {&quot;id&quot;: 123, &quot;name&quot;: &quot;Alice&quot;}
        │
        ▼
  CLIENT receives data
</code></pre>

<hr/>

<h2>HTTP vs HTTPS — 核心区别 / Core Difference</h2>

<p><strong>HTTP</strong> — HyperText Transfer Protocol</p>
<p>- 明文传输，数据可被中间人截获</p>
<p>- Plaintext transmission; data can be intercepted</p>

<p><strong>HTTPS</strong> — HTTP + TLS/SSL 加密</p>
<p>- 所有数据加密传输，第三方无法读取内容</p>
<p>- All data encrypted; third parties can&#x27;t read the content</p>
<p>- 通过证书验证服务器身份（你真的是在和 google.com 说话吗？）</p>
<p>- Certificate verifies server identity (are you really talking to google.com?)</p>

<pre><code>
HTTP:   你的密码 → [网络] → 服务器       ← 路由器能看到！
HTTPS:  你的密码 → [加密] → [网络] → [解密] → 服务器  ← 中间人只看到乱码
</code></pre>

<hr/>

<h2>REST API — 六个约束 / Six Constraints</h2>

<p>REST (Representational State Transfer) 不是技术，是一套设计风格：</p>

<p>1. <strong>Stateless（无状态）</strong> — 服务器不记住你。每个请求自带所有信息。</p>
<p>Server doesn&#x27;t remember you. Each request carries all needed info.</p>

<p>2. <strong>Client-Server（客户端-服务器分离）</strong> — 前端和后端独立演化。</p>
<p>Frontend and backend evolve independently.</p>

<p>3. <strong>Cacheable（可缓存）</strong> — 响应可以被缓存，减少重复请求。</p>
<p>Responses can be cached to reduce redundant requests.</p>

<p>4. <strong>Uniform Interface（统一接口）</strong> — 用标准HTTP方法操作资源。</p>
<p>Use standard HTTP verbs to manipulate resources.</p>

<p>5. <strong>Layered System（分层系统）</strong> — 客户端不关心中间有几层。</p>
<p>Client doesn&#x27;t care how many layers exist in between.</p>

<p>6. <strong>Code on Demand (Optional)</strong> — 服务器可返回可执行代码（如JS）。</p>
<p>Server can return executable code (e.g., JavaScript).</p>

<hr/>

<h2>HTTP 方法 CRUD 对应关系 / HTTP Methods → CRUD</h2>

<pre><code>
HTTP Method    CRUD Operation    Example
──────────────────────────────────────────────────
GET            Read              GET /users/123
POST           Create            POST /users  {body}
PUT            Replace (全量)    PUT /users/123  {full body}
PATCH          Update (部分)     PATCH /users/123 {partial}
DELETE         Delete            DELETE /users/123
</code></pre>

<p><strong>状态码速查 / Status Code Cheat Sheet:</strong></p>
<pre><code>
2xx  ✅  成功 / Success
  200 OK           — 请求成功
  201 Created      — 资源已创建
  204 No Content   — 成功但无返回体

3xx  ↩️  重定向 / Redirect
  301 Moved Permanently  — 永久跳转
  304 Not Modified       — 用缓存

4xx  ❌  客户端错误 / Client Error
  400 Bad Request    — 你的请求有问题
  401 Unauthorized   — 没登录
  403 Forbidden      — 登录了但没权限
  404 Not Found      — 资源不存在
  429 Too Many Reqs  — 限流了

5xx  💥  服务器错误 / Server Error
  500 Internal Server Error — 服务器崩了
  503 Service Unavailable   — 服务不可用
</code></pre>

<hr/>

<h2>为什么这样设计？/ Why This Design?</h2>

<p><strong>为什么REST选择无状态（Stateless）？</strong></p>

<p>如果服务器记住每个用户的状态，那扩展到100台服务器时，你必须确保每次请求都打到同一台机器（叫做&quot;粘性会话&quot;sticky session）。这非常麻烦。</p>

<p>无状态的好处：任何服务器都能处理任何请求，水平扩展极其简单。</p>

<p>If servers remembered user state, scaling to 100 servers would require routing each user to the same server every time (&quot;sticky sessions&quot;) — a maintenance nightmare. Stateless = any server can handle any request = horizontal scaling is trivial.</p>

<hr/>

<h2>别踩这个坑 / Don&#x27;t Fall Into This Trap</h2>

<p><strong>坑 1: 在GET请求中修改数据</strong></p>
<pre><code>
❌  GET /deleteUser?id=123    # 语义错误，GET应该是只读的
✅  DELETE /users/123
</code></pre>

<p><strong>坑 2: 用动词命名资源（URL应该是名词）</strong></p>
<pre><code>
❌  POST /createUser
❌  GET  /getUserById?id=123
✅  POST /users
✅  GET  /users/123
</code></pre>

<p><strong>坑 3: 忘记区分401和403</strong></p>
<pre><code>
401 Unauthorized → &quot;你是谁？请先登录&quot; (Who are you? Please log in)
403 Forbidden    → &quot;我知道你是谁，但你没权限&quot; (I know who you are, but you can&#x27;t)
</code></pre>

<p><strong>坑 4: 滥用200 OK返回错误信息</strong></p>
<pre><code>
❌  200 OK  {&quot;error&quot;: &quot;User not found&quot;}   # 前端要额外解析body判断成功失败
✅  404 Not Found  {&quot;message&quot;: &quot;User not found&quot;}
</code></pre>

<hr/>

<h2>与昨天的联系 / Connection to Day 2</h2>

<p>昨天我们学了DNS和TCP/IP。现在你明白了完整链路：</p>

<p>1. 你在浏览器输入 <code>https://api.example.com/users</code></p>
<p>2. <strong>DNS</strong> 解析域名 → IP地址</p>
<p>3. <strong>TCP</strong> 建立连接（三次握手）</p>
<p>4. <strong>TLS</strong> 握手，建立加密通道（HTTPS）</p>
<p>5. <strong>HTTP</strong> 发送请求，服务器返回响应</p>
<p>6. 浏览器渲染数据</p>

<p>Yesterday we covered DNS and TCP/IP. Now you see the full picture: DNS → TCP → TLS → HTTP → response. Each layer builds on the one before it.</p>

<hr/>

<p><em>Day 3 | 系统设计基础系列 | 明天：数据库基础</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 3 / Algorithms Day 3</h2>
<p><strong>#1 Two Sum (Easy) — Arrays &amp; Hashing</strong></p>
<p>🔗 https://leetcode.com/problems/two-sum/</p>

<hr/>

<h2>现实类比 / Real-World Analogy</h2>

<p>想象你在一家超市，口袋里有 $11，你想找到两件商品，价格加起来正好等于 $11。</p>

<p>笨方法：把每件商品和其他所有商品逐一配对比较 — 效率太低了 O(n²)。</p>

<p>聪明方法：每次拿起一件商品（价格 x），立刻检查你的&quot;已看过价格&quot;笔记本里有没有 <code>11 - x</code>。有就找到了！这就是哈希表的思路。</p>

<p>Imagine you&#x27;re in a supermarket with $11 and want two items that add up to exactly $11. Brute force: compare every pair. Smart way: for each item (price x), instantly check if <code>11 - x</code> is already in your &quot;seen prices&quot; notebook. That&#x27;s the hash map approach.</p>

<hr/>

<h2>题目 / Problem Statement</h2>

<p><strong>中文：</strong> 给定一个整数数组 <code>nums</code> 和一个目标值 <code>target</code>，找出数组中和为 <code>target</code> 的两个数的<strong>下标</strong>。假设每道题恰好只有一个答案，且同一个元素不能使用两次。</p>

<p><strong>English:</strong> Given an array of integers <code>nums</code> and an integer <code>target</code>, return the indices of the two numbers that add up to <code>target</code>. You may assume exactly one solution exists, and you may not use the same element twice.</p>

<pre><code>
Input:  nums = [2, 7, 11, 15], target = 9
Output: [0, 1]   (because nums[0] + nums[1] = 2 + 7 = 9)
</code></pre>

<hr/>

<h2>逐步思路 / Step-by-Step Walkthrough</h2>

<h3>方法1：暴力 O(n²) — 先理解，别用它</h3>

<pre><code>
For i = 0 (num = 2):
  For j = 1 (num = 7):  2 + 7 = 9 ✅ → return [0, 1]
</code></pre>

<h3>方法2：哈希表 O(n) — 这才是正解</h3>

<p>核心思路：遍历时，对每个数 <code>num</code>，我们不是找&quot;谁加上 num 等于 target&quot;，而是找&quot;target - num 在不在我们之前见过的数里&quot;。</p>

<p><strong>具体示例追踪：</strong></p>
<pre><code>
nums = [2, 7, 11, 15], target = 9
seen = {}  (空字典)

i=0, num=2:
  complement = 9 - 2 = 7
  7 in seen? → No (seen is empty)
  → 把 2 存入 seen: seen = {2: 0}

i=1, num=7:
  complement = 9 - 7 = 2
  2 in seen? → Yes! seen[2] = 0
  → 返回 [seen[2], i] = [0, 1] ✅
</code></pre>

<p><strong>再来一个不那么直接的例子：</strong></p>
<pre><code>
nums = [3, 2, 4], target = 6
seen = {}

i=0, num=3:
  complement = 6 - 3 = 3
  3 in seen? → No
  seen = {3: 0}

i=1, num=2:
  complement = 6 - 2 = 4
  4 in seen? → No
  seen = {3: 0, 2: 1}

i=2, num=4:
  complement = 6 - 4 = 2
  2 in seen? → Yes! seen[2] = 1
  → 返回 [seen[2], i] = [1, 2] ✅
  (nums[1] + nums[2] = 2 + 4 = 6 ✓)
</code></pre>

<hr/>

<h2>Python 解法 / Python Solution</h2>

<pre><code>
def twoSum(nums: list[int], target: int) -&gt; list[int]:
    # Hash map: value → index
    # We store numbers we&#x27;ve already visited
    seen = {}
    
    for i, num in enumerate(nums):
        # What number do we NEED to complete the pair?
        complement = target - num
        
        # Check if that number is already in our map
        if complement in seen:
            # Found it! Return both indices
            return [seen[complement], i]
        
        # Haven&#x27;t found a pair yet; record this number and its index
        seen[num] = i
    
    # Problem guarantees a solution exists, so we won&#x27;t reach here
    return []


# Test cases
print(twoSum([2, 7, 11, 15], 9))   # [0, 1]
print(twoSum([3, 2, 4], 6))         # [1, 2]
print(twoSum([3, 3], 6))            # [0, 1]
</code></pre>

<hr/>

<h2>复杂度分析 / Complexity Analysis</h2>

<pre><code>
时间复杂度 Time:  O(n)
  → 只遍历数组一次。哈希表查找是 O(1) 平均。

空间复杂度 Space: O(n)
  → 最坏情况下，哈希表存储 n-1 个元素（最后一对才匹配）。

vs. 暴力 Brute Force:
  Time: O(n²)  — 双重循环
  Space: O(1)  — 不用额外空间
</code></pre>

<p><strong>权衡：</strong> 用空间换时间。这通常是对的 — 内存便宜，时间贵。</p>

<p><strong>Trade-off:</strong> We trade space for time. Usually the right call — memory is cheap, user time is not.</p>

<hr/>

<h2>边界情况 / Edge Cases</h2>

<pre><code>
# 两个相同的数字
twoSum([3, 3], 6)   # → [0, 1] ✅
# 关键：先检查 complement，再存入 seen
# 这样避免同一元素用两次

# 负数
twoSum([-1, -2, -3, -4, -5], -8)   # → [2, 4] (nums[2]+nums[4] = -3+-5)

# 只有两个元素
twoSum([1, 9], 10)   # → [0, 1]
</code></pre>

<hr/>

<h2>举一反三 / Pattern Recognition</h2>

<p>掌握了&quot;边遍历边建哈希表&quot;这个模式，你可以解决：</p>

<p>- <strong>Two Sum II</strong> (sorted array) — 用双指针，O(1) 空间</p>
<p>- <strong>Two Sum III</strong> (data structure) — 设计一个支持 add/find 的类</p>
<p>- <strong>3Sum</strong> (#15) — 固定一个数，对剩余用双指针</p>
<p>- <strong>4Sum</strong> (#18) — 嵌套一层再用双指针</p>
<p>- <strong>Subarray Sum Equals K</strong> (#560) — 前缀和 + 哈希表</p>

<p><strong>核心模式：</strong> &quot;我需要找 X，先问问我见过 X 吗？没见过，就记下现在这个。&quot;</p>

<p><strong>The pattern:</strong> &quot;I need X. Have I seen X? No? Then record what I have now.&quot;</p>

<hr/>

<p><em>Day 3 | 数组与哈希系列 | 昨天：Valid Anagram | 明天：Contains Duplicate</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 3 / Soft Skills Day 3</h2>
<p><strong>Conflict Resolution — 冲突解决</strong></p>

<p><strong>问题 / Question:</strong></p>
<p>&gt; &quot;Tell me about a time you disagreed with your manager or a senior engineer. How did you handle it?&quot;</p>
<p>&gt; &quot;讲一个你与你的经理或高级工程师意见相左的经历。你是怎么处理的？&quot;</p>

<hr/>

<h2>为什么这很重要 / Why This Matters</h2>

<p>这道题考察的不是&quot;谁对谁错&quot;，而是：</p>

<p>1. <strong>你有没有主见？</strong> 没想法的工程师不能独立工作</p>
<p>2. <strong>你有没有成熟度？</strong> 能否在坚持立场和尊重经验之间取得平衡</p>
<p>3. <strong>你能不能影响他人？</strong> 在没有直接权力的情况下推动决策</p>

<p>This question isn&#x27;t about who was right. It probes:</p>
<p>1. Do you have independent judgment?</p>
<p>2. Are you mature enough to push back respectfully?</p>
<p>3. Can you influence without authority?</p>

<p>At senior/staff level, this is a daily reality. The inability to navigate technical disagreements is a major signal that someone isn&#x27;t ready to operate at the next level.</p>

<hr/>

<h2>STAR 框架拆解 / STAR Framework Breakdown</h2>

<pre><code>
S - Situation  (情境)  ← 快速设置场景，30秒内
T - Task       (任务)  ← 你的目标/责任是什么
A - Action     (行动)  ← 这是重点，用70%的时间
R - Result     (结果)  ← 量化 + 反思
</code></pre>

<hr/>

<h2>❌ 糟糕的回答 / Bad Approach</h2>

<p>&gt; &quot;我们在选数据库。我觉得应该用 PostgreSQL，我的经理说用 MongoDB。我给他看了对比文章，最后他同意了我的观点。&quot;</p>

<p><strong>为什么不好：</strong></p>
<p>- 没有上下文（为什么要做这个选择？）</p>
<p>- 没有展示思考过程（你怎么决定提出来的？）</p>
<p>- &quot;给他看文章&quot;太被动 — 没有体现你如何主动建立共识</p>
<p>- 听起来像是&quot;我赢了，他输了&quot; — 没有合作感</p>

<hr/>

<h2>✅ 好的回答 / Good Approach</h2>

<p>&gt; &quot;我们要为用户分析平台选择数据存储方案。我的Tech Lead倾向于继续用我们熟悉的MySQL，因为团队对它最熟悉，迁移成本低。我的判断是，我们的查询模式——大量聚合、时间序列、不规则的事件结构——更适合列式存储，比如ClickHouse。</p>
<p>&gt;</p>
<p>&gt; 我没有直接开会说&#x27;你错了&#x27;。我先花了两天做了一个小型基准测试：用两套方案各跑了我们最慢的5个查询，把性能数据整理成表格。同时我也整理了迁移的风险点和成本估算。</p>
<p>&gt;</p>
<p>&gt; 然后我约了Tech Lead 1-on-1，先说：&#x27;我想和你讨论一下数据库选型，我做了一些数据，想听听你的想法。&#x27;我们发现他的核心顾虑是迁移风险，而不是性能。于是我们达成了折中方案：新的分析pipeline用ClickHouse，老的业务数据留在MySQL，不做迁移。</p>
<p>&gt;</p>
<p>&gt; 最终上线后，分析查询从平均 8 秒降到 0.3 秒，用户投诉减少了 80%。而且我和Tech Lead的关系没有受损——他后来还把我当成这个领域的go-to person。&quot;</p>

<p><strong>为什么好：</strong></p>
<p>- 清楚的商业背景</p>
<p>- 展示了&quot;先做数据再谈分歧&quot;的成熟判断</p>
<p>- 用1-on-1而不是公开会议处理分歧（情商高）</p>
<p>- 承认对方顾虑有道理，找到折中</p>
<p>- 量化结果</p>
<p>- 关系反而变好了</p>

<hr/>

<h2>场景模板 / Scenario Template</h2>

<p>用这个框架构建你自己的故事：</p>

<pre><code>
情境：我们在 [项目] 做 [技术决策]
分歧：我的 [经理/Tech Lead] 倾向于 [方案A]
      我的判断是 [方案B] 更合适，因为 [数据/理由]
行动：
  1. 我先 [做了什么验证工作]，而不是直接开会争论
  2. 我通过 [1-on-1/小型演示/数据报告] 分享我的发现
  3. 我先理解了对方的核心顾虑是 [XXX]
  4. 我们达成了 [折中方案/对方被说服/我更新了我的判断]
结果：[量化结果] + [关系/团队影响]
</code></pre>

<hr/>

<h2>Senior/Staff 级别的加分点 / Senior/Staff Level Tips</h2>

<p><strong>1. 展示你知道何时该放手 / Show you know when to let go</strong></p>
<p>&gt; &quot;我做了充分的论证，但最终决策权在他。我接受了这个决定，全力支持执行。六个月后，我们确实遇到了我预料的问题，但那时我们一起复盘，而不是我说&#x27;我早就说过了&#x27;。&quot;</p>

<p><strong>2. 主动建立共识而非赢得辩论 / Build alignment, don&#x27;t win debates</strong></p>
<p>Staff工程师知道：即使你是对的，如果你让别人&quot;输了&quot;，你长期付出的代价更大。</p>

<p>Staff engineers know: even if you&#x27;re right, making someone else &quot;lose&quot; costs you more in the long run.</p>

<p><strong>3. 把技术分歧和人际关系分开 / Separate technical disagreement from personal conflict</strong></p>
<p>&gt; &quot;我特别注意在表达不同意见时，聚焦在数据和影响上，而不是质疑他的判断能力。&quot;</p>

<hr/>

<h2>关键要点 / Key Takeaways</h2>

<pre><code>
✅ DO:
  - 先做功课（数据、原型、基准测试）再提出异议
  - 用1-on-1，不要在大会议上让人难堪
  - 理解对方顾虑，找折中点
  - 量化你的结果
  - 即使不同意，也要优雅地接受最终决策

❌ DON&#x27;T:
  - &quot;我最终说服了他&quot; 式的叙述（听起来自大）
  - 描述一个你完全错了的故事（除非重点是你从中学到了什么）
  - 没有结果（&quot;我们还在讨论…&quot;）
  - 批评你的前经理（面试官会想：他会不会也这么说我？）
</code></pre>

<hr/>

<p><em>Day 3 | 软技能系列 | 昨天：影响力（无直接权力）| 明天：处理模糊需求</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 3 / Frontend Day 3</h2>
<p><strong>CSS Grid — Two-Dimensional Layouts / 二维布局</strong></p>
<p><em>Week 1 | CSS Fundamentals</em></p>

<hr/>

<h2>猜猜这段代码输出什么？/ What Does This Layout Look Like?</h2>

<pre><code>
&lt;div class=&quot;grid&quot;&gt;
  &lt;div&gt;A&lt;/div&gt;
  &lt;div&gt;B&lt;/div&gt;
  &lt;div&gt;C&lt;/div&gt;
  &lt;div&gt;D&lt;/div&gt;
  &lt;div&gt;E&lt;/div&gt;
&lt;/div&gt;
</code></pre>

<pre><code>
.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  grid-template-rows: 100px 100px;
  gap: 10px;
}
</code></pre>

<p><strong>猜猜结果 / Guess the output:</strong></p>

<pre><code>
┌────────┬───────────────┬────────┐
│   A    │       B       │   C    │
│ (1fr)  │     (2fr)     │ (1fr)  │
│        │               │        │  ← row 1: 100px tall
│────────┼───────────────┼────────│
│   D    │       E       │(empty) │
│ (1fr)  │     (2fr)     │        │  ← row 2: 100px tall
└────────┴───────────────┴────────┘

Total 4fr per row → A=25%, B=50%, C=25%
</code></pre>

<p>✅ <strong>答案：</strong> 3列布局，比例 1:2:1。B 的宽度是 A 和 C 的两倍。第五个元素 E 占第二行第二格，第三格为空。</p>

<hr/>

<h2>Grid vs Flexbox — 一句话记住 / One-Liner to Remember</h2>

<pre><code>
Flexbox = 一维  (一行 or 一列)     Think: 导航栏, 按钮组
Grid    = 二维  (行 AND 列同时)    Think: 页面布局, 卡片网格
</code></pre>

<p>昨天（Day 2）我们学了 Flexbox。今天 Grid 是它的二维升级版。</p>

<hr/>

<h2>核心概念图解 / Core Concepts Illustrated</h2>

<pre><code>
display: grid;
grid-template-columns: 1fr 1fr 1fr;  ← 3 equal columns
grid-template-rows: auto auto;        ← 2 rows, height = content

Grid Container (父元素)
┌──────────┬──────────┬──────────┐
│  cell    │  cell    │  cell    │ ← row 1
│  [0,0]   │  [0,1]   │  [0,2]  │
├──────────┼──────────┼──────────┤
│  cell    │  cell    │  cell    │ ← row 2
│  [1,0]   │  [1,1]   │  [1,2]  │
└──────────┴──────────┴──────────┘
     ↑            ↑           ↑
   col 1        col 2       col 3
</code></pre>

<hr/>

<h2>让子元素跨格 / Spanning Multiple Cells</h2>

<pre><code>
/* 这个元素跨越2列 */
.item-a {
  grid-column: 1 / 3;   /* start at line 1, end at line 3 */
  /* shorthand: grid-column: span 2; */
}

/* 这个元素跨越2行 */
.item-b {
  grid-row: 1 / 3;      /* start at line 1, end at line 3 */
}
</code></pre>

<pre><code>
Before span:          After .item-a spans 2 cols:
┌───┬───┬───┐         ┌─────────┬───┐
│ A │ B │ C │         │    A    │ C │   ← A now takes col 1+2
├───┼───┼───┤         ├───┬─────┴───┤
│ D │ E │ F │         │ D │ E   │ F │
└───┴───┴───┘         └───┴─────┴───┘
</code></pre>

<hr/>

<h2>代码示例：经典页面布局 / Classic Page Layout</h2>

<pre><code>
.page {
  display: grid;
  grid-template-areas:
    &quot;header  header  header&quot;
    &quot;sidebar main    main  &quot;
    &quot;footer  footer  footer&quot;;
  grid-template-columns: 200px 1fr 1fr;
  grid-template-rows: 60px 1fr 40px;
  min-height: 100vh;
  gap: 8px;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }
</code></pre>

<pre><code>
Visual Result:
┌──────────────────────────────────┐
│           HEADER (full width)    │  60px
├───────────┬──────────────────────┤
│           │                      │
│  SIDEBAR  │        MAIN          │  flex-grow
│  (200px)  │      (remaining)     │
│           │                      │
├───────────┴──────────────────────┤
│           FOOTER (full width)    │  40px
└──────────────────────────────────┘
</code></pre>

<p><strong><code>grid-template-areas</code> 的魔法：</strong> 用 ASCII 艺术描述布局，可读性极强。</p>

<hr/>

<h2>你可能不知道 / You Might Not Know</h2>

<h3>1. `fr` 单位只分配**剩余**空间</h3>

<pre><code>
grid-template-columns: 200px 1fr 1fr;
</code></pre>

<p>先分配固定的 200px，<strong>然后</strong> 把剩余宽度按 1:1 分给后两列。不是三等分！</p>

<p><code>fr</code> only distributes <em>remaining</em> space after fixed sizes are allocated.</p>

<h3>2. `auto-fill` vs `auto-fit`</h3>

<pre><code>
/* auto-fill: 尽可能多地创建列（即使是空的） */
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));

/* auto-fit: 拉伸现有列填满空间（不创建空列） */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
</code></pre>

<p>这是响应式布局的神器——不写任何 media query 就能自适应！</p>

<p>This is the secret to responsive grid layouts without media queries.</p>

<h3>3. Grid 也能嵌套</h3>

<pre><code>
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.card {
  display: grid;  /* nested grid! */
  grid-template-rows: auto 1fr auto;  /* image | content | button */
}
</code></pre>

<hr/>

<h2>Mini Challenge / 小挑战</h2>

<p>用 CSS Grid 创建一个响应式卡片布局，要求：</p>
<p>- 大屏显示4列，中等屏幕3列，小屏2列</p>
<p>- <strong>只用一行 CSS，不用任何 media query</strong></p>

<p>&lt;details&gt;</p>
<p>&lt;summary&gt;提示 / Hint&lt;/summary&gt;</p>

<pre><code>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}
</code></pre>

<p>当容器宽度变化时，<code>minmax(200px, 1fr)</code> 会自动计算可以放多少列！</p>

<p>&lt;/details&gt;</p>

<hr/>

<h2>Flexbox vs Grid 决策树 / Decision Tree</h2>

<pre><code>
你需要对齐内容吗？
│
├─ 只在一个方向（行 OR 列）→ 用 Flexbox
│   例：导航栏、按钮组、居中一个元素
│
└─ 同时在两个方向（行 AND 列）→ 用 Grid
    例：页面整体布局、图片画廊、仪表盘
</code></pre>

<hr/>

<p><em>Day 3 | CSS 布局系列 | 昨天：Flexbox | 明天：响应式设计 &amp; Media Queries</em></p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 3 — News Roundup / AI 新闻速递</h2>
<p><em>2026年3月第三周 | March 2026, Week 3</em></p>

<p>&gt; ⚠️ <strong>注意 / Note:</strong> AI 领域动态极快。以下新闻基于截至本文生成时的公开报道，具体数字与细节以原始来源为准。</p>

<hr/>

<h2>📰 Story 1: Meta 的 &quot;Avocado&quot; 模型推迟发布</h2>

<p>根据报道，Meta 的下一代 AI 大模型（内部代号 &quot;Avocado&quot;）被推迟至至少 2026 年 5 月，原因是性能尚未达到 Google 等竞争对手的水平。</p>

<p><strong>背景：</strong> Meta 此前招募了 Scale AI CEO Alexandr Wang，并投入数十亿美元试图追赶。Avocado 将是这一系列努力的首个重大发布。</p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>

<p>Meta 的 AI 布局直接影响整个生态——LLaMA 系列是很多开源项目的基础。如果 Avocado 继续落后于 GPT-4o 级别的模型，开源社区的&quot;平替方案&quot;质量也会受影响。对前端/全栈工程师来说：Meta AI 功能的集成（Facebook、Instagram、WhatsApp）会滞后，这可能影响你所在产品的 AI feature timeline。</p>

<hr/>

<h2>📰 Story 2: Palantir 的 Maven Smart System — AI 军事应用的争议</h2>

<p>Palantir 在其 AIPCon 会议上展示了 Maven Smart System，这是一个 AI 驱动的军事目标识别系统。演示视频显示，用户可以用&quot;左键点击、右键点击、左键点击&quot;的方式锁定打击目标，引发了广泛争议。</p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>

<p>这不只是道德话题——它是工程师职业生涯中越来越难以回避的现实问题。AI 的军事应用正在快速落地，而关于&quot;工程师是否应该为武器系统写代码&quot;的讨论也在硅谷持续升温（参考 Google 的 Project Maven 员工抗议事件）。面试中，你可能会被问到对这类项目的看法。答案没有对错，但你需要有自己的立场。</p>

<hr/>

<h2>📰 Story 3: BuzzFeed 的 AI 教训 — 内容农场的警示</h2>

<p>BuzzFeed 2025 年亏损达 $5730 万，股价跌至 $0.70，部分原因是大量使用 AI 生成文章和测验内容。尽管如此，CEO Jonah Peretti 仍表示将推出&quot;新的 AI 应用&quot;。</p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>

<p>这是一个&quot;AI 用错地方&quot;的典型案例。内容农场式的 AI 应用（用 AI 批量生产无差异化内容）正在被市场惩罚。读者不傻，算法（Google SEO、推荐算法）也越来越能识别低质量 AI 内容。</p>

<p><strong>工程师视角：</strong> 如果你在构建 AI 内容工具，问题不是&quot;能不能生成&quot;，而是&quot;用户为什么要看这个而不是其他的&quot;。差异化 &gt; 数量。</p>

<hr/>

<h2>📰 Story 4: Amazon Alexa Plus 新增&quot;Sassy&quot;个性模式</h2>

<p>Amazon 为 Alexa Plus 推出了新的&quot;Sassy&quot;个性风格，特点是&quot;不加滤镜的个性、机智的讽刺、以及偶尔被屏蔽的脏话&quot;。该功能仅限成人，需要额外身份验证。</p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>

<p>这其实是一个有趣的 UX / AI 产品设计信号——用户对 AI 助手的&quot;人格化&quot;需求越来越强。语音 AI 的个性化是下一个竞争维度（而不只是&quot;准确性&quot;）。</p>

<p><strong>技术角度：</strong> 实现&quot;可切换人格&quot;需要在 prompt 层面和 safety filter 层面同时设计。&quot;偶尔允许脏话&quot;这个边界的实现本身就是个有趣的工程问题。</p>

<hr/>

<h2>📰 Story 5: Meta 收购 Moltbook — AI 代理的&quot;社交网络&quot;</h2>

<p>Meta 近期收购了 Moltbook，一个&quot;AI 代理社交网络&quot;平台。收购后，平台更新了服务条款：用户（不是 Meta）对其 AI 代理的所有行为负责，无论行为是否有意为之、是否自主发生。</p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>

<p>这是 AI 代理（Agent）时代的法律雏形。谁对 AI Agent 的行为负责？目前答案是&quot;你，用户&quot;。这对于正在构建 AI Agent 产品的工程师来说是重要信号：你需要设计审计日志、权限边界、和&quot;人类在回路&quot;（human-in-the-loop）机制，不只是因为产品好，而是因为法律会要求你这么做。</p>

<hr/>

<h2>本周 AI 关键词 / This Week&#x27;s AI Keywords</h2>

<pre><code>
Agentic AI (代理式AI)    — AI 不只回答问题，而是自主行动
Human-in-the-loop (HITL) — 关键决策需要人类确认
AI Liability (AI责任归属) — 谁为 AI 的行为负责？
Model Delay (模型推迟)   — 发布时间表 ≠ 实际发布时间
AI + Military (AI军事化)  — 工程伦理的新战场
</code></pre>

<hr/>

<p><em>Day 3 | AI 新闻系列 | 昨天：Transformer 工作原理 | 明天：RAG（检索增强生成）</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-15</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-15</guid>
      <pubDate>Sun, 15 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>🏗️ 系统设计 Day 2 / System Design Day 2</h2>
<p><strong>Topic: DNS, IP, and TCP/UDP — 互联网的&quot;电话本&quot;与&quot;快递公司&quot;</strong></p>

<hr/>

<h2>场景引入 / Scenario</h2>

<p>想象你在设计一个全球用户访问的网站。你写了 <code>https://myblog.com</code>，浏览器是怎么找到你服务器的？从你按下 Enter 到页面出现，背后发生了什么魔法？</p>

<p><em>Imagine you&#x27;re building a website for global users. You type <code>https://myblog.com</code> — how does your browser find your server? What magic happens between pressing Enter and seeing the page?</em></p>

<hr/>

<h2>DNS：互联网的电话本 / DNS: The Internet&#x27;s Phone Book</h2>

<p>人类记得 <code>google.com</code>，机器只认识 <code>142.250.80.46</code>。DNS（Domain Name System）就是把&quot;人话&quot;翻译成&quot;机器话&quot;的翻译官。</p>

<p><em>Humans remember <code>google.com</code>, machines only understand <code>142.250.80.46</code>. DNS translates human-readable names into machine-readable IPs.</em></p>

<h3>DNS 查询流程 / DNS Resolution Flow</h3>

<pre><code>
你的浏览器
    │
    ▼
[1] 本地缓存 / Local Cache
    │  (找到了? 直接返回 / Found? Return immediately)
    │  (没找到? 继续 / Not found? Continue)
    ▼
[2] 操作系统 hosts 文件 / OS hosts file
    │  (/etc/hosts on Linux/Mac)
    ▼
[3] 递归解析器 / Recursive Resolver
    │  (通常是你的 ISP 或 8.8.8.8)
    │  (Usually your ISP or 8.8.8.8)
    ▼
[4] 根域名服务器 / Root Nameserver
    │  (&quot;我不知道 myblog.com，但 .com 服务器知道&quot;)
    │  (&quot;I don&#x27;t know myblog.com, but .com nameserver does&quot;)
    ▼
[5] TLD 服务器 / TLD Nameserver (.com)
    │  (&quot;myblog.com 的权威服务器在这里&quot;)
    │  (&quot;myblog.com&#x27;s authoritative server is here&quot;)
    ▼
[6] 权威 DNS 服务器 / Authoritative Nameserver
    │  &quot;myblog.com → 203.0.113.42&quot;
    ▼
IP 地址返回给浏览器 / IP returned to browser
</code></pre>

<p><strong>真实类比 / Real-world analogy:</strong></p>
<p>根服务器 = 全国电话总机 → TLD 服务器 = 城市区号本 → 权威服务器 = 某公司的直线</p>
<p><em>Root server = National operator → TLD = City directory → Authoritative = Company&#x27;s direct line</em></p>

<hr/>

<h2>IP：你在网络上的&quot;门牌号&quot; / IP: Your Network &quot;Address&quot;</h2>

<p>- <strong>IPv4</strong>: <code>203.0.113.42</code> — 32位，约43亿个地址，已经快用完了</p>
<p>- <strong>IPv6</strong>: <code>2001:0db8:85a3::8a2e:0370:7334</code> — 128位，几乎无限</p>

<p><em>IPv4 is 32-bit (~4.3 billion addresses, nearly exhausted). IPv6 is 128-bit, essentially unlimited.</em></p>

<p><strong>公网 vs 私网 / Public vs Private IP:</strong></p>
<pre><code>
家庭网络 / Home Network:
  你的电脑      → 192.168.1.100 (私网/Private)
  你的手机      → 192.168.1.101 (私网/Private)
  路由器对外    → 203.0.113.42  (公网/Public)  ← 互联网只看到这个
                                                  ← Internet only sees this
NAT（网络地址转换）帮你把私网地址映射到公网
NAT (Network Address Translation) maps private to public
</code></pre>

<hr/>

<h2>TCP vs UDP：快递公司 vs 广播电台</h2>

<p>| 特性 / Feature | TCP | UDP |</p>
<p>|---|---|---|</p>
<p>| 连接方式 / Connection | 三次握手 / 3-way handshake | 无连接 / Connectionless |</p>
<p>| 可靠性 / Reliability | ✅ 保证送达 / Guaranteed | ❌ 尽力而为 / Best-effort |</p>
<p>| 顺序 / Order | ✅ 有序 / Ordered | ❌ 可能乱序 / May arrive out of order |</p>
<p>| 速度 / Speed | 较慢 / Slower | 更快 / Faster |</p>
<p>| 适用场景 / Use cases | HTTP, Email, File transfer | Video streaming, Gaming, DNS |</p>

<h3>TCP 三次握手 / TCP 3-Way Handshake</h3>

<pre><code>
客户端 / Client          服务器 / Server
     │                        │
     │──── SYN ──────────────&gt;│  &quot;我想连接你 / I want to connect&quot;
     │                        │
     │&lt;─── SYN-ACK ───────────│  &quot;好的，收到 / OK, received&quot;
     │                        │
     │──── ACK ──────────────&gt;│  &quot;我也确认了 / Confirmed&quot;
     │                        │
     │══════ 连接建立 / Connection Established ══════│
</code></pre>

<p><strong>为什么需要3次？/ Why 3 handshakes?</strong></p>
<p>2次不够——服务器无法确认客户端收到了回复。就像打电话：&quot;喂？&quot; &quot;喂，听到了吗？&quot; &quot;听到了，开始说吧。&quot;</p>
<p><em>2 isn&#x27;t enough — the server can&#x27;t confirm the client received its reply. Like a phone call: &quot;Hello?&quot; &quot;Hello, can you hear me?&quot; &quot;Yes, go ahead.&quot;</em></p>

<hr/>

<h2>为什么这样设计？/ Why This Design?</h2>

<p><strong>DNS 分层设计的好处 / Benefits of hierarchical DNS:</strong></p>
<p>- <strong>可扩展性</strong>: 根服务器只有13个，但全球有数十亿个域名</p>
<p>- <strong>缓存</strong>: 每层都可以缓存，减少重复查询</p>
<p>- <strong>容错</strong>: 多个根服务器，一个挂了其他继续工作</p>

<p><em>Scalability (13 root servers handle billions of domains via delegation), caching at every layer, and fault tolerance through redundancy.</em></p>

<hr/>

<h2>别踩这个坑 / Don&#x27;t Fall Into This Trap</h2>

<p><strong>坑1: DNS 缓存污染面试题</strong></p>
<p>面试问：&quot;为什么我改了 DNS 记录，但用户还是访问旧服务器？&quot;</p>
<p>答：TTL（Time To Live）没过期。DNS 记录有缓存时间，改了之后要等 TTL 归零才会全面生效。<strong>上线前提前降低 TTL！</strong></p>

<p><em>DNS cache: after changing DNS records, users still hit old servers until TTL expires. Best practice: lower TTL hours before a migration.</em></p>

<p><strong>坑2: TCP 不等于安全</strong></p>
<p>TCP 保证送达，但不加密。<code>http://</code> 用 TCP，但数据是明文。需要加密要用 TLS（即 <code>https://</code>）。</p>

<p><em>TCP guarantees delivery, not security. HTTP over TCP is plaintext. TLS (HTTPS) is needed for encryption.</em></p>

<hr/>

<h2>关键要点 / Key Takeaways</h2>

<p>1. <strong>DNS</strong> = 域名 → IP 的翻译，分层设计，有缓存</p>
<p>2. <strong>IPv4</strong> 快用完了，<strong>IPv6</strong> 是未来</p>
<p>3. <strong>TCP</strong> = 可靠但慢（文件、网页）；<strong>UDP</strong> = 快但不可靠（直播、游戏）</p>
<p>4. 三次握手确保双向通信可靠建立</p>

<p><em>DNS translates domains to IPs with hierarchical caching. IPv4 is nearly exhausted, IPv6 is the future. TCP = reliable but slower; UDP = fast but lossy. 3-way handshake ensures both ends can send and receive.</em></p>

<hr/>
<p><em>Day 2 of 100 | #ByteByByte | 系统设计基础系列</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>💻 算法 Day 2 / Algorithms Day 2</h2>
<p><strong>#242 Valid Anagram（有效的字母异位词）— Easy | Pattern: Arrays &amp; Hashing</strong></p>

<hr/>

<h2>生活类比 / Real-World Analogy</h2>

<p>想象你有两袋相同字母的乐高积木。不管你怎么排列，只要袋子里的积木种类和数量完全一样，就是&quot;字母异位词&quot;。我们要做的，就是<strong>数清楚每个袋子里有什么积木</strong>。</p>

<p><em>Imagine two bags of Lego pieces. As long as both bags contain the exact same types and counts of pieces — no matter how they&#x27;re arranged — they&#x27;re anagrams. Our job: count the pieces in each bag and compare.</em></p>

<hr/>

<h2>题目 / Problem Statement</h2>

<p><strong>中文:</strong> 给定两个字符串 <code>s</code> 和 <code>t</code>，判断 <code>t</code> 是否是 <code>s</code> 的字母异位词（即用完全相同的字母，重新排列而成）。</p>

<p><strong>English:</strong> Given two strings <code>s</code> and <code>t</code>, return <code>true</code> if <code>t</code> is an anagram of <code>s</code>, and <code>false</code> otherwise. An anagram uses the same characters with the same frequencies.</p>

<pre><code>
Input: s = &quot;anagram&quot;, t = &quot;nagaram&quot;   → Output: True
Input: s = &quot;rat&quot;,     t = &quot;car&quot;       → Output: False
</code></pre>

<hr/>

<h2>解题思路 / Step-by-Step Walkthrough</h2>

<p><strong>核心想法 / Core Idea:</strong></p>
<p>字母异位词 = 每个字符出现的次数完全相同。</p>
<p><em>Anagrams have identical character frequency distributions.</em></p>

<p><strong>方法: 哈希表计数 / Method: Hash Map Counting</strong></p>

<p>用一个字典，遍历 <code>s</code> 时 +1，遍历 <code>t</code> 时 -1。最后所有值都是 0 → 是异位词。</p>
<p><em>Use one dictionary: +1 for each char in <code>s</code>, -1 for each char in <code>t</code>. If all values are 0 → anagram.</em></p>

<h3>具体追踪 / Concrete Trace</h3>

<p><code>s = &quot;anagram&quot;</code>, <code>t = &quot;nagaram&quot;</code></p>

<p><strong>遍历 s (+1):</strong></p>
<pre><code>
&#x27;a&#x27; → count[&#x27;a&#x27;] = 1
&#x27;n&#x27; → count[&#x27;n&#x27;] = 1
&#x27;a&#x27; → count[&#x27;a&#x27;] = 2
&#x27;g&#x27; → count[&#x27;g&#x27;] = 1
&#x27;r&#x27; → count[&#x27;r&#x27;] = 1
&#x27;a&#x27; → count[&#x27;a&#x27;] = 3
&#x27;m&#x27; → count[&#x27;m&#x27;] = 1
</code></pre>
<p>状态 / State: <code>{&#x27;a&#x27;:3, &#x27;n&#x27;:1, &#x27;g&#x27;:1, &#x27;r&#x27;:1, &#x27;m&#x27;:1}</code></p>

<p><strong>遍历 t (-1):</strong></p>
<pre><code>
&#x27;n&#x27; → count[&#x27;n&#x27;] = 0
&#x27;a&#x27; → count[&#x27;a&#x27;] = 2
&#x27;g&#x27; → count[&#x27;g&#x27;] = 0
&#x27;a&#x27; → count[&#x27;a&#x27;] = 1
&#x27;r&#x27; → count[&#x27;r&#x27;] = 0
&#x27;a&#x27; → count[&#x27;a&#x27;] = 0
&#x27;m&#x27; → count[&#x27;m&#x27;] = 0
</code></pre>
<p>状态 / State: <code>{&#x27;a&#x27;:0, &#x27;n&#x27;:0, &#x27;g&#x27;:0, &#x27;r&#x27;:0, &#x27;m&#x27;:0}</code></p>

<p><strong>所有值为 0 → True ✅</strong></p>

<hr/>

<h2>Python 解法 / Python Solution</h2>

<pre><code>
from collections import defaultdict

def isAnagram(s: str, t: str) -&gt; bool:
    # Quick check: different lengths can&#x27;t be anagrams
    # 长度不同直接排除
    if len(s) != len(t):
        return False
    
    # Count character frequencies
    # 统计每个字符出现的频率
    count = defaultdict(int)
    
    # +1 for every char in s
    for char in s:
        count[char] += 1
    
    # -1 for every char in t
    for char in t:
        count[char] -= 1
    
    # If all zeros, they have the same characters
    # 所有计数为零，说明字符完全匹配
    return all(v == 0 for v in count.values())


# 更 Pythonic 的写法 / More Pythonic version:
from collections import Counter

def isAnagram_v2(s: str, t: str) -&gt; bool:
    return Counter(s) == Counter(t)
</code></pre>

<hr/>

<h2>复杂度分析 / Complexity Analysis</h2>

<p>| | 复杂度 / Complexity | 说明 / Explanation |</p>
<p>|---|---|---|</p>
<p>| 时间 / Time | O(n) | n = len(s)，遍历两次 / two passes |</p>
<p>| 空间 / Space | O(k) | k = 字符集大小，最多26个字母 / at most 26 letters |</p>

<p><strong>为什么不排序？/ Why not sort?</strong></p>
<p>排序是 O(n log n)，哈希表是 O(n)，更快。面试时提出这个比较能加分。</p>
<p><em>Sorting is O(n log n) vs O(n) for hash map. Always worth mentioning this tradeoff in interviews.</em></p>

<hr/>

<h2>边界情况 / Edge Cases</h2>

<pre><code>
isAnagram(&quot;a&quot;, &quot;a&quot;)     # True  — single char match
isAnagram(&quot;a&quot;, &quot;b&quot;)     # False — single char mismatch
isAnagram(&quot;&quot;, &quot;&quot;)       # True  — both empty (Counter({}) == Counter({}))
isAnagram(&quot;ab&quot;, &quot;a&quot;)    # False — length check catches this early
isAnagram(&quot;aa&quot;, &quot;bb&quot;)   # False — same length, different chars
</code></pre>

<hr/>

<h2>举一反三 / Pattern Recognition</h2>

<p>这道题的核心模式：<strong>用哈希表统计频率，再比较</strong>。以下题目用同一个模式：</p>

<p><em>Core pattern: <strong>use a hash map to count frequencies, then compare</strong>. Same pattern appears in:</em></p>

<p>| 题目 / Problem | 变化 / Twist |</p>
<p>|---|---|</p>
<p>| #49 Group Anagrams | 把所有互为异位词的字符串分组 / group all anagrams together |</p>
<p>| #438 Find All Anagrams in a String | 滑动窗口找所有异位词位置 / sliding window to find positions |</p>
<p>| #383 Ransom Note | 一个字符串能否由另一个构成 / can s be built from t&#x27;s chars |</p>

<p><strong>进阶思考 / Follow-up:</strong></p>
<p>如果字符串包含 Unicode（中文、emoji）怎么办？用 <code>Counter</code> 依然 work，因为它对任何 hashable 字符都有效。</p>
<p><em>What if strings contain Unicode (Chinese, emoji)? <code>Counter</code> still works — it handles any hashable character.</em></p>

<hr/>
<p><em>Day 2 of 100 | #ByteByByte | Arrays &amp; Hashing 系列</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>🗣️ 软技能 Day 2 / Soft Skills Day 2</h2>
<p><strong>Topic: 没有直接权力如何影响他人 / Influencing Without Authority</strong></p>

<hr/>

<h2>为什么这很重要 / Why This Matters</h2>

<p>在大公司里，真正的工作不是独自完成任务，而是让<strong>别人</strong>帮你完成任务——在你没有权力命令他们的情况下。这是 Senior 和 Staff 工程师的核心技能，也是最常被问到的行为面试题之一。</p>

<p><em>In big tech, the real job isn&#x27;t doing work alone — it&#x27;s getting <strong>others</strong> to do work without being able to order them. This is the core skill separating senior from staff engineers, and one of the most common behavioral interview questions.</em></p>

<hr/>

<h2>经典面试题 / Classic Question</h2>

<p>&gt; &quot;描述一次你需要在没有直接管理权限的情况下影响他人的经历。&quot;</p>
<p>&gt; <em>&quot;Describe a situation where you had to influence others without having direct authority.&quot;</em></p>

<hr/>

<h2>STAR 框架拆解 / STAR Framework Breakdown</h2>

<p><strong>S — Situation（情境）</strong></p>
<p>设定背景：你在哪个团队，影响的是谁（另一个团队、高级别同事、跨职能合作者），以及为什么他们没有义务听你的。</p>

<p><em>Set the scene: which team, who you needed to influence (cross-team, senior colleague, partner org), and crucially — why they had no obligation to listen to you.</em></p>

<p><strong>T — Task（任务）</strong></p>
<p>你的目标是什么？为什么这件事很重要？影响失败会有什么后果？</p>

<p><em>What was your goal? Why did it matter? What was at stake if influence failed?</em></p>

<p><strong>A — Action（行动）</strong> ← 这是重点 / This is where you shine</p>

<p>Senior 级别要展示的不是&quot;我很能说服人&quot;，而是<strong>系统性的影响力策略</strong>：</p>

<p>1. <strong>理解对方的激励机制</strong> — 他们关心什么？什么对他们有利？</p>
<p>2. <strong>用数据说话</strong> — 不是&quot;我觉得应该这样&quot;，而是&quot;数据显示这会影响 X% 用户&quot;</p>
<p>3. <strong>建立盟友</strong> — 先和愿意接受的人对齐，再扩大影响范围</p>
<p>4. <strong>给对方一个赢的理由</strong> — frame 成对他们也有好处，不是你求他们帮忙</p>

<p><em>Don&#x27;t just say &quot;I convinced them.&quot; Show a systematic influence strategy: understand their incentives, use data, build allies, frame it as their win too.</em></p>

<p><strong>R — Result（结果）</strong></p>
<p>量化结果。不只是&quot;成功了&quot;，而是&quot;减少了 X 毫秒延迟&quot;、&quot;帮助团队提前 2 周上线&quot;。</p>

<p><em>Quantify outcomes. Not &quot;it worked&quot; but &quot;reduced P99 latency by 40ms&quot; or &quot;helped the team ship 2 weeks early.&quot;</em></p>

<hr/>

<h2>❌ 坏回答 vs ✅ 好回答 / Bad vs Good Answer</h2>

<p><strong>❌ 坏回答：</strong></p>
<p>&gt; &quot;我们的设计文档需要安全团队 review，但他们很忙不想做。我就发了很多邮件催他们，最后他们 review 了。&quot;</p>

<p>问题：被动、无策略、显示影响力只是&quot;坚持催&quot;，没有展示任何高级技能。</p>

<p><em>Bad: &quot;The security team was busy, so I kept emailing until they reviewed it.&quot; — Passive, no strategy, just persistence.</em></p>

<hr/>

<p><strong>✅ 好回答：</strong></p>
<p>&gt; &quot;我需要安全团队 review 一个涉及用户数据的新功能，但他们的 Q4 排期已经满了，而我们的 launch date 是固定的。我没有直接汇报关系。</p>
<p>&gt;</p>
<p>&gt; 我先花时间了解了安全团队的 OKR——他们那季度的目标之一是&#x27;减少高风险数据暴露事件&#x27;。我把这个功能的 review 包装成他们达成 OKR 的机会，而不是额外负担。</p>
<p>&gt;</p>
<p>&gt; 我准备了一份 1 页的风险摘要，聚焦于如果不 review 可能的合规风险，并提议缩小 review 范围（只看数据流部分，而不是整个 PR）来降低他们的时间成本。</p>
<p>&gt;</p>
<p>&gt; 同时，我找到了一位之前和安全团队合作过的 Staff Engineer，请他帮我引荐，建立了初始信任。</p>
<p>&gt;</p>
<p>&gt; 最终安全团队在 3 天内完成了 review，我们按时上线，功能还因为 review 过程发现并修复了一个边界条件。&quot;</p>

<p><em>Good: Understood their OKRs, reframed as their win, reduced their cost, used a warm introduction to build trust. Showed systematic strategy.</em></p>

<hr/>

<h2>场景模板 / Scenario Template to Adapt</h2>

<pre><code>
情境: 我需要 [另一个团队/高级工程师/PM] 在 [时间节点] 前完成 [X]，
     但他们没有义务优先处理我的需求。

策略:
  1. 我研究了他们的 [优先级/OKR/痛点]
  2. 我将需求包装成对他们的 [利益/风险规避/认可机会]
  3. 我降低了他们的参与成本，通过 [缩小范围/提供草稿/async 方式]
  4. 我通过 [共同认识的人/过去的合作] 建立了信任基础

结果: [量化的结果]
</code></pre>

<hr/>

<h2>Senior/Staff 级别加分项 / Senior/Staff Level Tips</h2>

<p>1. <strong>展示系统性思维，而不是一次性技巧。</strong> Staff 工程师影响的不是一个人，而是建立了一套让他人自愿对齐的系统（写 RFC、建立 review 文化、设计 API 让正确做法成为默认）。</p>

<p>2. <strong>提到失败的尝试。</strong> &quot;我第一次直接发需求，他们无视了。然后我调整策略…&quot; — 这比一帆风顺更真实，也更展示学习能力。</p>

<p>3. <strong>区分说服和操纵。</strong> 好的影响力是基于真实利益对齐，不是包装欺骗。面试官会探究&quot;你是如何确保这对他们也真的有好处的？&quot;</p>

<p><em>Show systematic thinking not one-off tricks. Mention what didn&#x27;t work first — shows learning. Distinguish persuasion (real alignment) from manipulation (packaging deception).</em></p>

<hr/>

<h2>关键要点 / Key Takeaways</h2>

<p>1. <strong>理解对方的激励，而不是假设他们应该配合你</strong></p>
<p>2. <strong>用数据和风险框架，而不是个人请求</strong></p>
<p>3. <strong>降低对方的参与成本</strong> — 帮他们更容易说&quot;好&quot;</p>
<p>4. <strong>量化结果</strong> — &quot;成功了&quot;不够，需要具体数字</p>

<p><em>Understand their incentives. Use data/risk framing, not personal favors. Lower their cost to say yes. Always quantify results.</em></p>

<hr/>
<p><em>Day 2 of 100 | #ByteByByte | 行为面试系列</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>🎨 前端 Day 2 / Frontend Day 2</h2>
<p><strong>Topic: Flexbox — 一维布局的瑞士军刀 / One-Dimensional Layouts Made Easy</strong></p>

<hr/>

<h2>猜猜这段代码输出什么？/ What Does This Code Output?</h2>

<pre><code>
&lt;div class=&quot;container&quot;&gt;
  &lt;div class=&quot;box&quot;&gt;A&lt;/div&gt;
  &lt;div class=&quot;box&quot;&gt;B&lt;/div&gt;
  &lt;div class=&quot;box&quot;&gt;C&lt;/div&gt;
&lt;/div&gt;
</code></pre>

<pre><code>
.container {
  display: flex;
  justify-content: space-between;
  width: 300px;
}
.box {
  width: 80px;
  height: 80px;
  background: steelblue;
}
</code></pre>

<p><strong>你的猜测 / Your guess:</strong></p>
<p>A) 三个方块左对齐，紧靠在一起</p>
<p>B) 三个方块均匀分布，A 在最左，C 在最右，B 在中间</p>
<p>C) 三个方块居中显示</p>
<p>D) 报错，因为 80×3=240 &lt; 300</p>

<hr/>

<p><strong>答案: B ✅</strong></p>

<pre><code>
|容器 300px / container 300px|
|A       |        B        |       C|
 ←80px→  ←←←30px gap→→→  ←80px→
         (C 也是 80px，右端对齐 / C is also 80px, flush right)
</code></pre>

<p><code>space-between</code> 的含义：第一个元素靠左边，最后一个元素靠右边，中间的间距<strong>平均分配</strong>。</p>
<p><em><code>space-between</code>: first item at start, last item at end, remaining space distributed equally between items.</em></p>

<p>剩余空间 = 300 - 80×3 = 60px，分成 2 份间距 = 每份 <strong>30px</strong>。</p>
<p><em>Remaining space = 300 - 240 = 60px, split into 2 gaps = <strong>30px</strong> each.</em></p>

<hr/>

<h2>Flexbox 心智模型 / Mental Model</h2>

<p>Flexbox 的核心：一个<strong>主轴（main axis）</strong>和一个<strong>交叉轴（cross axis）</strong>。</p>

<pre><code>
flex-direction: row (默认/default)

主轴 main axis →→→→→→→→→→→→→→→→→→→→→→→
                ┌──────┐  ┌──────┐  ┌──────┐
                │  A   │  │  B   │  │  C   │
                └──────┘  └──────┘  └──────┘
交叉轴 cross axis ↓ (垂直方向/vertical)

flex-direction: column

主轴 main axis ↓  ┌──────┐
               ↓  │  A   │
               ↓  ├──────┤
               ↓  │  B   │
               ↓  ├──────┤
               ↓  │  C   │
                  └──────┘
交叉轴 cross axis → (水平方向/horizontal)
</code></pre>

<hr/>

<h2>核心属性速查 / Key Properties Cheat Sheet</h2>

<h3>父容器属性 / Container Properties</h3>

<pre><code>
.container {
  display: flex;
  
  /* 主轴方向 / Main axis direction */
  flex-direction: row | row-reverse | column | column-reverse;
  
  /* 主轴对齐 / Main axis alignment */
  justify-content: flex-start | flex-end | center 
                 | space-between | space-around | space-evenly;
  
  /* 交叉轴对齐 / Cross axis alignment */
  align-items: stretch | flex-start | flex-end | center | baseline;
  
  /* 换行 / Wrapping */
  flex-wrap: nowrap | wrap | wrap-reverse;
  
  /* gap (现代写法/modern) */
  gap: 16px;  /* 比 margin 更优雅 / cleaner than margin hacks */
}
</code></pre>

<h3>子元素属性 / Item Properties</h3>

<pre><code>
.item {
  /* 伸长比例 / Grow ratio */
  flex-grow: 0;    /* 默认不伸长 / default: don&#x27;t grow */
  flex-grow: 1;    /* 占据剩余空间 / take remaining space */
  
  /* 收缩比例 / Shrink ratio */
  flex-shrink: 1;  /* 默认允许收缩 / default: can shrink */
  flex-shrink: 0;  /* 禁止收缩 / don&#x27;t shrink */
  
  /* 基准尺寸 / Base size */
  flex-basis: auto | 200px | 30%;
  
  /* 简写 / Shorthand */
  flex: 1;        /* = flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
  flex: 0 0 200px; /* = 固定200px，不伸不缩 / fixed 200px */
}
</code></pre>

<hr/>

<h2>你可能不知道 / You Might Not Know (Gotcha!)</h2>

<p><strong><code>flex: 1</code> 和 <code>flex: 1 1 auto</code> 不一样！</strong></p>

<pre><code>
/* flex: 1 → flex-grow:1, flex-shrink:1, flex-basis: 0 */
/* 基准是 0，意思是从 0 开始按比例分配空间 */
/* base is 0: space is distributed purely by ratio */

/* flex: 1 1 auto → flex-grow:1, flex-shrink:1, flex-basis: auto */
/* 基准是内容大小，先按内容分，剩余的再按比例分 */
/* base is content size: content first, then distribute remaining */

.container { display: flex; width: 300px; }
.a { flex: 1; }          /* a 和 b 各得 150px */
.b { flex: 1; }          /* split evenly from 0 */

/* vs */
.a { flex: 1 1 auto; content: &quot;longer text&quot;; }  /* a 会更宽！*/
.b { flex: 1 1 auto; content: &quot;hi&quot;; }           /* a gets more space! */
</code></pre>

<p><em><code>flex: 1</code> splits space from zero (equal shares). <code>flex: 1 1 auto</code> splits from content size (content-biased). This trips up many senior devs!</em></p>

<hr/>

<h2>经典布局示例 / Classic Layout Example</h2>

<p><strong>圣杯布局（Header + Sidebar + Main + Footer）</strong></p>

<pre><code>
/* 用 Flexbox 实现三列布局 / Three-column layout */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.content-area {
  display: flex;
  flex: 1;  /* 占据剩余高度 / fill remaining height */
}

.sidebar {
  flex: 0 0 240px;  /* 固定宽度，不伸不缩 / fixed width */
}

.main {
  flex: 1;  /* 占据剩余宽度 / take remaining width */
}
</code></pre>

<hr/>

<h2>Mini Challenge 🎯</h2>

<p>用纯 CSS Flexbox（不用 Grid），实现这个布局：</p>

<pre><code>
┌─────────────────────────────┐
│         Header              │
├────────┬────────────────────┤
│Sidebar │   Main Content     │
│(200px) │   (flexible)       │
├────────┴────────────────────┤
│         Footer              │
└─────────────────────────────┘
</code></pre>

<p>侧边栏固定 200px，主内容区自适应，整体高度 100vh。</p>
<p><em>Sidebar fixed at 200px, main content flexible, total height 100vh.</em></p>

<p>答案明天揭晓！/ Answer revealed tomorrow!</p>

<hr/>
<p><em>Day 2 of 100 | #ByteByByte | CSS Fundamentals 系列</em></p>

<hr/>
<h1>🤖 AI</h1>
<h2>🤖 AI Day 2</h2>
<p><strong>Topic: Transformer 是怎么工作的？— &quot;Attention Is All You Need&quot;</strong></p>

<hr/>

<h2>从&quot;翻译&quot;说起 / Start With Translation</h2>

<p>2017年之前，翻译系统用 RNN（循环神经网络）：<strong>逐字读取，逐字生成</strong>。就像一个翻译员，读完一个字才能记下来，再读下一个，记忆有限，长句子容易忘记开头。</p>

<p><em>Before 2017, translation used RNNs: process word by word, like a translator who reads one word at a time with limited working memory. Long sentences = forgotten beginnings.</em></p>

<p>2017年，Google 发了一篇论文：&quot;Attention Is All You Need&quot;。核心思想震惊了整个 AI 界：</p>

<p>&gt; <strong>&quot;你不需要按顺序读句子。你可以一次性看整个句子，然后决定每个词该&#x27;关注&#x27;哪些其他词。&quot;</strong></p>
<p>&gt; <em>&quot;You don&#x27;t need to read sequentially. Look at the whole sentence at once, and let each word &#x27;attend&#x27; to whichever other words are most relevant.&quot;</em></p>

<hr/>

<h2>直觉解释 / Intuitive Explanation</h2>

<p><strong>为什么需要 Attention（注意力机制）？</strong></p>

<p>翻译 &quot;The animal didn&#x27;t cross the street because <strong>it</strong> was too tired.&quot;</p>

<p>&quot;it&quot; 指的是什么？是 &quot;animal&quot; 还是 &quot;street&quot;？</p>

<p>人类一眼就知道是 &quot;animal&quot;（动物会累，街道不会累）。</p>
<p>RNN 在处理 &quot;it&quot; 的时候，离 &quot;animal&quot; 已经太远了，可能已经&quot;忘了&quot;。</p>

<p><strong>Attention 的解法：</strong> 处理 &quot;it&quot; 时，让模型自动&quot;回头看&quot;整个句子，计算 &quot;it&quot; 和每个其他词的相关性分数。</p>

<pre><code>
&quot;it&quot; 与各词的 attention 分数示意 / Attention scores for &quot;it&quot;:
The     → 0.05
animal  → 0.72  ← 高分！/ High score!
didn&#x27;t  → 0.03
cross   → 0.04
the     → 0.02
street  → 0.08
because → 0.03
it      → 0.03
</code></pre>

<p><em>Attention solves the &quot;it&quot; problem: when processing &quot;it&quot;, the model looks back at all words, assigns a relevance score to each, and &quot;pays attention&quot; to &quot;animal&quot; the most.</em></p>

<hr/>

<h2>Transformer 核心机制 / Core Mechanism</h2>

<h3>Self-Attention 三步走 / Three Steps</h3>

<p>每个词（token）会生成三个向量：</p>
<p><em>Each word generates three vectors:</em></p>

<pre><code>
                     ┌─────────────────────────────────────┐
输入词 / Input word  │  Q (Query)  K (Key)   V (Value)     │
&quot;animal&quot;             │  &quot;我想问什么&quot; &quot;我是什么&quot; &quot;我代表什么信息&quot;  │
                     │  &quot;what I ask&quot; &quot;what I am&quot; &quot;my info&quot; │
                     └─────────────────────────────────────┘
</code></pre>

<p><strong>计算过程 / Computation:</strong></p>

<pre><code>
步骤1: Score = Q · K^T / √d_k
       用&quot;查询&quot;和每个词的&quot;键&quot;做点积，得到相关性分数
       Dot product of Query with all Keys → relevance scores

步骤2: Softmax(Score)
       把分数转成概率分布（所有词的权重加起来 = 1）
       Convert scores to probability distribution (weights sum to 1)

步骤3: Output = Σ(weight × V)
       用权重加权所有词的&quot;值&quot;向量，得到最终表示
       Weighted sum of all Value vectors → final representation
</code></pre>

<hr/>

<h2>Transformer 整体架构 / Overall Architecture</h2>

<pre><code>
输入文本 / Input Text: &quot;I love cats&quot;
         │
         ▼
[词嵌入 + 位置编码]
[Token Embedding + Positional Encoding]
  (告诉模型每个词的位置，因为 Attention 本身没有位置感)
  (adds position info, since Attention has no inherent order)
         │
         ▼
┌─────────────────────────────┐
│   Encoder Block × N        │  ← N 层叠加 / N stacked layers
│  ┌──────────────────────┐   │
│  │   Multi-Head         │   │  ← 多个 Attention &quot;头&quot;并行
│  │   Self-Attention     │   │    multiple Attention heads in parallel
│  └──────────┬───────────┘   │
│             │ (+ residual)  │
│  ┌──────────▼───────────┐   │
│  │   Feed Forward       │   │  ← 每个位置独立的 MLP
│  │   Network (FFN)      │   │    per-position MLP
│  └──────────────────────┘   │
└─────────────────────────────┘
         │
         ▼
[丰富的上下文表示 / Rich contextual representation]
</code></pre>

<p><strong>多头注意力 / Multi-Head Attention:</strong> 同时用多个 Attention（比如 8 个），每个关注不同的语义关系（语法、指代、情感等），最后拼接。就像同时从 8 个角度看一张照片。</p>

<p><em>Multi-head: run 8 attention mechanisms in parallel, each learning different relationship types (syntax, coreference, sentiment). Concatenate results. Like viewing a photo from 8 angles simultaneously.</em></p>

<hr/>

<h2>为什么 Transformer 改变了一切？/ Why Did It Change Everything?</h2>

<p>| 特性 / Feature | RNN | Transformer |</p>
<p>|---|---|---|</p>
<p>| 并行计算 / Parallelizable | ❌ 必须顺序 / Must be sequential | ✅ 所有位置同时处理 / All positions at once |</p>
<p>| 长距离依赖 / Long-range | ❌ 容易遗忘 / Forgets | ✅ 直接 Attention / Direct connection |</p>
<p>| 可扩展性 / Scalability | 差 / Poor | 优秀 / Excellent |</p>
<p>| GPU 利用率 / GPU usage | 低 / Low | 高 / High |</p>

<p><strong>这就是 GPT、BERT、Claude、Gemini 的核心。</strong> 这篇 2017 年的论文，启动了整个现代 AI 时代。</p>

<p><em>This is the foundation of GPT, BERT, Claude, Gemini — every modern LLM. The 2017 paper that started the modern AI era.</em></p>

<hr/>

<h2>代码片段：简化版 Attention / Simplified Attention in Code</h2>

<pre><code>
import numpy as np

def scaled_dot_product_attention(Q, K, V):
    &quot;&quot;&quot;
    Q: Query matrix  (seq_len, d_k)
    K: Key matrix    (seq_len, d_k)
    V: Value matrix  (seq_len, d_v)
    &quot;&quot;&quot;
    d_k = Q.shape[-1]
    
    # Step 1: Compute attention scores
    # scores[i][j] = how much position i should attend to position j
    scores = Q @ K.T / np.sqrt(d_k)
    
    # Step 2: Softmax — convert scores to probabilities
    # exp for numerical stability trick usually applied here
    exp_scores = np.exp(scores - scores.max(axis=-1, keepdims=True))
    attention_weights = exp_scores / exp_scores.sum(axis=-1, keepdims=True)
    
    # Step 3: Weighted sum of values
    output = attention_weights @ V
    
    return output, attention_weights

# Example: 3 tokens, d_k=4
np.random.seed(42)
Q = np.random.randn(3, 4)  # 3 tokens asking questions
K = np.random.randn(3, 4)  # 3 tokens presenting keys
V = np.random.randn(3, 4)  # 3 tokens&#x27; actual info

output, weights = scaled_dot_product_attention(Q, K, V)
print(&quot;Attention weights shape:&quot;, weights.shape)  # (3, 3)
# Each row sums to 1.0 — how much each token attends to each other
</code></pre>

<hr/>

<h2>一句话总结 / One-Liner</h2>

<p>&gt; Transformer = &quot;让每个词直接看所有其他词，用相关性加权求和，并行计算，堆叠多层&quot;</p>
<p>&gt; <em>Transformer = &quot;let every word directly look at all others, weight by relevance, compute in parallel, stack many layers&quot;</em></p>

<hr/>

<h2>延伸阅读 / Going Deeper</h2>

<p>- 原始论文 / Original paper: &quot;Attention Is All You Need&quot; (Vaswani et al., 2017)</p>
<p>- 可视化工具 / Visualization: [The Illustrated Transformer](https://jalammar.github.io/illustrated-transformer/)</p>
<p>- 明天 Day 3 AI 主题预告：BERT vs GPT — 为什么双向比单向更聪明（有时候）</p>

<p><em>Tomorrow Day 3 AI preview: BERT vs GPT — why bidirectional beats unidirectional (sometimes).</em></p>

<hr/>
<p><em>Day 2 of 100 | #ByteByByte | AI Foundations 系列</em></p>
]]></description>
    </item>
    <item>
      <title>byte-by-byte — 2026-03-14</title>
      <link>https://github.com/YushengAuggie/byte-by-byte/tree/main/archive</link>
      <guid isPermaLink="false">https://github.com/YushengAuggie/byte-by-byte/archive/2026-03-14</guid>
      <pubDate>Sat, 14 Mar 2026 12:00:00 +0000</pubDate>
      <description><![CDATA[<h1>🏗️ System Design</h1>
<h2>System Design Day 1 — Client-Server Model &amp; How the Internet Works</h2>
<p><em>Date: 2026-03-14 | Category: Fundamentals | Difficulty: Beginner</em></p>

<hr/>

<p>🏗️ <strong>系统设计 Day 1 / System Design Day 1</strong></p>
<p><strong>客户端-服务器模型 &amp; 互联网是怎么运转的</strong></p>
<p><strong>Client-Server Model &amp; How the Internet Works</strong></p>

<hr/>

<p>想象你在一家餐厅点餐。你（客户端）告诉服务员（网络）你想要什么，厨房（服务器）接到订单后准备好食物，再通过服务员把食物送到你面前。互联网的每一次请求，都是这个流程的数字版本。</p>

<p><em>Imagine you&#x27;re ordering food at a restaurant. You (the client) tell the waiter (the network) what you want, the kitchen (the server) prepares it, and the waiter brings it back. Every internet request follows this exact same flow — digitally.</em></p>

<hr/>

<p><strong>架构图 / Architecture Diagram</strong></p>

<pre><code>
你的浏览器 / Your Browser
        |
        | HTTP Request (GET /index.html)
        v
+-------+--------+
|   DNS Resolver  |   &quot;把 google.com 翻译成 IP 地址&quot;
|  (Phone book)   |   &quot;Translates domain → IP address&quot;
+-------+--------+
        |
        | IP: 142.250.80.46
        v
+-------+--------+
|    Internet     |   路由器、交换机、光缆
|  (The pipes)    |   Routers, switches, fiber cables
+-------+--------+
        |
        v
+-------+--------+
|  Web Server     |   nginx / Apache
|  (The waiter)   |   Receives your request
+-------+--------+
        |
        v
+-------+--------+
| App Server      |   Node.js / Django / Spring
| (The kitchen)   |   Runs your business logic
+-------+--------+
        |
        v
+-------+--------+
|   Database      |   PostgreSQL / MySQL / MongoDB
|  (The pantry)   |   Stores &amp; retrieves data
+-------+--------+
        |
        | HTTP Response (200 OK + HTML)
        v
你的浏览器渲染页面 / Browser renders the page
</code></pre>

<hr/>

<p><strong>关键概念 / Key Concepts</strong></p>

<p><strong>1. IP 地址 — 互联网的门牌号</strong></p>
<p>每台联网设备都有一个 IP 地址，就像你家的门牌号。</p>
<p><code>IPv4: 192.168.1.1</code> (4组数字，已快耗尽)</p>
<p><code>IPv6: 2001:0db8:85a3::8a2e:0370:7334</code> (新标准，几乎无限)</p>

<p><em>Every device on the internet has an IP address — like a postal address for packets.</em></p>

<p><strong>2. DNS — 互联网的电话簿</strong></p>
<p>你记 <code>google.com</code>，但计算机需要 <code>142.250.80.46</code>。DNS 负责翻译。</p>
<p>解析顺序：浏览器缓存 → 系统缓存 → 本地 DNS 服务器 → 根域名服务器</p>

<p><em>You type <code>google.com</code>, DNS translates it to an IP. Without DNS, you&#x27;d memorize numbers for every website.</em></p>

<p><strong>3. HTTP/HTTPS — 请求的语言</strong></p>
<pre><code>
GET  /api/users        → 获取资源 / Fetch resource
POST /api/users        → 创建资源 / Create resource
PUT  /api/users/1      → 更新资源 / Update resource
DELETE /api/users/1    → 删除资源 / Delete resource
</code></pre>
<p>HTTPS = HTTP + TLS 加密。没有 HTTPS，你的数据在网络上是明文。</p>

<p><strong>4. TCP/IP — 可靠传输的保障</strong></p>
<p>TCP 保证数据包完整到达，就像注册邮件（有回执）。</p>
<p>UDP 不保证，但更快，适合视频流、游戏（偶尔丢帧没关系）。</p>

<hr/>

<p><strong>为什么这样设计？/ Why This Design?</strong></p>

<p>客户端-服务器分离的核心好处：</p>

<p>- <strong>可扩展性</strong>：可以独立扩展服务器（加机器），不影响客户端</p>
<p>- <strong>安全性</strong>：数据库不暴露给互联网，只有应用服务器能访问</p>
<p>- <strong>可维护性</strong>：前端、后端、数据库各自独立部署</p>

<p><em>Separation of concerns: clients handle presentation, servers handle logic and data. This lets you scale, secure, and maintain each layer independently.</em></p>

<hr/>

<p><strong>别踩这个坑 / Don&#x27;t Fall Into This Trap</strong></p>

<p>❌ <strong>面试时说「用户点击按钮，数据就存到数据库了」</strong></p>
<p>这跳过了太多层。面试官想听到：</p>
<p>DNS解析 → TCP握手 → HTTP请求 → 负载均衡 → 应用服务器 → 数据库</p>

<p>✅ <strong>学会分层描述系统</strong></p>
<p>每次系统设计，先画出这张图的骨架，再逐层深入。</p>

<p><em>In interviews, never skip layers. &quot;The user clicks a button and data gets saved&quot; misses: DNS, TCP handshake, load balancers, app servers, caching, and database transactions. Walk through every hop.</em></p>

<hr/>

<p><strong>明日预告 / Tomorrow</strong></p>
<p>Day 2 将深入 <strong>负载均衡</strong> — 当一台服务器不够用时，如何优雅地横向扩展。</p>
<p><em>Day 2 covers Load Balancing — what happens when one server isn&#x27;t enough.</em></p>

<hr/>
<h1>💻 Algorithms</h1>
<h2>Algorithms Day 1 — #217 Contains Duplicate</h2>
<p><em>Date: 2026-03-14 | Pattern: Arrays &amp; Hashing | Difficulty: Easy</em></p>

<hr/>

<p>💻 <strong>算法 Day 1 / Algorithms Day 1</strong> — #217 Contains Duplicate (Easy) — Arrays &amp; Hashing</p>

<hr/>

<p><strong>现实类比 / Real-World Analogy</strong></p>

<p>想象你在整理一箱名片。你从盒子里一张一张往外拿，每拿出一张，先看看桌上有没有一样的。如果有，说明你有重复的联系人。这就是「哈希集合」的工作方式——把「已见过的」放在一个快速查找的结构里。</p>

<p><em>Imagine going through a box of business cards. You pull each card out and check if you&#x27;ve already put one on the table. If you find a match, you have a duplicate. That&#x27;s exactly what a hash set does — it gives you O(1) lookup for &quot;have I seen this before?&quot;</em></p>

<hr/>

<p><strong>题目 / Problem Statement</strong></p>

<p>给你一个整数数组 <code>nums</code>，如果其中存在任何重复值，返回 <code>true</code>；否则返回 <code>false</code>。</p>

<p><em>Given an integer array <code>nums</code>, return <code>true</code> if any value appears at least twice, <code>false</code> if every element is distinct.</em></p>

<pre><code>
Input:  [1, 2, 3, 1]   → Output: True  (1 出现了两次)
Input:  [1, 2, 3, 4]   → Output: False (每个数都唯一)
Input:  [1, 1, 1, 3, 3, 4, 3, 2, 4, 2] → Output: True
</code></pre>

<hr/>

<p><strong>逐步分析 / Step-by-Step Walkthrough</strong></p>

<p><strong>方法一：暴力法（别用这个）</strong></p>
<p>双重循环，比较每对元素。Time: O(n²)，Space: O(1)</p>
<p>面试中绝对不要停在这里。</p>

<p><strong>方法二：排序法</strong></p>
<p>排序后相邻元素比较。Time: O(n log n)，Space: O(1)</p>
<p>稍好，但破坏了原数组顺序。</p>

<p><strong>方法三：哈希集合（最优解）</strong></p>
<p>维护一个「已见过」的集合，遍历一次搞定。</p>

<pre><code>
nums = [1, 2, 3, 1]
seen = {}

Step 1: num=1  → seen={1}           (新元素，加入)
Step 2: num=2  → seen={1,2}         (新元素，加入)
Step 3: num=3  → seen={1,2,3}       (新元素，加入)
Step 4: num=1  → 1 在 seen 里！→ return True ✓
</code></pre>

<hr/>

<p><strong>Python 解法 / Python Solution</strong></p>

<pre><code>
def containsDuplicate(nums: list[int]) -&gt; bool:
    # Use a hash set for O(1) average-case lookup
    seen = set()
    
    for num in nums:
        if num in seen:
            # Found a duplicate — return immediately (early exit)
            return True
        seen.add(num)
    
    # No duplicates found after full traversal
    return False

# One-liner alternative (Pythonic, but reads the whole list)
# return len(nums) != len(set(nums))
</code></pre>

<p><strong>为什么用 <code>set</code> 而不是 <code>list</code>？</strong></p>
<p><code>x in list</code> → O(n)，要遍历找</p>
<p><code>x in set</code>  → O(1)，哈希直接定位</p>
<p><em><code>list</code> membership check is O(n); <code>set</code> uses hashing for O(1) average lookup. This is the key insight.</em></p>

<hr/>

<p><strong>复杂度 / Complexity</strong></p>

<p>| | 暴力法 | 排序法 | 哈希集合 |</p>
<p>|---|---|---|---|</p>
<p>| Time | O(n²) | O(n log n) | <strong>O(n)</strong> |</p>
<p>| Space | O(1) | O(1) | <strong>O(n)</strong> |</p>

<p>最优解是时间-空间的经典权衡：用 O(n) 额外空间换取 O(n) 时间。</p>
<p><em>Classic time-space tradeoff: we pay O(n) space to get O(n) time.</em></p>

<hr/>

<p><strong>举一反三 / Pattern Recognition</strong></p>

<p>这道题是「Arrays &amp; Hashing」模式的入口。掌握它，你能解：</p>

<p>1. <strong>#1 Two Sum</strong> — 同样的「已见过」思路，存的是值→索引的映射</p>
<p>2. <strong>#128 Longest Consecutive Sequence</strong> — 先用 set 存所有数，再按规律遍历</p>
<p>3. <strong>#49 Group Anagrams</strong> — 用排序后的字符串作 key，用 dict 分组</p>
<p>4. <strong>#36 Valid Sudoku</strong> — 三个方向各维护一个 set</p>

<p><strong>核心模式</strong>：每次遇到「需要快速判断某元素是否出现过」，第一反应是 <code>set</code>；需要记录「出现次数或位置」，用 <code>dict</code>。</p>

<p><em>The pattern: whenever you need &quot;have I seen this before?&quot;, think <code>set</code>. When you need &quot;how many times / where did I see it?&quot;, think <code>dict</code>. This pattern appears in ~30% of easy/medium array problems.</em></p>

<hr/>

<p><strong>Mini Challenge 🎯</strong></p>

<p>如果题目改成：找到数组中出现超过 n/2 次的元素（保证存在），怎么做？</p>
<p><em>What if you need to find the element that appears more than n/2 times? (Boyer-Moore Voting Algorithm — hint for tomorrow&#x27;s pattern thinking)</em></p>

<hr/>
<h1>🗣️ Soft Skills</h1>
<h2>Soft Skills Day 1 — Decision Making Under Uncertainty</h2>
<p><em>Date: 2026-03-14 | Category: Decision Making | Level: Senior/Staff</em></p>

<hr/>

<p>🗣️ <strong>软技能 Day 1 / Soft Skills Day 1</strong></p>
<p><strong>在信息不完整时做出关键技术决策</strong></p>
<p><strong>Making Critical Technical Decisions with Incomplete Information</strong></p>

<hr/>

<p><strong>为什么这很重要 / Why This Matters</strong></p>

<p>初级工程师等信息齐全再行动。高级工程师知道：信息永远不会完全齐全。</p>

<p>系统随时会挂，竞争对手随时会发布，产品上线时间表不会等你做完全量分析。Senior/Staff 工程师和 L3 工程师最大的差距，不是编码能力，而是在模糊中决断的能力。</p>

<p><em>Junior engineers wait for complete information. Senior engineers know it never arrives. The gap between L3 and Staff isn&#x27;t coding — it&#x27;s the ability to make good decisions under uncertainty and own the outcome.</em></p>

<hr/>

<p><strong>STAR 框架拆解 / STAR Framework</strong></p>

<p><strong>Situation（情境）</strong></p>
<p>描述背景，但要聚焦：有什么压力？为什么信息不完整？</p>
<p>⚠️ 不要花超过 20% 的时间在这里</p>

<p><strong>Task（任务）</strong></p>
<p>你需要做什么决定？有什么约束？时间线？</p>
<p>清楚说明为什么这个决定很难。</p>

<p><strong>Action（行动）</strong> ← 这是重点，占 60-70%</p>
<p>- 你如何快速收集最关键的信息？</p>
<p>- 你评估了哪些方案？</p>
<p>- 你如何在时间压力下做出判断？</p>
<p>- 谁参与了决策，如何达成共识？</p>
<p>- 你如何记录决定和理由（ADR）？</p>

<p><strong>Result（结果）</strong></p>
<p>具体指标。但如果结果不完美，更要说清楚你学到了什么。</p>

<hr/>

<p><strong>❌ 糟糕的回答 / Bad Approach</strong></p>

<p>&gt; &quot;我们的数据库响应变慢了，我研究了一下，最后升级了实例类型，问题解决了。&quot;</p>

<p>问题出在哪里：</p>
<p>- 没有体现「信息不完整」的挑战</p>
<p>- 没有说明评估过的其他方案</p>
<p>- 没有数字</p>
<p>- 听起来是一个人默默解决，没有体现协作</p>
<p>- 面试官不知道你的思维过程</p>

<hr/>

<p><strong>✅ 好的回答结构 / Good Approach</strong></p>

<p>&gt; &quot;2024年Q3，我们的支付服务在高峰期 p99 延迟从 80ms 跳到了 800ms，但我们不知道根因——可能是代码、数据库、还是下游 API。问题是周五下午5点发生的，我们有个重要的 launch 在下周一。&quot;</p>
<p>&gt;</p>
<p>&gt; &quot;我需要在没有完整 tracing 数据的情况下（我们当时监控覆盖率只有60%）决定：是回滚最近的部署、扩容数据库、还是限流？&quot;</p>
<p>&gt;</p>
<p>&gt; &quot;我做了三件事：第一，让团队15分钟内各自排查一个方向，并行收集证据。第二，设定了一个阈值——如果30分钟内找不到根因，就先限流保护系统，再继续排查。第三，在 Slack 里实时记录我们的假设和证据，方便团队同步。&quot;</p>
<p>&gt;</p>
<p>&gt; &quot;结果是我们在22分钟内发现是一个 N+1 查询问题被一次数据迁移触发了。我们加了一个临时索引，延迟降到了 95ms，顺利支撑了周一 launch。事后我们补了完整的 APM tracing。&quot;</p>

<hr/>

<p><strong>场景模板 / Scenario Template</strong></p>

<pre><code>
背景: [系统 X] 在 [时间点] 出现了 [问题/机会]
信息缺口: 我们不知道 [关键未知项]，因为 [原因]
约束: [时间/资源/风险约束]
我的决策框架:
  - 快速信息收集: [做了什么]
  - 方案评估: [A vs B vs C，为什么选A]
  - 风险缓解: [如何降低决策风险]
  - 沟通对齐: [如何同步团队/stakeholders]
结果: [具体数字] + [事后学到的]
</code></pre>

<hr/>

<p><strong>Senior/Staff 加分项 / Level-Up Tips</strong></p>

<p>1. <strong>提到 ADR（架构决策记录）</strong></p>
<p>&quot;我们写了一个 ADR 记录了这个决定和我们当时的信息状态，方便3个月后的人理解为什么这么做。&quot;</p>

<p>2. <strong>主动承认决定的局限性</strong></p>
<p>Staff 级别的工程师不假装自己的决定完美，他们说：&quot;这是基于当时信息的最优解，我们设置了一个检查点在30天后重新评估。&quot;</p>

<p>3. <strong>体现系统性思维</strong></p>
<p>不只解决这次的问题，还要防止下次同类问题发生。</p>

<hr/>

<p><strong>关键要点 / Key Takeaways</strong></p>

<p>- 面试官想看的是你的<strong>思维过程</strong>，不只是结果</p>
<p>- 信息不完整≠瘫痪，要展示你如何<strong>快速收集关键信息</strong></p>
<p>- 好的决定有<strong>明确的理由</strong>，坏的结果有<strong>清晰的复盘</strong></p>
<p>- 量化一切：延迟数字、时间窗口、影响用户数</p>

<p><em>The interviewer wants to see: structured thinking under pressure, ability to make good-enough decisions fast, and ownership of outcomes regardless of result.</em></p>

<hr/>
<h1>🎨 Frontend</h1>
<h2>Frontend Day 1 — CSS Box Model: The Foundation of Layout</h2>
<p><em>Date: 2026-03-14 | Category: CSS Fundamentals | Week: 1</em></p>

<hr/>

<p>🎨 <strong>前端 Day 1 / Frontend Day 1</strong></p>
<p><strong>CSS 盒模型 — 所有布局的起点</strong></p>
<p><strong>CSS Box Model — The Foundation of Layout</strong></p>

<hr/>

<p><strong>猜猜这段代码输出什么？/ What does this code output?</strong></p>

<pre><code>
.box {
  width: 100px;
  padding: 20px;
  border: 5px solid black;
  margin: 10px;
}
</code></pre>

<pre><code>
&lt;div class=&quot;box&quot;&gt;Hello&lt;/div&gt;
</code></pre>

<p>问题：<code>.box</code> 在页面上占多少宽度？</p>
<p><em>Question: How wide does <code>.box</code> actually appear on screen?</em></p>

<p>A) 100px</p>
<p>B) 150px</p>
<p>C) 160px</p>
<p>D) 170px</p>

<p>答案是 <strong>C) 160px</strong> — 但等等，很多人会猜 A！</p>
<p><em>Most people guess A. The answer is C — here&#x27;s why.</em></p>

<hr/>

<p><strong>盒模型可视化 / Box Model Visualization</strong></p>

<pre><code>
+------------------------------------------+
|              margin: 10px                |
|  +------------------------------------+  |
|  |         border: 5px               |  |
|  |  +------------------------------+ |  |
|  |  |       padding: 20px          | |  |
|  |  |  +------------------------+  | |  |
|  |  |  |   content: 100px wide  |  | |  |
|  |  |  |      &quot;Hello&quot;           |  | |  |
|  |  |  +------------------------+  | |  |
|  |  |                              | |  |
|  |  +------------------------------+ |  |
|  +------------------------------------+  |
+------------------------------------------+

实际渲染宽度 / Rendered width:
100 (content) + 20*2 (padding) + 5*2 (border) = 150px
注意：margin 不计入元素宽度，但影响占位空间
</code></pre>

<p>默认的 <code>box-sizing: content-box</code> 意味着 <code>width</code> 只是内容区域的宽度。</p>
<p>Padding 和 border 会叠加在外面，让元素比你想的更大。</p>

<hr/>

<p><strong>解决方案：box-sizing: border-box</strong></p>

<pre><code>
/* 现代 CSS 的最佳实践 / Modern CSS best practice */
*, *::before, *::after {
  box-sizing: border-box;
}

.box {
  width: 100px;   /* Now this IS the final rendered width */
  padding: 20px;
  border: 5px solid black;
  /* Content area auto-shrinks to: 100 - 40 - 10 = 50px */
}
</code></pre>

<p>用了 <code>border-box</code> 后，<code>width: 100px</code> 就真的是 100px，padding 和 border 都&quot;向内压缩&quot;。这是几乎所有现代 CSS 框架（Bootstrap、Tailwind）默认使用的设置。</p>

<p><em>With <code>border-box</code>, <code>width</code> means what you think it means. Padding and border carve inward. This is why every modern CSS framework resets box-sizing globally.</em></p>

<hr/>

<p><strong>你可能不知道 / You Might Not Know</strong></p>

<p><strong>Gotcha #1: margin 不是元素的一部分</strong></p>
<p><code>margin</code> 是元素与其他元素之间的空白，不影响元素本身的宽度，但影响布局空间。用 <code>background-color</code> 你会发现 background 不延伸到 margin 里。</p>

<p><strong>Gotcha #2: Margin Collapse（外边距折叠）</strong></p>
<pre><code>
.top    { margin-bottom: 20px; }
.bottom { margin-top: 30px; }
</code></pre>
<p>两个块级元素垂直相邻，你以为间距是 50px，实际是 <strong>30px</strong>（取较大值）。</p>
<p>水平方向的 margin 不会折叠，只有垂直方向才有这个&quot;惊喜&quot;。</p>

<p><em>Vertical margins between block elements collapse to the larger value. Horizontal margins never collapse. This trips up every developer at least once.</em></p>

<p><strong>Gotcha #3: inline 元素的 padding/margin 行为不同</strong></p>
<p><code>&lt;span&gt;</code> 这类行内元素，设置 <code>padding-top/bottom</code> 和 <code>margin-top/bottom</code> 不会影响行高，效果跟你想的不一样。要控制高度，先把它变成 <code>inline-block</code>。</p>

<hr/>

<p><strong>Mini Challenge 🎯</strong></p>

<p>不用打开浏览器，算出这个元素的实际渲染宽度：</p>

<pre><code>
.card {
  box-sizing: border-box;
  width: 300px;
  padding: 16px;
  border: 2px solid #eee;
  margin: 24px auto;
}
</code></pre>

<p>渲染宽度是多少？内容区域宽度是多少？</p>
<p><em>What&#x27;s the rendered width? What&#x27;s the content area width?</em></p>

<p>答案下方揭晓：</p>
<p>- 渲染宽度：<strong>300px</strong>（因为 <code>border-box</code>）</p>
<p>- 内容区域：300 - 32 - 4 = <strong>264px</strong></p>

<hr/>
<h1>🤖 AI</h1>
<h2>AI Day 1 — AI News Roundup</h2>
<p><em>Date: 2026-03-14 | Mode: NEWS</em></p>

<hr/>

<p>🤖 <strong>AI Day 1 — 本周 AI 大事件 / This Week in AI</strong></p>

<hr/>

<p><strong>📰 Story 1: Claude 1M Context 全面开放</strong></p>
<p><strong>Claude&#x27;s 1M Token Context Window Goes Generally Available</strong></p>

<p>Anthropic 昨天（3月13日）宣布，Claude Opus 4.6 和 Sonnet 4.6 的 100万 token 上下文窗口正式 GA，并且<strong>不收长上下文溢价</strong>——无论你发送 9K 还是 900K token，每 token 定价相同（Opus: $5/$25/M，Sonnet: $3/$15/M）。同时单次请求可以包含最多 600 张图片或 PDF 页面（之前是 100）。</p>

<p><em>Anthropic made the 1M context window for Claude Opus 4.6 &amp; Sonnet 4.6 generally available on March 13 with no long-context premium. Same per-token price whether you send 9K or 900K tokens. Media limits expanded 6x to 600 images/PDFs per request.</em></p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>
<p>对于需要处理整个代码库、大型合同文档、或长时间 agent 运行的工程师来说，这是实质性突破。之前 200K 以上需要特殊 beta header，现在自动生效。Claude Code 的 Max/Team/Enterprise 用户也自动获得 1M 上下文，减少 compaction 中断——Anthropic 表示这让 compaction 事件减少了 15%。</p>

<p><em>For engineers working with large codebases, legal documents, or long-running agents: this eliminates forced context compression. A 1M context means you can load an entire enterprise codebase and reason across it without losing track of earlier decisions.</em></p>

<hr/>

<p><strong>📰 Story 2: 「会思考的 AI」正在改变代码审查</strong></p>
<p><strong>AI Agents Are Changing Code Review</strong></p>

<p>越来越多的团队开始用 AI agent（Claude Code、Devin、Copilot Workspace）做第一轮代码审查。一个真实案例：某公司把整个 diff 喂给 Opus 4.6 的 1M 上下文，拿到比分块处理高质量得多的跨文件依赖分析。</p>

<p><em>Teams are deploying AI agents as first-pass code reviewers. With 1M context, agents can ingest full diffs and reason about cross-file dependencies that chunking strategies miss.</em></p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>
<p>这不是说 AI 要取代 code review，而是说 AI 能处理「检查你有没有更新所有调用者」、「这个改动和3个文件之外的逻辑一致吗」这类枯燥但重要的检查，让人类 reviewer 聚焦在架构和意图层面。</p>

<p><em>AI handles the mechanical review (did you update all callers? is this consistent with the contract 3 files away?). Humans focus on intent and architecture. Your job as a reviewer is evolving.</em></p>

<hr/>

<p><strong>📰 Story 3: 多模态 AI 的「看懂图纸」能力</strong></p>
<p><strong>Multimodal AI Learning to Read Engineering Diagrams</strong></p>

<p>前沿模型处理架构图、电路图、数学公式的能力在过去一年显著提升。工程师们开始用它来：解读遗留系统的手绘架构图、分析竞争对手产品的硬件拆解照片、把 Figma 截图直接转成 React 组件。</p>

<p><em>Frontier models have dramatically improved at reading architecture diagrams, circuit schematics, and hand-drawn flowcharts. Engineers are using this to digitize legacy documentation and convert design screenshots to code.</em></p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>
<p>如果你的团队还有一堆「只有某个老员工看得懂」的架构图、Confluence 里的白板照片，现在是时候让 AI 把这些知识结构化了。技术债不只是代码债，文档债也是。</p>

<p><em>The &quot;tribal knowledge&quot; locked in whiteboard photos and napkin sketches can now be extracted. Teams that act on this will onboard engineers faster and reduce key-person risk.</em></p>

<hr/>

<p><strong>📰 Story 4: Vibe Coding 的隐藏成本</strong></p>
<p><strong>The Hidden Costs of Vibe Coding</strong></p>

<p>「Vibe coding」（完全让 AI 写代码，自己不看细节）在 Twitter/X 上很流行，但越来越多的工程师报告了真实代价：安全漏洞（AI 生成了但没人审查）、架构债（快速生成的代码结构混乱）、以及最麻烦的——你不理解自己系统的工作原理，无法 debug。</p>

<p><em>&quot;Vibe coding&quot; — prompting AI to build entire features without reviewing the output — is creating a new class of tech debt: security vulnerabilities nobody audited, architectural chaos from unreviewed code, and engineers who can&#x27;t debug systems they nominally wrote.</em></p>

<p><strong>为什么你应该关心 / Why You Should Care:</strong></p>
<p>AI 是加速器，不是替代品。最有效的工程师是「AI 辅助」而不是「AI 依赖」。理解你代码中每一个关键决策，即使是 AI 建议的，是职业生涯的护城河。</p>

<p><em>The engineers who thrive long-term use AI as an accelerator, not a replacement for understanding. Owning your code means being able to explain every key decision — even if AI suggested it.</em></p>

<hr/>

<p><strong>本周一句话总结 / One-Line Summary</strong></p>

<p>上下文窗口越来越大，AI 能「记住」的越来越多——但你需要理解的也越来越多。工具在进化，思维方式也得跟上。</p>

<p><em>Context windows are expanding, AI can remember more — but so does your responsibility to understand what it&#x27;s doing. The tools are evolving; so must the mindset.</em></p>
]]></description>
    </item>
  </channel>
</rss>
