Kaen — เรียนวิศวกรรมซอฟต์แวร์เป็นภาษาคน
เรียนเรื่องยากของซอฟต์แวร์ เป็นภาษาคน
คอร์สเชิงลึกภาษาไทย พร้อมไดอะแกรมโต้ตอบและแบบฝึกหัด เลือกเส้นทางที่ใช่ — DDD, .NET, AI Engineering หรือ Rust
เส้นทาง DDD — หลักการและกลยุทธ์
10 คอร์สออกแบบซอฟต์แวร์รอบ domain ธุรกิจ ตั้งแต่แนวคิดหลักจนถึงการค้น domain ทั้งทีม แล้วปิดท้ายด้วยสองคอร์สที่คอมไพล์ได้ คือคอร์สที่ถามว่าอะไรบังคับโครงสร้างนั้น และคอร์สที่ถามว่าความถูกต้องข้างในเดินทางออกไปถึงผู้เรียกหรือเปล่า
- ระดับกลาง
Domain-Driven Design ฉบับภาษาคน
ออกแบบซอฟต์แวร์รอบ domain ธุรกิจจริง ไม่ใช่รอบฐานข้อมูล
6 บทเรียน
เริ่มเรียน → - ระดับกลาง
DDD Distilled — กลั่นจากตำรา
แปลพร้อมสรุปความเข้าใจจากหนังสือ Domain-Driven Design Distilled ของ Vaughn Vernon (2016)
7 บทเรียน
เริ่มเรียน → - ระดับสูง
Patterns, Principles & Practices of DDD
แปลไทยครบทั้งเล่มจากตำราของ Scott Millett & Nick Tune — ตั้งแต่หลักการ กลยุทธ์ ยุทธวิธี จนถึงสถาปัตยกรรม app (26 บท)
26 บทเรียน
เริ่มเรียน → - ระดับเริ่มต้น
Event Storming
ค้น domain ทั้งทีมด้วย Post-it สีส้มภายในไม่กี่ชั่วโมง
6 บทเรียน
เริ่มเรียน → - ระดับกลาง
Kotlin + Spring Boot + Clean Architecture — กฎทิศทางที่ compiler บังคับให้
คอร์สที่ถามว่าอะไรบังคับโครงสร้างหลังจากออกแบบเสร็จแล้ว — สร้างระบบจองคิวคลินิกด้วย Kotlin 2.4.10, Spring Boot 4.1.1 และ Gradle multi-module แล้วพิสูจน์ทีละข้อว่ากฎทิศทางบังคับด้วย build ได้จริง ไม่ใช่ข้อตกลงในเอกสาร ไล่จากการแยกโมดูลที่ทำให้ import Spring ใน domain แปลไม่ผ่าน ผ่าน @Entity ที่แปลเขียวและสตาร์ตขึ้นแล้วไปตายที่ save ครั้งแรก ราคาของ persistence model ตัวที่สองที่นับเป็นบรรทัด ขอบ transaction ที่ use case จนถึงสองบทที่คอร์สนี้มีอยู่เพื่อมัน คือ invariant ที่กว้างกว่า aggregate จนโดเมนบังคับเองไม่ได้ และ optimistic locking ที่ทิ้งผู้แพ้ ไม่ใช่รวมของสองฝั่ง ทุกข้ออ้างมาจาก probe ที่รันได้ ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
ขอบ HTTP ของคลินิก — สัญญาที่ผู้เรียกปลายสายอ่านออก
คอร์สต่อจาก clinic-clean-arch-kotlin ที่ถามคำถามเดียว คือความถูกต้องทั้งหมดที่สร้างไว้ข้างใน เดินทางออกไปถึงผู้เรียกปลายสายหรือเปล่า คำตอบที่วัดได้คือไม่ เพราะคำขอที่ผิดกับระบบที่พังจริงตอบกลับด้วย body รูปเดียวกันเป๊ะ ต่างกันแค่ตัวเลข และความขัดแย้งของข้อมูลที่ฐานข้อมูลกันไว้ได้ กลายเป็น 500 ที่แปลว่าเราพัง ผู้เรียกจึงลองใหม่ทั้งที่ลองกี่ครั้งก็ชนเหมือนเดิม ไล่จาก body ที่ไม่มีใครใช้ได้ ผ่าน problemdetails ที่แก้ครึ่งเดียว ตัวจัดการที่ไม่เคยรันสักครั้งโดยไม่มีอะไรเตือน การแปลง 23P01 เป็น 409 คีย์ที่แยกคำขอเดิมออกจากคำขอใหม่ที่บังเอิญเหมือนกัน จนถึงสัญญาของขอบที่เขียนเป็นตารางแล้วบังคับด้วยเทสต์ ทุกข้ออ้างมาจาก probe ที่รันได้ วัดด้วยรหัส HTTP ชื่อคีย์และจำนวนแถว ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
คัดกรองผู้เรียกของคลินิก — ใครเรียกได้ และเห็นข้อมูลของใคร
คอร์สที่สามของซีรีส์คลินิก ต่อจาก clinic-http-boundary โดยตรง คอร์สก่อนจบด้วยสัญญาที่ผู้เรียกอ่านออก body รูปเดียวทั้งระบบ บังคับด้วยเทสต์แล้ว คอร์สนี้เพิ่ม dependency หนึ่งบรรทัดแล้วถามว่าสัญญานั้นเหลืออะไร คำตอบที่วัดได้คือ body ของคำตอบที่ปฏิเสธผู้เรียกว่างเปล่าสนิท ไม่มีคีย์สักตัว และไม่มีเทสต์ไหนแดง เพราะเรื่องเกิดใน filter chain ซึ่งอยู่ก่อน DispatcherServlet ตัวจัดการที่คอร์สก่อนสร้างไว้จึงไม่มีทางเห็น ไล่จากหนึ่งบรรทัดที่ทำสัญญาพัง ผ่าน config รูปเก่าที่ไม่คอมไพล์แล้ว คำถามว่าใครเลือกระหว่าง 401 กับ 403 ซึ่งคำตอบไม่ใช่กฎที่เราเขียน chain ที่ถูกกลืนจนกฎข้างในไม่มีผล การเอา body กลับเข้าสัญญาเดิม จนถึงการรั่วข้าม tenantที่ไม่มีรหัสข้อผิดพลาดให้ดู มีแต่จำนวนแถว ทุกข้ออ้างมาจาก probe ที่รันได้ วัดด้วยรหัส HTTP ชื่อคีย์ ชื่อ filter และจำนวนแถว ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
คำตอบที่คลินิกจำไว้ — cache ที่จำคำตอบ แต่ลืมว่าใครถาม
คอร์สที่สี่ของซีรีส์คลินิก ต่อจาก clinic-security โดยตรง คอร์สก่อนปิดรูข้ามผู้เช่าไว้ที่บทสุดท้าย รูที่ไม่มีรหัส HTTP ให้ดู เห็นได้ทางเดียวคือจำนวนแถวที่คืนกลับไป คอร์สนี้เพิ่มชั้นที่จำคำตอบไว้ แล้วถามคำถามถัดไปของสัญญาเดียวกัน คือรูนั้นกลับมาไหม และถ้ากลับมา มีอะไรแดงบ้าง คำตอบที่วัดได้คือกลับมาเต็มรูป clinic-b ได้สองแถวของ clinic-a พร้อมรหัส 200 และสามคำขอทำให้ฐานข้อมูลถูกถามครั้งเดียว ส่วนความแดงไม่ได้ขึ้นกับ code ของแอป มันขึ้นกับหนึ่งบรรทัดใน config ของเทสต์ ไล่จากคำตอบที่เป็นของคนอื่น ผ่าน annotation ที่ครบแล้วแอปยังไม่สตาร์ต คำถามว่าใครเป็นคนสร้างกุญแจ คำเรียกใน class เดียวกันที่ไม่เคยถึงตัวห่อ อ็อบเจ็กต์ที่ยื่นออกไปแล้วถูกแก้ แถวที่ไม่เคย commit แต่ยังถูกคืน บรรทัดเดียวที่ทำให้เทสต์ไม่ได้ทดสอบอะไร จนถึงสัญญาของชั้นที่จำคำตอบ ทุกข้ออ้างมาจาก probe ที่รันได้ วัดด้วยจำนวนแถว จำนวน query ชื่อกุญแจ และรหัส HTTP ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
คำขอข้ามต้นทางของคลินิก — เบราว์เซอร์บล็อกคำตอบ แต่แถวลงไปแล้ว
คอร์สที่ห้าของซีรีส์คลินิก และเป็นคำตอบของบรรทัดเดียวที่คอร์ส clinic-security ประกาศไว้เองว่าใหญ่เกินกว่าแปดบทของมันจะรับไหว คอร์สนั้นปิด CSRF ทิ้งด้วยหนึ่งบรรทัด ในกฎขั้นต่ำที่ 10 จาก 12 ทรงของคอร์สนั้นตั้งอยู่บน และไม่มีบทไหนบอกว่ามันปิดอะไรไป คำถามของคอร์สนี้คือเบราว์เซอร์บอกว่าคำขอถูกบล็อกด้วย CORS แล้วฐานข้อมูลได้แถวเพิ่มกี่แถว คำตอบที่วัดได้คือหนึ่งแถว server ตอบ 200 โดยไม่มี Access-Control-Allow-Origin ออกมาสักตัว เบราว์เซอร์ปฏิเสธที่ขั้นอ่านคำตอบ ส่วนการเขียนเกิดไปแล้ว ยกเว้นทรงที่ต้อง preflight ซึ่งวัดได้ OPTIONS = 1 และ POST = 0 คือการเขียนไม่เคยเกิด เส้นแบ่งจึงไม่ได้อยู่ที่ข้ามโดเมนหรือไม่ มันอยู่ที่ content-type กับ header ข้อที่คมที่สุดคือ GET ที่เขียนฐานข้อมูล ผ่านการกันของ CSRF ไปทั้งดุ้นด้วย 200 และหนึ่งแถว ส่วนตัวที่ปิดรูนั้นได้คือ CORS ซึ่งตอบ 403 และไม่มีแถวลง ไล่จากบรรทัดที่ถูกปิด token ที่ผูกกับ session ลำดับ filter ที่เลือกรหัส method ที่ CSRF ไว้ใจ แถวที่ลงไปแล้ว คำขอที่ไม่เคยออกเดินทาง ตอนที่ server เป็นคนปฏิเสธ จนถึงสองกลไกกับสองรูที่มันปิดคนละรู ทุกข้ออ้างมาจาก probe ที่รันได้ วัดด้วยจำนวนแถว จำนวนคำขอ จำนวน header และรหัส HTTP ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
row-level security ของคลินิก — เปิดแล้ว policy ยังไม่เคยถูกเรียกใช้
คอร์สที่หกของซีรีส์คลินิก และเป็นคอร์สแรกของไซต์ที่ถามว่า policy ที่ผูกอยู่กับตารางแล้วและเขียนถูกทุกตัวอักษร กันแถวของผู้เช่าคนอื่นไว้ได้จริงหรือยัง คำตอบที่วัดได้คือยัง เจ้าของตารางที่สั่ง ENABLE ROW LEVEL SECURITY แล้วอ่านตารางหกแถวของตัวเอง ยังได้ 6 แถวครบ เติม FORCE ROW LEVEL SECURITY เข้าไปโดยไม่แตะ SQL ของ policy สักตัวอักษร แล้วได้ 0 แถว ตั้ง GUC เป็นผู้เช่า a หรือ b ได้ 3 แถว และสั่ง NO FORCE ทั้งที่ GUC ยังตั้งอยู่ กลับไปได้ 6 แถว สิ่งที่เปลี่ยนคือธงใน catalog ไม่ใช่นิพจน์ที่เขียนไว้ ธีสิสพลิกอีกสองชั้น ชั้นที่สองคือหน่วยที่จับการรั่วนี้ได้มีหน่วยเดียวคือจำนวนแถว เพราะคำขอที่รั่วสำเร็จทุกครั้งจึงไม่มีรหัสข้อผิดพลาดให้ดู ชั้นที่สามคือหน่วยนั้นมีจุดบอดของมันเอง ฟังก์ชันของ policy ที่ไม่ได้ตรึง search_path ทำให้ผู้เช่าที่เห็นแถวสลับกัน โดยยอดรวมค้างอยู่ที่ 3 ทุกสถานะ ไล่จาก policy ที่ไม่ทำอะไรเลย คีย์ของผู้เช่าที่เป็นค่าตั้งของ session การเขียนที่ถูกกันด้วย policy ที่คุณไม่ได้เขียน การปฏิเสธที่ไม่ส่งเสียง ประตูที่ policy มองไม่เห็น การโจมตีที่ไม่ขยับจำนวนแถว connection pool ที่พกค่าตั้งติดไปด้วย จนถึงตารางสัญญาว่าผู้อ่านพูดอะไรได้แล้ว ทุกข้ออ้างมาจาก probe ที่คุณรันเองได้ วัดด้วยจำนวนแถว จำนวนแถวที่ถูกกระทำ จำนวน policy รหัส SQLSTATE ธงบูลีนใน catalog และรหัส HTTP ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน →
เส้นทาง .NET — สร้างและดูแล domain Order
9 คอร์สลงมือสร้าง platform ฟู้ดเดลิเวอรีจริงทีละชั้น ตั้งแต่ C# สมัยใหม่จนถึง observability
- ระดับเริ่มต้น
C# สมัยใหม่สำหรับ DDD — ปูพื้นก่อนลงมือ
เรียน C# สมัยใหม่ด้วยการสร้าง domain Order ตั้งแต่ศูนย์ — จุดเริ่มต้นที่แนะนำของสาย .NET/DDD ทั้งเส้น ตั้งแต่ record ที่กลายเป็น Value Object, pattern matching ที่ขับเคลื่อนสถานะ, nullable reference types ที่กลายเป็น guard clause, ไปจนถึง async/await ที่กลายเป็นรูปร่างของ port
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
Clean Architecture ด้วย .NET
ลงมือสร้าง ASP.NET Core API จริงทีละ layer ตาม Dependency Rule — domain ฟู้ดเดลิเวอรีต่อจาก Event Storming พร้อม EF Core, CQRS/MediatR, DI และ test
8 บทเรียน
เริ่มเรียน → - ระดับสูง
แปลง DDD tactical patterns เป็น code จริง (C#)
ลงลึก domain Order เดิมจาก Clean Architecture .NET — Value Object, Entity, Aggregate, state machine, Domain Event, Specification และ Factory ที่รักษากฎธุรกิจไว้ใน code
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
ทดสอบ app DDD/Clean Architecture ให้เป็น
พิสูจน์ว่า domain Order จาก Clean Architecture .NET + DDD in Code ทำงานถูกจริง — ยูนิต test domain แบบไม่ต้อง mock, test double, Testcontainers และ e2e ผ่าน API พร้อมพีระมิด test และสี่เสาหลัก
8 บทเรียน
เริ่มเรียน → - ระดับสูง
เก็บ Aggregate ลงฐานข้อมูลด้วย EF Core โดยไม่ทำลาย model
เก็บ domain Order เดิมทั้งก้อนลง SQL อย่างซื่อสัตย์ต่อ model — strongly-typed ID, owned type, migration, optimistic concurrency, transaction boundary และ query ให้ไม่ N+1 ต่อจาก Clean Architecture บท 4
8 บทเรียน
เริ่มเรียน → - ระดับสูง
ออกแบบระบบเงินและ Ledger ด้วย DDD
เงินไม่มีวันหาย — วางระบบ double-entry ledger ให้ platform ฟู้ดเดลิเวอรีเดิม รองรับ wallet, PromptPay, escrow ระหว่างส่งอาหาร, settlement, payout ให้ร้าน/ไรเดอร์, refund และ reconcile ยอดทุกบาททุกสตางค์
8 บทเรียน
เริ่มเรียน → - ระดับสูง
แยก Bounded Context แล้วเชื่อมกลับ — Modular Monolith สู่ Service
ขยาย domain Order เดิมเป็น 4 bounded context (Ordering, Kitchen, Delivery, Payment) เชื่อมกันด้วย integration event และ transactional outbox ที่เชื่อถือได้ ก่อนแตก1 context ออกเป็น service จริงด้วย Wolverine + RabbitMQ
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
Observability platform Order — OpenTelemetry + ClickStack
ปิดเส้นทาง platform Order (#5–#10) ด้วยชั้น OPERATE ที่ขาดไป — instrument ระบบ 4 bounded context ด้วย OpenTelemetry ที่เป็นกลางต่อ vendor แล้วส่ง trace/metric/log เข้า ClickStack (ClickHouse + HyperDX) ไล่หนึ่งออเดอร์เป็น trace เดียวข้าม RabbitMQ ตั้ง SLO/error budget และขับเคลื่อน incident จริงจาก alert สู่ root cause ด้วย C#/.NET
8 บทเรียน
เริ่มเรียน → - ระดับสูง
เขียน C#/.NET ให้เร็ว: struct, zero allocation, Span<T>
ชั้น PERFORMANCE ของ domain Order — คอร์ส #8–#11 สร้าง domain ขึ้นมา #19 สอนให้มองเห็นมันตอนรัน คอร์สนี้ทำให้มันเร็วขึ้น โดยมีหลักฐาน hot path เดียวทั้งคอร์ส (parse บรรทัดออร์เดอร์ → รวมยอด → พิมพ์ใบเสร็จ) ไล่จากการวัดให้เป็น → struct/class → boxing → Span<T>/stackalloc → ArrayPool → งาน string → ValueTask จนถึง capstone ที่วัดได้ว่าลดจาก 12,272 byte ต่อ batch เหลือ 0 byte โดยผลลัพธ์ยังตรงกันทุก byte (982 byte ทั้ง 7 version) ทุกตัวเลขในคอร์สวัดจริงบนเครื่องเดียว .NET 10.0.10 / linux-x64 และคอร์สนี้ตัดสินด้วย column Allocated ไม่ใช่ column Mean
8 บทเรียน
เริ่มเรียน →
เส้นทาง AI Engineering
8 คอร์สสร้างระบบ AI agent ที่ใช้งานได้จริงบน domain Order ตั้งแต่ agent ตัวแรกจนถึงความปลอดภัย แล้วเปิดฝากระโปรง model ถึงระดับ neural net
- ระดับเริ่มต้น
agent ตัวแรกด้วย C#/.NET — สร้าง AI Agent ตัวแรก
ประตูสำหรับมือใหม่ของสาย AI-engineering — สร้าง AI agent ตัวแรกบน domain Order ฟู้ดเดลิเวอรีที่คุ้นเคย ไล่จากการเรียก Messages API แบบดิบ สู่ Microsoft.Extensions.AI แล้วเติม tool use ปิดท้ายด้วย the loop จนได้ agent Order support ตัวเล็ก ๆ ที่ทำงานได้จริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
วิศวกรรมบริบท — Context Engineering
สร้าง context layer จริงคร่อม agent Order จากคอร์สที่ 15 — retrieval, long-term memory, งบประมาณ token, compaction และการจัดระเบียบผลของ tool แล้ววัดต้นทุน token ของมันด้วย C#/.NET
8 บทเรียน
เริ่มเรียน → - ระดับสูง
เครื่องยนต์ของ agent — Agent Harness & Loop
สร้าง runtime ของ agent คร่อม agent Order จากคอร์สที่ 15/16 ด้วย C#/.NET — the loop และ harness, workflow pattern ตามแนว BEA (chaining, routing, parallelization, orchestrator-workers, evaluator-optimizer), ความทนทาน (retry, timeout, idempotency) ปิดท้ายด้วย autonomous loop ที่มี compound stop policy จริง
8 บทเรียน
เริ่มเรียน → - ระดับสูง
Agent as Bounded Context — ออกแบบระบบ AI Agent ด้วย DDD
คอร์ส AI-engineering คอร์สแรกของแก่น — นำเครื่องมือเชิงกลยุทธ์ของ DDD (bounded context, ubiquitous language, context map, ACL) มาออกแบบระบบ AI agent ให้มีขอบเขตชัดเจน เป็นคำตอบทวนกระแส multi-agent hype ที่มักแตก agent เกินความจำเป็น
8 บทเรียน
เริ่มเรียน → - ระดับสูง
ประเมิน AI Agent ให้เป็น — Evals
คู่หูสาย AI-engineering ของ Testing in Practice — วัดผล ops-copilot ต่อจาก Agent as Bounded Context ให้เป็นวิทยาศาสตร์ ด้วย eval ที่ตัดสินจากผลลัพธ์ไม่ใช่ trajectory, LLM-as-judge ที่ผ่านการ calibrate, error analysis อย่างเป็นระบบ และ eval ทั้ง offline กับ online
8 บทเรียน
เริ่มเรียน → - ระดับสูง
ออกแบบ MCP Server ที่ดี
คอร์สที่สามในสาย AI-engineering ของแก่น ต่อจาก Agent as Bounded Context — ออกแบบ MCP server ที่ดีผ่านมุมมอง tool-as-ACL วาง Primitive ทั้งสาม (Tool, Resource, Prompt) ให้ชัดว่าใครเป็นผู้ควบคุม และปฏิบัติต่อความปลอดภัยเป็นส่วนหนึ่งของสถาปัตยกรรมตั้งแต่ต้น บนมาตรฐานเปิดที่วันนี้เป็นกลางทางค่ายแล้วภายใต้ Linux Foundation
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
ความปลอดภัยของ AI Agent — Guardrails & Security
ป้องกัน agent Order จากคอร์สที่ 15–17 เมื่อมีศัตรูลงมือโจมตีจริง — prompt injection, jailbreak, input/output guardrail, least-privilege + human-approval gate และการรักษาข้อมูลไม่ให้รั่ว ประกอบเป็น defense-in-depth ชั้นเดียวด้วย C#/.NET
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
Deep Learning จากศูนย์ — สร้าง neural net จนถึง transformer ด้วยมือ
เปิดฝากระโปรง model ที่ agent จากคอร์ส #15–#18 เรียกใช้ผ่าน API — สร้าง neural network เองตั้งแต่ neuron ตัวเดียว ไล่ backprop ด้วย micrograd → bigram LM → MLP+embedding → training internals → PyTorch → self-attention/transformer จนประกอบ nano-GPT ตัวจิ๋วที่รันบน CPU ได้ 8 บทเรียนลงมือจริงด้วย Python ตาม Karpathy Zero-to-Hero
8 บทเรียน
เริ่มเรียน →
เส้นทาง Rust / Systems
6 คอร์สเรียนภาษา systems ด้วยการสร้างของจริง จาก CLI จนถึง cluster แบบกระจาย
- ระดับกลาง
Rust จากศูนย์ — เรียนภาษา systems ด้วยการสร้าง CLI จริง
คอร์ส Rust แบบยืนเดี่ยว สำหรับวิศวกรที่มาจากสาย C#/.NET — เรียนภาษา systems ตั้งแต่ศูนย์ด้วยการสร้างเครื่องมือ command-line จริงหนึ่งตัว (taskcli ตัวจัดการงาน to-do ที่เก็บสถานะเป็น JSON) ไล่จาก cargo new จนถึง capstone แบบ multi-threaded ใช้แค่ภาษาแกน + std + library ที่นิ่งจริง จงใจไม่พึ่ง async ecosystem ทั้ง 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับสูง
Rust Systems II — สร้าง Key-Value Store ที่กู้คืนจากการล่มได้ (kaen-kvstore)
คอร์ส Rust แบบยืนเดี่ยว ต่อจาก #21 — สร้าง key-value store บนเครือข่ายที่กู้คืนจาก crash ได้จริง ด้วย Rust std ล้วน (ไม่มี async/tokio ไม่มี serde) ไล่จาก wire protocol ที่เขียนเอง → append-only log + hash index (model Bitcask) → durability ด้วย fsync → compaction → thread pool → ทดสอบ crash-consistency → benchmark ปูทางสู่ Distributed Systems (#23) ทั้ง 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับสูง
Distributed Systems บน kaen-kvstore — Rust Systems III
ต่อจาก #22 — เลื่อน kaen-kvstore จาก1 node เป็น cluster ที่ replicate + quorum-consistent + CRDT ลู่เข้าเอง ด้วย Rust std ล้วน (splitmix64 + std SipHash ไม่มี serde/tokio/proptest) สอนเฉพาะส่วนที่ property-test ได้จริง (vector clock, consistent hashing, quorum, CRDT, Merkle/gossip, harness ฉีด partition) ส่วน consensus/Raft เป็นไดอะแกรม ทุก snippet compile+รันได้จริงในแซนด์บ็อกซ์ เป็นคอร์สที่สามของเสา Systems (#21→#22→#23) ก่อนที่ #24 จะเปิด async ecosystem ต่อ ทั้ง 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับสูง
Async Rust ด้วย Tokio — Rust Systems IV
ต่อจากซีรีส์ Rust (#21–#23) ที่จงใจเลี่ยง async ecosystem — คราวนี้เปิด async จริง: future/await, Tokio runtime, task และ .await, non-blocking I/O และ networking เพื่อเขียน server ที่รองรับคอนเนกชันจำนวนมากพร้อมกันโดยไม่ต้องผูก1 thread ต่อหนึ่งคอนเนกชัน
8 บทเรียน
เริ่มเรียน → - ระดับสูง
สร้างภาษาโปรแกรมของคุณเอง — Rust Systems V
ปิดซีรีส์ Rust ด้วยการสร้าง interpreter ของภาษาเล็กๆ ตั้งแต่ศูนย์ด้วย Rust std ล้วน — scanner/tokenizer → AST → recursive-descent parser → tree-walking interpreter → environment/scope → function และ closure → class และ method จนถึง REPL ที่รัน script ได้จริง อิงโครง Crafting Interpreters แต่เขียนใหม่ให้เป็น Rust แท้ (enum + pattern matching + ownership) ไม่มี external crate
8 บทเรียน
เริ่มเรียน → - ระดับสูง
สร้าง log broker เอง — Rust Systems VI
คอร์สที่หกของเสา Rust และเป็นของที่ #22 สัญญาไว้แล้วยังไม่มีใครสร้าง — kaen-kvstore ปิดท้ายด้วยการวาง segment ไว้เป็นสะพานไปคอร์สถัดไป แต่ #23 รับไม้ไปทางระบบกระจาย คอร์สนี้คือที่ที่ segment มาถึงจริง สร้าง log broker ทรง Kafka ชื่อ kaen-log ตั้งแต่ศูนย์ด้วย Rust std ล้วน ไม่มี external crate สักตัว และจงใจไม่ใช้ Tokio ทั้งที่ #24 สอนไว้ เพราะแก่นของคอร์สอยู่ที่ protocol กับ disk ไม่ใช่ที่ scheduler เดินครบสายตั้งแต่ log ที่เป็นความจริง segment กับสารบัญที่จงใจไม่ครบ ทาง produce กับ acks ทาง fetch ที่จอดคำขอไว้แทนการตอบกลับว่าง consumer group กับ offset ที่ commit แล้ว retention กับ compaction delivery semantics จนถึง replication กับ high watermark ธีสิสของคอร์สคือ log ไม่ใช่คิว มันคือลำดับที่ไม่เปลี่ยน ซึ่งผู้อ่านหลายรายเดินอยู่คนละตำแหน่ง · 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน →
เส้นทางข้อมูลและประสิทธิภาพ
3 คอร์สเปิดฝากระโปรงชั้นเก็บข้อมูล ประกอบชิ้นส่วนของ query engine และ transaction manager ด้วยมือ จนอธิบายได้ว่า planner เลือกแผนด้วยตัวเลขอะไร และ isolation level แลกอะไรกับอะไร
- ระดับกลาง
สร้าง query engine เอง — ทำไม query ช้า และ planner ตัดสินใจด้วยอะไร
คอร์สแรกของเส้นทางข้อมูลและประสิทธิภาพ — ประกอบชิ้นส่วนของ query engine ด้วยมือทีละชั้นด้วย Python stdlib ล้วน: heap file บนหน้า 4096 byte → B+tree → สถิติกับ selectivity → cost model ที่ไม่มีหน่วยเวลา → access path กับ covering index → อัลกอริทึม join จนถึง capstone ที่เอา planner ของตัวเองไปเทียบกับ sqlite3 แล้วอธิบายทุกจุดที่ไม่ตรงให้ได้ คำถามที่คอร์สนี้ตอบคือ ทำไมการเพิ่ม index บางครั้งถึงไม่ช่วย และเอนจินตัดสินใจเรื่องนี้ด้วยตัวเลขอะไร หน่วยของคอร์สคือหน้าที่แตะ ครั้งที่เทียบคีย์ และแถวที่ต้องถือไว้พร้อมกัน ไม่ใช่วินาที ทุกตัวเลขวัดจริงจากสคริปต์ที่ commit ไว้ในรีโป 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับสูง
ทำไมต้องมี isolation level — สร้าง transaction manager เอง
คอร์สที่สองของเส้นทางข้อมูลและประสิทธิภาพ และเป็นชั้นที่คอร์ส query engine ประกาศว่าตัวเองไม่ทำ — สร้าง transaction manager ตั้งแต่ศูนย์ด้วย Rust std ล้วน ไม่มี external crate ตั้งแต่ transaction API → anomaly ที่ทำซ้ำได้ทุกครั้ง → two-phase locking กับ deadlock → MVCC และกฎการมองเห็น → snapshot isolation กับ write skew → SSI → ราคาของ transaction จนถึงการเทียบกับ SQLite และ PostgreSQL ของจริง ธีสิสของคอร์สคือ isolation level ที่สูงขึ้นไม่ได้แปลว่าคำตอบถูกขึ้น มันแปลว่าเอนจินยอมปฏิเสธงานบ่อยขึ้น serializable คืน abort ที่คุณต้อง retry เอง ไม่ได้คืนคำตอบที่ถูก หน่วยของคอร์สคือจำนวน abort จำนวน version ที่ค้าง และจำนวนหน้าที่เขียน ไม่ใช่วินาที 8 บทลงมือจริงด้วย Rust
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
column store จากศูนย์ — เก็บกลับด้านแล้วอ่านมากกว่าเดิม
คอร์สที่สามของเสาข้อมูลและประสิทธิภาพ เก็บตารางออเดอร์ใบเดิมของคอร์สที่ 26 และ 28 กลับด้าน แล้วถามว่าการวางค่าของ column เดียวกันไว้ติดกันซื้ออะไรกลับมาจริง คำตอบที่วัดได้หักล้างสรุปตามตำราตั้งแต่บทแรก คลัง column ที่ยังไม่เข้ารหัสและแตะครบ 6 จาก 6 column แตะ 3320 หน้า ขณะที่ heap ที่มันมาแทนแตะ 3153 หน้า เปิด bit packing แล้วทรงเดียวกันเหลือ 989 หน้า ที่ชนะจึงเป็นการเข้ารหัส ไม่ใช่การวางแนว และกลไกที่ตำรายกให้คลัง column ก็อยู่ในคลังแนวแถวมาก่อนแล้ว heap เก็บ status ทั้ง column ด้วย 200,000 ไบต์ ส่วนคลัง column ที่เก็บสตริงดิบใช้ 1,985,792 ไบต์ ธีสิสของคอร์สคือการแยกการวางแนวออกจากการเข้ารหัส และเมื่อแยกแล้วเลือกการเข้ารหัสที่เล็กที่สุดต่อ column ทั้งคลังเหลือ 1,238,089 ไบต์ เทียบ heap ที่ 12,914,688 ไบต์ ไล่จาก dictionary ที่ทำให้สาม column ใหญ่ขึ้น การนับบิตแทนการนับ byte สองการเข้ารหัสที่ถูกเรียกว่า delta เหมือนกัน zone map ที่อ่านลำดับการเขียนไม่ใช่จำนวนค่าที่ไม่ซ้ำ การเรียงที่จ่ายราคาที่ column อื่น จนถึงการประกอบแถวกลับคืนซึ่ง heap แตะ 1 หน้าและคลัง column แตะ 42 หน้า ทุกข้ออ้างมาจาก probe ที่คุณรันเองได้โดยไม่ต้องมี Docker และไม่ต้องต่อเน็ต วัดด้วยหน้าที่แตะ ไบต์ row group ที่ข้าม ค่าที่ถอดรหัส บิตต่อค่า จำนวน run และจำนวนค่าที่ไม่ซ้ำ ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน →
เส้นทางภาษา backend
7 คอร์สเรียนภาษาที่ทีม backend ใช้จริง ด้วยการสร้างของชิ้นเดียวกันซ้ำในแต่ละภาษา — เครื่องมือประมวลผลออเดอร์ตัวเดิม เขียนใหม่ทีละภาษา เพื่อให้เห็นว่าแต่ละภาษาตัดสินใจต่างกันตรงไหน และแลกอะไรไปกับการตัดสินใจนั้น แล้วปิดท้ายด้วยคอร์สที่ไม่ได้เริ่มจากศูนย์ แต่วัดว่ารุ่นใหม่ของภาษาให้อะไรกลับมาที่ตรวจสอบได้
- ระดับเริ่มต้น
Go จากศูนย์ — เรียนภาษาที่ตัดสินใจน้อยกว่า ด้วยการสร้าง CLI จริง
คอร์สแรกของสายภาษา backend สำหรับวิศวกรที่เขียนอีกภาษาเป็นอยู่แล้ว — เรียน Go ด้วยการสร้างเครื่องมือ command-line จริงหนึ่งตัว (orderctl ตัวประมวลผลออเดอร์อาหารที่อ่านจาก JSON) ไล่จาก go mod init จนถึง capstone ที่เปิด HTTP และทำงานพร้อมกันได้ ใช้ standard library ล้วน ไม่มี framework ไม่มี ORM ทุกบทเทียบให้เห็นว่า Go ตัดสินใจต่างจากภาษาที่คุณเขียนอยู่ตรงไหน และแลกอะไรไป 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับเริ่มต้น
Java จากศูนย์ — เรียนภาษาที่พูดชื่อออกมาทุกครั้ง ด้วยการสร้าง CLI จริง
คอร์สที่สองของสายภาษา backend ต่อจาก Go จากศูนย์ — สร้างเครื่องมือ command-line ตัวเดิม (orderctl ตัวประมวลผลออเดอร์อาหารที่อ่านจาก JSON) ใหม่ทั้งตัวด้วย Java 25 เพื่อให้เทียบได้ตรงๆ ว่าสองภาษาตัดสินใจต่างกันตรงไหน ไล่จาก java Main.java ที่ยังไม่มี build tool ผ่าน record, Maven, Jackson, interface ที่ต้องเอ่ยชื่อ, checked exception จนถึง capstone ที่เปิด HTTP ด้วย JDK เปล่า และใช้ virtual thread ทุกบทบอกราคาของทั้งสองฝั่ง ไม่เชียร์ฝั่งใดฝั่งหนึ่ง 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับเริ่มต้น
Kotlin จากศูนย์ — ภาษาที่ยืมกล่องเครื่องมือของ Java มาทั้งใบ แล้วเปลี่ยนสามอย่าง
คอร์สปิดท้ายของสายภาษา backend ต่อจาก Go จากศูนย์ และ Java จากศูนย์ — สร้าง orderctl ตัวประมวลผลออเดอร์อาหารตัวเดิมใหม่ทั้งตัวด้วย Kotlin 2.4.10 เพื่อให้อ่านบทเลขเดียวกันสามภาษาแล้วเทียบได้ตรงๆ ไล่จากโปรแกรมแรกที่ยังไม่มี build tool ผ่าน data class, Gradle, kotlinx.serialization ที่โยนทั้งคีย์เกินและคีย์ขาด, delegation ด้วย by, ภาษาที่ไม่มี checked exception เลย จนถึง capstone ที่เปิด HTTP ด้วย JDK เปล่าและใช้ virtual thread ทุกบทบอกราคาของทั้งสามฝั่ง ไม่เชียร์ฝั่งใดฝั่งหนึ่ง 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
Java 8 ถึง 25 — เขียนใหม่ทีละอย่าง แล้ววัดว่าได้อะไรกลับมา
คอร์สสำหรับคนที่เขียน Java เป็นมานานแต่ความรู้หยุดอยู่ที่ 8 หรือ 11 — ไม่ใช่รายการ feature ใหม่ซึ่งหาได้ทุกที่ แต่เป็นคำตอบของสองคำถามที่หาไม่ได้ คือของพวกนี้ใช้ได้จริงแล้วหรือยังในรุ่นที่คุณรันอยู่ และเปลี่ยนไปใช้แล้วได้อะไรกลับมาที่ตรวจสอบได้ ทุกข้ออ้างวัดด้วย javac javap และ java ตัวจริงบน JDK 25 ไล่จากบทวัดสถานะที่บอกว่าอะไร final อะไรยัง preview ผ่าน record ที่เป็นสัญญาไม่ใช่การพิมพ์น้อยลง switch ที่พลาดไม่ได้กับช่องเดียวที่มันยังพลาดได้ Gatherers ที่ขี้เกียจจริง virtual thread ที่นับเป็นจำนวนไม่ใช่เป็นเวลา จนถึงราคาของ preview ที่ตรึง class file ไว้กับรุ่น ไม่มี build tool และไม่มีหน่วยเวลาสักตัวทั้งคอร์ส 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
สัญญาบนสาย — protobuf ที่สองฝั่งไม่เคย deploy พร้อมกัน
คอร์สแรกของไซต์ที่เป็นเจ้าของเรื่อง protobuf ทั้งเรื่อง สัญญาบนสายผูกสองฝั่งที่ไม่เคย deploy พร้อมกัน ฝั่งเขียนคือ Go ฝั่งอ่านคือ Java แก้ schema หนึ่งอย่าง แล้วฝั่งที่ไม่ได้แก้ อ่านได้ อ่านผิด หรืออ่านไม่ออก คำตอบที่วัดได้คือเกือบทุกการแก้ยัง parse ผ่าน และนั่นคือปัญหา เพราะสิ่งที่ทำให้สัญญาพังไม่ใช่ error แต่เป็นค่าที่เดินไปนั่งผิดช่อง โดยไม่มีอะไรแดง ไล่จาก byte ชุดแรกที่บอกว่าชื่อ field ไม่ได้เดินทางไปกับมัน ผ่าน field ที่ไม่เคยขึ้นสาย ของที่ผู้อ่านรุ่นเก่าถือติดมือไป หมายเลขที่เอากลับมาใช้ซ้ำแล้วค่าเดิมไปโผล่ในช่องใหม่ ชนิดที่หน้าตาเหมือนเดิมแต่คืนค่าคนละตัว ชื่อเดียวที่กลายเป็นหลายชื่อแล้วชื่อแรกหายจริง จนถึงสองภาษาที่อ่าน byte ชุดเดียวกัน แล้วตอบต่างกันหนึ่งช่อง และสัญญาที่ตรวจได้ก่อนของจะขึ้น ทุกข้ออ้างมาจาก probe ที่รันได้ วัดด้วยจำนวน byte ค่า hex หมายเลข field จำนวน unknown field และรหัสออก ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
จำนวนเต็มที่เชื่อไม่ได้ — ค่าเปลี่ยนห้าจุด และไม่มีจุดไหนแดง
คอร์สที่หกของเสาภาษา backend และเป็นคอร์สแรกที่ไม่ได้สร้างเครื่องมือประมวลผลออเดอร์ขึ้นใหม่ มันหยิบค่าเดิมสองค่าคือยอดออเดอร์กับ order id แล้วเดินตามมันข้ามห้าภาษา เพื่อถามว่าปลายทางยังเป็นค่าเดิมอยู่หรือเปล่า สิ่งที่หักล้างสัญชาตญาณไม่ใช่ภาษาต่างกันตอบต่างกัน แต่เป็นภาษาเดียว source เดียว ตอบไม่เหมือนกันเอง rustc a.rs ให้รหัสออก 101 กับ panic ส่วน rustc -O a.rs ให้รหัสออก 0 กับ 4294967281 และสวิตช์ที่ตัดสินคือ -C overflow-checks ไม่ใช่ debug ไม่ใช่ release ธีสิสของคอร์สคือค่าจำนวนเต็มค่าหนึ่งเปลี่ยนได้ห้าจุดระหว่างทาง และทั้งห้าจุดเงียบ ตอนความกว้างหมด ตอนหาค่าสัมบูรณ์ ตอนหาร ตอนทศนิยมกลืนมัน และตอนมันข้ามพรมแดนของการ parse เข้ามา Math.abs ของ Integer.MIN_VALUE คืน -2147483648 โดยไม่มี exception ส่วน absExact โยน ArithmeticException ออกมา -7/2 ได้ -3 ที่สามภาษา -3.5 ที่ Node และ -4 ที่ Python Node ตอบว่า 2**53 เท่ากับ 2**53+1 เป็น true JSON ก้อนเดียวเข้าโปรแกรม Go ตัวเดียวได้สามคำตอบ คือ 9.007199254740992e+15 ที่ map และ 9007199254740993 พร้อม error ที่ struct และครบทั้งสองช่องเมื่อใช้ UseNumber ส่วน strconv.Atoi ที่เกินช่วงคืน 9223372036854775807 พร้อม error ไม่ใช่ 0 ทุกข้ออ้างมาจาก probe ที่คุณรันเองได้ ไม่ต้องมี Docker และไม่ต้องต่อเน็ต วัดด้วยค่าจำนวนเต็ม รหัสออก ชื่อ class ของ exception และ diagnostic ของ compiler ไม่มีหน่วยเวลาสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน → - ระดับกลาง
เวลาที่เชื่อไม่ได้ — ครึ่งชั่วโมงที่ไม่มีอยู่จริง และภาษาเดียวที่บอกว่ามันไม่มี
คอร์สที่เจ็ดของเสาภาษา backend และเป็นคอร์สที่สองที่ไม่ได้สร้างอะไรขึ้นใหม่ มันหยิบเวลาของออเดอร์ คือเวลาสั่ง เวลาตัดรอบ วันส่ง และ slot ส่งของ แล้วเดินตามมันข้ามห้าภาษา เพื่อถามว่าปลายทางยังชี้จุดเดิมบนเส้นเวลาอยู่หรือเปล่า ประเทศไทยไม่มี DST คนไทยจึงมักคิดว่าเรื่องนี้ไม่ใช่ปัญหาของตัวเอง แต่ระบบไทยคุยกับระบบในโซนที่มี DST และ Bangkok ในปี 1900 ใช้ +06:42:04 ไม่ใช่ +07:00 ธีสิสของคอร์สคือเวลาท้องถิ่นค่าหนึ่งไม่ได้ระบุจุดเดียวบนเส้นเวลา มันอาจไม่มีจุดเลย มีสองจุด หรือมีจุดเดียวที่แต่ละภาษาเลือกต่างกัน และในทรงที่ probe วัด มีภาษาเดียวจากห้าภาษาที่บอกคุณ 02:30 ของวันที่ 2026-03-08 ที่ America/New_York ไม่มีอยู่จริง Java ให้ 2026-03-08T03:30-04:00[America/New_York] Python เก็บ 2026-03-08T02:30:00-05:00 ไว้ตามเดิม และ Rust เป็นภาษาเดียวที่ตอบว่า LocalResult::None บวก 24 ชั่วโมงจาก 2026-03-07 12:00 ที่โซนเดียวกันได้ 13:00-04:00 ที่ Java ส่วน timedelta(hours=24) ของ Python ได้ 12:00-04:00 เท่ากับการบวกหนึ่งวัน วันส่งของ 2026-10-02 ของร้านใน Bangkok คือ 2026-10-01T17:00:00Z ใน UTC ซึ่งเป็นคนละวัน json.dumps ของ Python โยน TypeError ใส่เวลาของออเดอร์ และ time.RFC3339 ของ Go กับ to_rfc3339() ของ chrono พิมพ์ offset ของ Bangkok ปี 1900 เป็น +06:42 ทุกข้ออ้างมาจาก probe ที่คุณรันเองได้ ไม่ต้องมี Docker วัดด้วยสตริงของเวลา offset ที่อ่านจาก API ชื่อ class ของ exception และ variant ของ enum ไม่มีหน่วยเวลาที่วัดจากการรันสักตัว 8 บทลงมือจริง
8 บทเรียน
เริ่มเรียน →
บทความ
ดูบทความทั้งหมด (44) →- บทความ
Effective Context Engineering
ขยับจาก prompt engineering สู่การคัดบริบทที่ให้สัญญาณสูงสุดให้ AI agent
Anthropic
อ่าน → - บทความ
The Clean Architecture
วงกลมซ้อนภายใต้ Dependency Rule — dependency ของ code ชี้เข้าด้านในเสมอ
Robert C. Martin
อ่าน → - บทความ
Structured-Prompt-Driven Development
ปฏิบัติต่อ prompt เป็นชิ้นงานชั้นหนึ่งที่เก็บคู่กับ code ใน version control
Thoughtworks
อ่าน →
อ้างอิง
- Reference ใหม่
หลักการออกแบบซอฟต์แวร์ · DevIQ
208 หัวข้อจาก DevIQ แปลไทยครบ — หลักการ แนวปฏิบัติ ดีไซน์แพตเทิร์น แอนติแพตเทิร์น code กลิ่นแรง กฎ และ DDD โยงถึงกันทั้งเล่ม พร้อมตัวอย่าง code และอภิธานศัพท์
208 หัวข้อ · 7 หมวด
เปิดอ่าน →