Skip to content

D4: decide the caching strategy per public surface — ADR (#234) #241

Description

@xenodeve

Part of #234 · PRD: docs/prd/2026-07-26-public-read-latency-and-cache-strategy.md (D4)

This is a decision, not an implementation. It is ready-for-human on the one question that is genuinely not mine to settle.

EN

#238–#240 make /projects sub-second while it stays a dynamic route. What they cannot do is make it CDN-class (/faq serves in 0.07–0.11 s without invoking a function), because HTML is still rendered per request. Getting there requires one of two hard-to-reverse changes, and choosing between them is what this issue is for.

Option A — cacheComponents: true. Next 16 removed experimental.ppr; this is the only opt-in that lets a searchParams page have a prerendered static shell with the dynamic part streamed. It changes caching semantics app-wide, so every page needs auditing, and 'use cache'/'use cache: remote' become meaningful only alongside a cacheHandlers durable store (cacheHandlers.md:24,66).

Option B — per-filter prerendered routes. e.g. /projects/c/[category] with generateStaticParams, keeping /projects prerendered for the unfiltered list, plus redirects so existing ?category=… links keep working. Cannot cover q (free-text search) at all, and category × tag × tech is combinatorial — so it is a partial fix by construction.

Option C — accept dynamic. Sub-second from #238–#240 is enough; spend the effort elsewhere.

The ADR must also settle the surfaces this touches beyond /projects: /blog has the identical searchParams shape and is also uncached (0.65 s measured), and /projects/[slug] reads the DB per request too.

Acceptance criteria

  • an ADR records the caching strategy per public surface — which pages are static, which are ISR and with what window, which are deliberately dynamic and why
  • the ADR states how the choice interacts with ADR 0004's on-demand revalidation and ADR 0014's three cache layers, including that Cloudflare "Cache Everything" stays rejected
  • the decision names its failure mode, not just its benefit
  • /blog and /projects/[slug] are each either covered by the decision or explicitly listed as out of scope with a reason
  • whichever option wins, the follow-up implementation issues are filed from the ADR — not started before it

TH

ส่วนหนึ่งของ #234 · PRD: docs/prd/2026-07-26-public-read-latency-and-cache-strategy.md (D4)

นี่คือการตัดสินใจ ไม่ใช่การ implement เป็น ready-for-human ในคำถามข้อเดียวที่ไม่ใช่เรื่องที่ผมควรตัดสิน

#238–#240 ทำให้ /projects ต่ำกว่า 1 วินาทีขณะที่มันยังเป็น dynamic route สิ่งที่มันทำไม่ได้คือทำให้เป็นระดับ CDN (/faq เสิร์ฟใน 0.07–0.11 วิ โดยไม่เรียก function) เพราะ HTML ยัง render ต่อ request การไปถึงจุดนั้นต้องใช้การเปลี่ยนแปลงที่ย้อนยากหนึ่งในสองแบบ และการเลือกคือเหตุที่ issue นี้มีอยู่

ทางเลือก A — cacheComponents: true Next 16 ถอด experimental.ppr ออก นี่เป็นทางเปิดใช้เดียวที่ทำให้หน้าที่อ่าน searchParams มี static shell ที่ prerender พร้อมสตรีมส่วน dynamic มันเปลี่ยน semantics การแคช ทั้งแอป ฉะนั้นทุกหน้าต้องถูกตรวจ และ 'use cache'/'use cache: remote' จะมีความหมายเฉพาะเมื่อมี cacheHandlers ที่เก็บแบบ durable ควบคู่ (cacheHandlers.md:24,66)

ทางเลือก B — route ที่ prerender ต่อตัวกรอง เช่น /projects/c/[category] ด้วย generateStaticParams เก็บ /projects ที่ prerender ไว้สำหรับรายการไม่กรอง บวก redirect เพื่อให้ลิงก์ ?category=… เดิมยังทำงาน มันครอบ q (ค้นหาข้อความอิสระ) ไม่ได้เลย และ category × tag × tech เป็น combinatorial จึงเป็นการแก้บางส่วนโดยโครงสร้าง

ทางเลือก C — ยอมรับว่าเป็น dynamic ต่ำกว่า 1 วินาทีจาก #238–#240 พอแล้ว เอาแรงไปทำอย่างอื่น

ADR ต้องตัดสินผิวสัมผัสอื่นที่เกี่ยวด้วย: /blog มีรูปแบบ searchParams เหมือนกันเป๊ะและไม่ถูกแคชเช่นกัน (วัดได้ 0.65 วิ) และ /projects/[slug] ก็อ่าน DB ต่อ request

เกณฑ์รับงาน

  • มี ADR บันทึกกลยุทธ์แคช ต่อผิวสาธารณะ — หน้าไหน static, หน้าไหน ISR และ window เท่าไร, หน้าไหน dynamic โดยเจตนาและเพราะอะไร
  • ADR ระบุว่าการเลือกนั้นสัมพันธ์กับ on-demand revalidation ของ ADR 0004 และแคชสามชั้นของ ADR 0014 อย่างไร รวมถึงว่า Cloudflare "Cache Everything" ยังถูกปฏิเสธ
  • การตัดสินใจระบุ failure mode ของมัน ไม่ใช่แค่ประโยชน์
  • /blog และ /projects/[slug] ถูกครอบด้วยการตัดสินใจ หรือถูกระบุชัดว่าอยู่นอกขอบเขตพร้อมเหตุผล
  • ทางเลือกไหนชนะ ให้ยื่น issue implement ตามหลัง ADR — ไม่เริ่มก่อนมันเสร็จ

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-humanRequires human implementation (secrets/dashboard)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions