<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gophernment</title>
    <description>The latest articles on DEV Community by Gophernment (@gophernment).</description>
    <link>https://dev.to/gophernment</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4007831%2Fe66b819e-eb2d-4214-91f0-fa9c1204b592.png</url>
      <title>DEV Community: Gophernment</title>
      <link>https://dev.to/gophernment</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gophernment"/>
    <language>en</language>
    <item>
      <title>pkg.go.dev — มากกว่าแค่ที่อ่านเอกสาร Go</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Sun, 19 Jul 2026 02:30:26 +0000</pubDate>
      <link>https://dev.to/gophernment/pkggodev-maakkwaaaekhthiiaaneksaar-go-2ogm</link>
      <guid>https://dev.to/gophernment/pkggodev-maakkwaaaekhthiiaaneksaar-go-2ogm</guid>
      <description>&lt;h2&gt;
  
  
  pkg.go.dev — มากกว่าแค่ที่อ่านเอกสาร Go
&lt;/h2&gt;

&lt;p&gt;ถ้าคุณเขียน Go — คุณน่าจะเคยเปิด pkg.go.dev เพื่ออ่านเอกสาร package สักตัว&lt;/p&gt;

&lt;p&gt;แต่ pkg.go.dev ไม่ใช่แค่ reader — มันคือ enforcement mechanism สำหรับ best practices ของระบบนิเวศ Go ทั้งหมด&lt;/p&gt;

&lt;p&gt;ทุกครั้งที่มีคนเปิดหน้า package ของคุณ — pkg.go.dev จะแสดงผลการตรวจสอบ 4 อย่าง: มี go.mod ไหม, license เป็นอะไร, มี tagged version หรือเปล่า, version เสถียรหรือยัง&lt;/p&gt;

&lt;p&gt;ถ้าคุณเขียน library แล้ว pkg.go.dev ไม่ขึ้นเครื่องหมายถูก — มันคือป้ายไฟนีออนที่บอกทุกคนว่า "package นี้ยังไม่พร้อมสำหรับ production"&lt;/p&gt;




&lt;h2&gt;
  
  
  4 เครื่องหมายที่ pkg.go.dev เช็ค
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Has go.mod file
&lt;/h3&gt;

&lt;p&gt;go.mod คือมาตรฐาน dependency management ของ Go — ตั้งแต่ Go 1.11 — และ pkg.go.dev ถือว่าการมี go.mod คือ baseline&lt;/p&gt;

&lt;p&gt;ไม่มี go.mod → ไม่ใช่ Go module → ไม่มี versioning จริง → pkg.go.dev จะแสดง package แบบไม่มีข้อมูลเวอร์ชัน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ควรทำ:&lt;/strong&gt; ทุก repo ควรมี &lt;code&gt;go.mod&lt;/code&gt; — แม้จะเป็น package เล็ก ๆ ก็ตาม&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Redistributable license
&lt;/h3&gt;

&lt;p&gt;pkg.go.dev ตรวจสอบ license ของ package — และจะแสดงเฉพาะ license ที่ redistributable (MIT, BSD, Apache 2.0, etc.)&lt;/p&gt;

&lt;p&gt;license ที่ restrictive เกินไป หรือไม่พบ license → pkg.go.dev อาจไม่แสดง documentation บางส่วน หรือขึ้นคำเตือน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ควรทำ:&lt;/strong&gt; ใส่ &lt;code&gt;LICENSE&lt;/code&gt; file ใน repo — MIT หรือ BSD หรือ Apache 2.0&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Tagged version
&lt;/h3&gt;

&lt;p&gt;Go modules ใช้ Git tags เป็นเวอร์ชัน — &lt;code&gt;v1.0.0&lt;/code&gt;, &lt;code&gt;v1.2.3&lt;/code&gt; — และ &lt;code&gt;go get&lt;/code&gt; ให้ความสำคัญกับ tagged versions ก่อน commit hash&lt;/p&gt;

&lt;p&gt;ไม่มี tag → &lt;code&gt;go get&lt;/code&gt; ต้องใช้ pseudo-version (&lt;code&gt;v0.0.0-20250101000000-abc123&lt;/code&gt;) — ซึ่งไม่ stable และ importers ไม่มั่นใจ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ควรทำ:&lt;/strong&gt; tag ทุก release ด้วย semver — &lt;code&gt;git tag v1.0.0 &amp;amp;&amp;amp; git push origin v1.0.0&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Stable version
&lt;/h3&gt;

&lt;p&gt;Projects ที่ &lt;code&gt;v0.x.x&lt;/code&gt; ถูกมองว่า experimental — breaking changes เกิดได้ตลอดเวลา — ไม่มีสัญญาว่าจะ backward compatible&lt;/p&gt;

&lt;p&gt;เมื่อถึง &lt;code&gt;v1.0.0&lt;/code&gt; — คือสัญญาว่า public API จะไม่เปลี่ยนแบบ breaking ภายใน major version เดียวกัน&lt;/p&gt;

&lt;p&gt;pkg.go.dev แสดงสถานะนี้ให้ทุกคนเห็น — "stable" หรือ "experimental" — และมันมีผลต่อการตัดสินใจของคนที่จะ import package ของคุณ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ควรทำ:&lt;/strong&gt; เมื่อ API นิ่ง — release &lt;code&gt;v1.0.0&lt;/code&gt; — และถ้าต้อง breaking change — ขึ้น major version ใหม่ (&lt;code&gt;v2.0.0&lt;/code&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  มากกว่า 4 เครื่องหมาย — สิ่งที่ pkg.go.dev ทำเบื้องหลัง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Documentation จาก source โดยตรง
&lt;/h3&gt;

&lt;p&gt;pkg.go.dev ดึง source code จาก Go Module Mirror (&lt;code&gt;proxy.golang.org&lt;/code&gt;) — และ generate documentation จาก source โดยตรง — ไม่ต้องอัปโหลดอะไรเอง&lt;/p&gt;

&lt;p&gt;แค่ push code + tag → documentation ปรากฏบน pkg.go.dev ภายในไม่กี่นาที&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Context
&lt;/h3&gt;

&lt;p&gt;package บางตัวทำงานต่างกันบน OS/architecture ต่างกัน — pkg.go.dev แสดง build context ให้เห็น — เช่น &lt;code&gt;linux/amd64&lt;/code&gt;, &lt;code&gt;js/wasm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;และมี dropdown ให้สลับดู document สำหรับ build context อื่น&lt;/p&gt;

&lt;h3&gt;
  
  
  การเพิ่ม package ใหม่
&lt;/h3&gt;

&lt;p&gt;ถ้า package ยังไม่ปรากฏบน pkg.go.dev — แค่เข้า URL นั้นตรง ๆ แล้วกด "Request":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://pkg.go.dev/example.com/my/module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือ &lt;code&gt;go get&lt;/code&gt; package นั้นผ่าน proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GOPROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://proxy.golang.org go get example.com/my/module@v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retract เวอร์ชันที่มีปัญหา
&lt;/h3&gt;

&lt;p&gt;ถ้า release เวอร์ชันที่มีบั๊กร้ายแรง — แก้ด้วย &lt;code&gt;retract&lt;/code&gt; directive ใน &lt;code&gt;go.mod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;retract&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.2.3&lt;/span&gt; &lt;span class="c"&gt;// security vulnerability&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;go get&lt;/code&gt; และ pkg.go.dev จะไม่แนะนำเวอร์ชันนั้นอีก — แต่ยังดาวน์โหลดได้ (retract ≠ delete)&lt;/p&gt;




&lt;h2&gt;
  
  
  เขียน package comment ให้ดี — มันไปโผล่บน go.dev
&lt;/h2&gt;

&lt;p&gt;ประโยคแรกของ package comment คือสิ่งที่ go.dev ใช้เป็น search result snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Package httputil provides HTTP utility functions, complementing the&lt;/span&gt;
&lt;span class="c"&gt;// more common ones in the net/http package.&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;httputil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ประโยคนี้คือสิ่งที่จะปรากฏเวลามีคน search บน &lt;a href="https://go.dev" rel="noopener noreferrer"&gt;go.dev&lt;/a&gt; — และทุกคนที่เปิดหน้า pkg.go.dev ของคุณ — จะเห็นประโยคนี้เป็นอันดับแรก&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ควรทำ:&lt;/strong&gt; ประโยคแรกของ package comment ควรเป็น summary ที่สมบูรณ์ — ไม่ใช่แค่ชื่อ package&lt;/p&gt;




&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;pkg.go.dev คือ public scoreboard ของทุก Go package — และ 4 เครื่องหมายที่มันเช็คคือสิ่งที่ทุก package ควรมี&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;✅ go.mod
✅ Redistributable license (MIT/BSD/Apache)
✅ Tagged version (semver)
✅ Stable version (v1.0.0+)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ไม่มีอะไรซับซ้อน — แต่เป็นสัญญาณว่า "package นี้พร้อมให้คนอื่นใช้"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pkg.go.dev/about" rel="noopener noreferrer"&gt;pkg.go.dev/about&lt;/a&gt; — official documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://go.dev/ref/mod" rel="noopener noreferrer"&gt;Go Modules Reference&lt;/a&gt; — versioning, retract, compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>tools</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Ken Thompson — คนที่เขียนระบบปฏิบัติการใน 3 สัปดาห์</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Fri, 17 Jul 2026 15:19:01 +0000</pubDate>
      <link>https://dev.to/gophernment/ken-thompson-khnthiiekhiiynrabbptibatikaarain-3-sapdaah-3hoi</link>
      <guid>https://dev.to/gophernment/ken-thompson-khnthiiekhiiynrabbptibatikaarain-3-sapdaah-3hoi</guid>
      <description>&lt;h2&gt;
  
  
  Ken Thompson — คนที่เขียนระบบปฏิบัติการใน 3 สัปดาห์
&lt;/h2&gt;

&lt;p&gt;ปี 1969 เคน ทอมป์สัน อายุ 26 เป็นวิศวกรที่ Bell Labs&lt;/p&gt;

&lt;p&gt;เขากับทีมเพิ่งเสียโปรเจกต์ Multics ซึ่งเป็นระบบปฏิบัติการที่ซับซ้อนเกินไปจนถูกยกเลิก Bell Labs ถอนตัว ทีมแตก โปรเจกต์ตาย&lt;/p&gt;

&lt;p&gt;เคนมีเวลาเหลือเฟือ และมี PDP-7 ซึ่งเป็นคอมพิวเตอร์เก่าที่แทบไม่มีใครใช้&lt;/p&gt;

&lt;p&gt;ใน 3 สัปดาห์ เขาเขียน Unix kernel, shell, editor, และ assembler ขึ้นมาบน PDP-7 — ทั้งหมดเป็น assembly — ภาษา B ยังไม่เกิดตอนนั้น&lt;/p&gt;

&lt;p&gt;B ถูกสร้างขึ้นหลังจากนั้น — เพื่อใช้เขียน utility ต่าง ๆ แทน assembly — และนี่คือจุดเริ่มต้นของสายภาษา B → C → Go&lt;/p&gt;




&lt;h2&gt;
  
  
  ภาษา B — เกิดหลัง Unix เวอร์ชันแรก
&lt;/h2&gt;

&lt;p&gt;Unix เวอร์ชันแรกสุด — สิงหาคม 1969 — เป็น assembly ล้วน&lt;/p&gt;

&lt;p&gt;หลังจากนั้นไม่นาน Ken ก็เริ่มสร้าง B — โดยตัดทอนมาจาก BCPL — เพื่อให้มีภาษาระดับสูงไว้เขียน tools โดยไม่ต้องใช้ assembly ทั้งหมด&lt;/p&gt;

&lt;p&gt;B มาจาก BCPL (Basic Combined Programming Language) ซึ่งพัฒนาโดย Martin Richards ที่ Cambridge ในปี 1966 BCPL เป็นภาษาที่ไม่มี type — ทุกอย่างคือ word — และออกแบบมาให้ compiler พกพาง่าย&lt;/p&gt;

&lt;p&gt;Ken เอา BCPL มาตัดทุกอย่างที่ไม่จำเป็นออก — จนเหลือภาษาเล็กมากที่ทำงานได้บน PDP-7 ซึ่งมี RAM แค่ 4K words&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PDP-7 Assembly → Unix v1 (1969, 3 สัปดาห์)
                    ↓
BCPL (Richards, 1966)
    ↓ ตัด feature, ลดขนาด
B (Thompson, 1969-1970)
    ↓ ใช้ rewrite Unix utilities (ไม่ใช่ kernel)
    ↓
    ↓ เพิ่ม type system, struct, portability
C (Ritchie, 1972)
    ↓ Unix v4 rewrite ด้วย C (1973)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B ไม่มี type system ไม่มีโครงสร้างข้อมูล มีแค่ word — เหมือนกับว่าเป็น BCPL เวอร์ชัน minimal&lt;/p&gt;

&lt;p&gt;B ถูกใช้เขียน shell, utilities, และ tools ต่าง ๆ ของ Unix — แต่ &lt;strong&gt;kernel ยังเป็น assembly&lt;/strong&gt; อยู่ จนกระทั่ง Dennis Ritchie สร้าง C ขึ้นมาในปี 1972&lt;/p&gt;




&lt;h2&gt;
  
  
  ทำไมต้อง B
&lt;/h2&gt;

&lt;p&gt;PDP-7 มี assembly แต่นั่นไม่ใช่เหตุผลที่ดีพอที่จะใช้มัน&lt;/p&gt;

&lt;p&gt;Ken ไม่เชื่อในการเขียน OS ด้วย assembly ทั้งระบบ — assembly เร็วแต่เขียนช้า แก้ยาก พกพาไม่ได้&lt;/p&gt;

&lt;p&gt;การใช้ภาษาระดับสูง (แม้จะสูงนิดเดียวแบบ B) ทำให้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เขียน shell, utilities เร็วขึ้นมาก&lt;/li&gt;
&lt;li&gt;แก้ไขง่าย — ไม่ต้องเขียนใหม่เวลาย้ายเครื่อง&lt;/li&gt;
&lt;li&gt;ใช้คนน้อยลง — Unix version แรกเขียนโดย 2 คน&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  จาก B → C → Unix → ทุกอย่าง
&lt;/h2&gt;

&lt;p&gt;เมื่อทีมได้ PDP-11 ซึ่งเป็นเครื่องที่ใหญ่กว่า — B เริ่มมีปัญหา PDP-11 มี byte-addressing แต่ B ออกแบบมาเพื่อ word-addressing — ทำให้ใช้ความสามารถของเครื่องใหม่ได้ไม่เต็มที่&lt;/p&gt;

&lt;p&gt;Dennis Ritchie จึงเริ่มเพิ่ม type system ลงใน B — &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, &lt;code&gt;struct&lt;/code&gt;, pointer — และ C ก็เกิดในปี 1972&lt;/p&gt;

&lt;p&gt;Unix ถูก rewrite ด้วย C — และกลายเป็นระบบปฏิบัติการที่พกพาได้ — ครั้งแรกที่ OS ถูกเขียนด้วยภาษาระดับสูงที่ไม่ใช่ assembly&lt;/p&gt;

&lt;p&gt;จาก Unix → BSD → Linux → macOS → iOS → Android — ทุกสิ่งที่เราใช้อยู่ทุกวันนี้ เริ่มจาก Ken Thompson — คนที่เขียน OS ใน 3 สัปดาห์เพราะเขาเบื่อ&lt;/p&gt;




&lt;h2&gt;
  
  
  สิ่งที่ Ken ทำหลังจากนั้น
&lt;/h2&gt;

&lt;p&gt;UTF-8 — Ken Thompson และ Rob Pike ร่างมันระหว่างกินข้าวเย็นที่นิวเจอร์ซีย์ บนกระดาษรองจาน&lt;/p&gt;

&lt;p&gt;Go — Ken Thompson, Rob Pike, Robert Griesemer สร้างขึ้นในปี 2007 ภาษาของ cloud-native era&lt;/p&gt;

&lt;p&gt;Turing Award 1983 — ร่วมกับ Dennis Ritchie สำหรับการสร้าง Unix และ National Medal of Technology 1998 จากประธานาธิบดี Clinton&lt;/p&gt;




&lt;h2&gt;
  
  
  นิสัยการทำงานของ Ken
&lt;/h2&gt;

&lt;p&gt;Ken เป็นคนที่ไม่ชอบประชุม ไม่เขียนเอกสาร ไม่ทำแผน — เขาแค่เขียนโค้ด&lt;/p&gt;

&lt;p&gt;เวลามีคนถามว่าทำไม Unix ถึงดี — เขาตอบว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I can't tell you why Unix is good without sounding like I'm bragging."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rob Pike เคยเล่าว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Ken สามารถถือโค้ดของระบบปฏิบัติการทั้งระบบไว้ในหัวได้ — และรู้ว่าบรรทัดไหนทำอะไร — โดยไม่ต้องเปิดไฟล์"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ทำไมเรื่องนี้ถึงสำคัญ
&lt;/h2&gt;

&lt;p&gt;ปี 1969 — มนุษย์ไปดวงจันทร์ครั้งแรก — ปีเดียวกับที่ Ken Thompson เขียน OS ใน 3 สัปดาห์&lt;/p&gt;

&lt;p&gt;56 ปีผ่านไป — เรายังใช้สิ่งที่เขาสร้าง&lt;/p&gt;

&lt;p&gt;Shell ที่คุณเปิด terminal อยู่ตอนนี้ — ใช้ &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt; — ทั้งหมดมาจาก Unix&lt;/p&gt;

&lt;p&gt;ภาษา Go ที่เราเขียน — ออกแบบโดย Ken Thompson ในวัย 64&lt;/p&gt;

&lt;p&gt;UTF-8 ที่เว็บทั้งโลกใช้ — ออกแบบโดย Ken Thompson และ Rob Pike บนกระดาษรองจาน&lt;/p&gt;




&lt;p&gt;นี่คือ Ken Thompson — คนที่เบื่อ Multics — แล้วเขียน Unix ขึ้นมาแทน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.bell-labs.com/usr/dmr/www/hist.html" rel="noopener noreferrer"&gt;The Evolution of the Unix Time-sharing System&lt;/a&gt; — Dennis Ritchie, 1979&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dl.acm.org/doi/10.1145/358210.358217" rel="noopener noreferrer"&gt;Ken Thompson — Turing Award Lecture&lt;/a&gt; — 1984&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt" rel="noopener noreferrer"&gt;UTF-8 history&lt;/a&gt; — Rob Pike, 2003&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>history</category>
      <category>programming</category>
      <category>unix</category>
      <category>go</category>
    </item>
    <item>
      <title>WSO2 ประกาศเดินหน้า Go แทน Java — วาง roadmap 10-20 ปี</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Fri, 17 Jul 2026 10:33:39 +0000</pubDate>
      <link>https://dev.to/gophernment/wso2-prakaasedinhnaa-go-aethn-java-waang-roadmap-10-20-pii-3mo3</link>
      <guid>https://dev.to/gophernment/wso2-prakaasedinhnaa-go-aethn-java-waang-roadmap-10-20-pii-3mo3</guid>
      <description>&lt;h2&gt;
  
  
  WSO2 ประกาศเดินหน้า Go แทน Java
&lt;/h2&gt;

&lt;p&gt;WSO2 — บริษัท enterprise middleware อายุ 20 ปีจากศรีลังกา — ประกาศทิศทางใหม่&lt;/p&gt;

&lt;p&gt;ผู้บริหาร Sameera Jayasoma (VP) และ Sanjiva Weerawarana (CEO) เขียนโพสต์ชื่อ &lt;a href="https://wso2.com/library/blogs/goodbye-java-hello-go/" rel="noopener noreferrer"&gt;"Goodbye Java, Hello Go!"&lt;/a&gt; เมื่อ 26 ม.ค. 2026&lt;/p&gt;

&lt;p&gt;นี่ไม่ใช่ startup ที่เปลี่ยนภาษาเล่น ๆ WSO2 คือผู้ผลิต API Manager, Identity Server, Micro Integrator — โค้ดฝั่งเซิร์ฟเวอร์ส่วนใหญ่เป็น Java มาตลอด 20 ปี&lt;/p&gt;

&lt;p&gt;แต่ตอนนี้พวกเขาบอกว่า — ถ้าจะมองไปอีก 10-20 ปีข้างหน้า — Java ไม่ใช่คำตอบเดียวอีกต่อไป&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📅 อ่านเมื่อ: กรกฎาคม 2026 — WSO2 ยังอยู่ในช่วงเริ่มต้น migration — โค้ดส่วนใหญ่ยังเป็น Java — บทความนี้เป็นการรายงานทิศทางเชิงกลยุทธ์ ไม่ใช่ผลลัพธ์ที่เกิดแล้ว&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ไม่ใช่แค่ลอง
&lt;/h2&gt;

&lt;p&gt;WSO2 ไม่ได้เพิ่งเริ่มใช้ Go พวกเขาใช้ Go ใน production มาแล้วหลายระบบ:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;โปรเจกต์&lt;/th&gt;
&lt;th&gt;คืออะไร&lt;/th&gt;
&lt;th&gt;ภาษา&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenChoreo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Developer Platform — CNCF Sandbox project&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thunder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloud-native Identity Platform ตัวใหม่&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ballerina Compiler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compiler ของภาษา Ballerina ที่ WSO2 สร้างเอง — rewrite จาก Java&lt;/td&gt;
&lt;td&gt;Go (ใหม่)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platformer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloud platform ที่ WSO2 เข้าซื้อ&lt;/td&gt;
&lt;td&gt;Go + Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WUM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;WSO2 Update Manager (CLI)&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Jayasoma เรียก OpenChoreo ว่า "most ambitious project" ของ WSO2 — และมันเขียนด้วย Go ทั้งหมด&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;การที่ทีม rewrite Ballerina compiler — ภาษาที่ตัวเองสร้าง — จาก Java ไป Go — นี่คือสัญญาณที่ชัดที่สุด&lt;/p&gt;




&lt;h2&gt;
  
  
  3 เหตุผลเบื้องหลัง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Java เป็น default ของปี 2005
&lt;/h3&gt;

&lt;p&gt;WSO2 ก่อตั้งปี 2005 — ตอนนั้นไม่มีคำถามเลยว่าจะใช้ภาษาอะไรเขียน server-side enterprise infrastructure — Java เท่านั้น&lt;/p&gt;

&lt;p&gt;20 ปีผ่านไป โลกเปลี่ยน Container, microservice, cloud-native กลายเป็นมาตรฐาน และ Go ถูกออกแบบมาเพื่อโลกนี้ — ไม่ใช่ปี 2005 อีกต่อไป&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Go ถูกสร้างมาเพื่อ infrastructure
&lt;/h3&gt;

&lt;p&gt;สิ่งที่ WSO2 ทำ — API Gateway, Identity Server, Integrator — เป็น network services ทั้งหมด&lt;/p&gt;

&lt;p&gt;Go มีสิ่งที่ Java framework พยายามสร้างแต่ Go มีตั้งแต่เกิด: fast startup, small footprint, simple concurrency, static binary — ไม่ใช่ afterthought&lt;/p&gt;

&lt;h3&gt;
  
  
  3. WSO2 ใช้ Go จริง
&lt;/h3&gt;

&lt;p&gt;OpenChoreo และ Thunder คือโปรเจกต์ใหม่ทั้งหมดที่เลือก Go เป็นภาษาแรก — ไม่ใช่การ rewrite ของเก่า — และ Ballerina compiler rewrite จาก Java → Go คือการตัดสินใจที่ต้นทุนสูงที่สุดเท่าที่พวกเขาเคยทำ&lt;/p&gt;

&lt;p&gt;เมื่อทีมที่สร้างภาษา Ballerina ตัดสินใจเขียน compiler ใหม่ด้วย Go — มันจบแล้วสำหรับข้อถกเถียง "Go ใช้กับ enterprise ได้ไหม"&lt;/p&gt;




&lt;h2&gt;
  
  
  นี่ไม่ใช่ "Java จะตาย"
&lt;/h2&gt;

&lt;p&gt;WSO2 ไม่ได้ประกาศว่า Java แย่ — 95% ของโค้ดยังเป็น Java — และการย้ายจะใช้เวลาเป็นทศวรรษ&lt;/p&gt;

&lt;p&gt;สิ่งที่พวกเขาพูดคือ: &lt;strong&gt;"เมื่อมองไปข้างหน้า 10-20 ปี — Java ไม่ใช่ default อีกต่อไป — และ Go คือตัวเลือกที่เราวางไว้"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;message ไม่ใช่ "ทิ้งทุกอย่างไปใช้ Go" — แต่คือ "เลือกเครื่องมือให้เหมาะกับงาน" — และสำหรับ server-side infrastructure ในยุค cloud-native — Go คือทางที่ WSO2 เลือก&lt;/p&gt;




&lt;h2&gt;
  
  
  สิ่งที่ Go มีที่ Java ไม่มี
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;th&gt;Go&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deploy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JVM + dependencies&lt;/td&gt;
&lt;td&gt;binary เดียว&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ช้า (วินาทีถึงนาที)&lt;/td&gt;
&lt;td&gt;เร็ว (มิลลิวินาที)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory footprint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ต่ำ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Threads (OS-managed)&lt;/td&gt;
&lt;td&gt;Goroutines (lightweight, managed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex (Gradle/Maven)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;go build&lt;/code&gt; — คำสั่งเดียว&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สูง — ecosystem ใหญ่&lt;/td&gt;
&lt;td&gt;ต่ำ — ภาษาเล็กโดยตั้งใจ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;ตัวเลขเป็นทิศทางเชิงคุณภาพ — ไม่ใช่ benchmark แบบ controlled — ขึ้นอยู่กับ workload และ configuration&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;WSO2 ประกาศทิศทางใหม่หลังจาก 20 ปีกับ Java — ไม่ใช่เพราะ Java ไม่ดี — แต่เพราะ Go ถูกออกแบบมาเพื่อโลกที่ WSO2 กำลังจะอยู่ไปอีก 10-20 ปี&lt;/p&gt;

&lt;p&gt;และหลักฐานไม่ได้อยู่ที่ blog post — มันอยู่ที่ OpenChoreo (เขียน Go), Thunder (เขียน Go), Ballerina compiler (rewrite จาก Java → Go)&lt;/p&gt;

&lt;p&gt;ถ้าบริษัท enterprise ระดับนี้วาง roadmap แบบนี้ — มันไม่ใช่ trend — มันคือ signal&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://wso2.com/library/blogs/goodbye-java-hello-go/" rel="noopener noreferrer"&gt;Goodbye Java, Hello Go! — WSO2 Blog&lt;/a&gt; (26 ม.ค. 2026) — Sameera Jayasoma &amp;amp; Sanjiva Weerawarana&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>java</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>ออกแบบ error ให้เป็น first-class citizen — ต่อจาก Double Report กับ Logging</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Thu, 09 Jul 2026 15:37:30 +0000</pubDate>
      <link>https://dev.to/gophernment/kaebb-error-aihepn-first-class-citizen-tcchaak-double-report-kab-logging-578h</link>
      <guid>https://dev.to/gophernment/kaebb-error-aihepn-first-class-citizen-tcchaak-double-report-kab-logging-578h</guid>
      <description>&lt;h2&gt;
  
  
  ออกแบบ error ให้เป็น first-class citizen — ต่อจาก Double Report กับ Logging
&lt;/h2&gt;

&lt;p&gt;สองบทความก่อนเราคุยกันเรื่อง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/gophernment/double-report-emuue-error-thuuk-log-chamchaakain-go-1607"&gt;Double Report&lt;/a&gt; — หลักการ "log หรือ return อย่างใดอย่างหนึ่ง"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/gophernment/log-ain-microservice-khwr-log-aair-emuueaihr-aelaaairthiiaimkhwr-log-1enk"&gt;Logging ใน microservice&lt;/a&gt; — วิธี log ให้ถูกที่ ถูกเวลา&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;บทความนี้คือภาค 3 — &lt;strong&gt;เมื่อ log ถูกที่แล้ว — แล้ว error ควรมีหน้าตายังไง&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เพราะ error ไม่ใช่แค่ &lt;code&gt;err.Error()&lt;/code&gt; — มันคือ API contract ระหว่าง layer — และการออกแบบ error ให้ดี จะทำให้ caller จัดการได้ถูกต้องโดยไม่ต้องอ่าน string message&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Sentinel Errors — "error ที่รู้จักกันทั่ว"
&lt;/h2&gt;

&lt;p&gt;Sentinel error คือ error ที่ประกาศเป็นตัวแปรระดับ package — ใช้ &lt;code&gt;errors.Is&lt;/code&gt; เช็ค:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// repo/errors.go&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ErrNotFound&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ErrConflict&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"resource already exists"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ErrInvalidInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid input"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// service/user.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrNotFound&lt;/span&gt;  &lt;span class="c"&gt;// map ไปเป็น error ของ service เอง&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetUser: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ข้อดี:&lt;/strong&gt; caller ไม่ต้อง parse string — &lt;code&gt;errors.Is(err, repo.ErrNotFound)&lt;/code&gt; — ชัดเจน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อเสีย:&lt;/strong&gt; sentinel error กลายเป็น public API ของ package — เปลี่ยนไม่ได้โดยไม่ break caller&lt;/p&gt;

&lt;h3&gt;
  
  
  Sentinel vs string matching
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ แย่ — parse string&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// error message เปลี่ยน = โค้ดพัง&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ✅ ดี — sentinel&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// message เปลี่ยนยังไงก็ไม่พัง&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Custom Error Types — "error ที่พกข้อมูล"
&lt;/h2&gt;

&lt;p&gt;Sentinel บอกว่า "เกิดอะไร" — แต่บางครั้ง caller ต้องการข้อมูลเพิ่ม:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// service/errors.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Field&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Value&lt;/span&gt;   &lt;span class="n"&gt;any&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Err&lt;/span&gt;     &lt;span class="kt"&gt;error&lt;/span&gt;  &lt;span class="c"&gt;// ← เก็บ sentinel error ไว้ข้างใน (ถ้ามี)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"validation: %s=%v: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;  &lt;span class="c"&gt;// ← ทำให้ errors.Is(e, ErrInvalidInput) ทำงานได้&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// handler/user.go — ใช้ errors.As เพื่อดึงข้อมูล&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"validation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ความต่างจาก sentinel:&lt;/strong&gt; &lt;code&gt;errors.Is&lt;/code&gt; = "ใช่ error นี้ไหม?" — &lt;code&gt;errors.As&lt;/code&gt; = "เป็น type นี้ไหม? + ดึงข้อมูล"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Typed-nil interface trap:&lt;/strong&gt; ถ้า function return &lt;code&gt;(*ValidationError)(nil)&lt;/code&gt; (nil pointer) แล้ว assign เข้า &lt;code&gt;error&lt;/code&gt; interface — &lt;code&gt;err != nil&lt;/code&gt; จะเป็น &lt;code&gt;true&lt;/code&gt; เพราะ interface เก็บ &lt;code&gt;(type=*ValidationError, value=nil)&lt;/code&gt; — ไม่ใช่ &lt;code&gt;(nil, nil)&lt;/code&gt; — classic Go footgun ที่ทั้ง junior และ senior เจอบ่อย — วิธีแก้: return &lt;code&gt;nil&lt;/code&gt; แบบไม่มี type (&lt;code&gt;return nil&lt;/code&gt; ไม่ใช่ &lt;code&gt;return (*ValidationError)(nil)&lt;/code&gt;) — เพิ่มเติม: &lt;a href="https://go.dev/doc/faq#nil_error" rel="noopener noreferrer"&gt;Go FAQ: nil error&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Retryable Error — "error ที่บอกว่าให้ลองใหม่"
&lt;/h2&gt;

&lt;p&gt;บาง error transient — ลองใหม่แล้วหาย (network timeout, connection reset, deadlock) — บาง error permanent — ลองกี่ครั้งก็ไม่หาย (not found, invalid input, permission denied)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// pkg/errors/retryable.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;RetryableError&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Retryable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// service/order.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CreateOrderReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isNetworkTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Retryable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Charge: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Charge: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;orderID&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;ห้าม retry โดยไม่มี idempotency — โดยเฉพาะ payment:&lt;/strong&gt; network timeout ไม่ได้แปลว่า payment gateway ไม่ได้รับ request — อาจ charge สำเร็จแล้วแต่ response ไม่กลับ — retry โดยไม่มี idempotency key = double charge — Stripe/Omise บังคับ &lt;code&gt;Idempotency-Key&lt;/code&gt; header ทุก request — ห้าม retry การ charge โดยไม่มี key เด็ดขาด&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// handler — retry แบบ loop + backoff + jitter + max attempts&lt;/span&gt;
&lt;span class="c"&gt;// (ไม่ใช่ recursion — stack overflow ถ้า error เกิดซ้ำ)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;baseDelay&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// (*Order, error)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"create order failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attempt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;writeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ← ใช้ error code + status code&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// exponential backoff + jitter — ไม่ retry ทันที&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;baseDelay&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// 100ms, 200ms, 400ms&lt;/span&gt;
        &lt;span class="n"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int63n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WarnContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"retrying"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attempt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"max_retries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="c"&gt;// client disconnected — ไม่ต้อง retry&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Sync retry ใน handler = บล็อก goroutine:&lt;/strong&gt; ที่ traffic สูง retry loop จะผูก HTTP handler thread ไว้เป็นวินาที — เสี่ยง connection exhaustion + client-side timeout — สำหรับ operation สำคัญที่ retry ยาว ควรพิจารณา async retry queue (เช่น NATS, Redis streams) แทนการ retry ใน handler โดยตรง — ตัวอย่างด้านบนเหมาะกับ retry สั้น (&amp;lt; 1 วิ) และ traffic ปานกลาง&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;handler ต้อง integrate กับ idempotency store:&lt;/strong&gt; retry loop อย่างเดียวไม่พอ — ต้อง wrap ด้วย &lt;code&gt;idempotencyStore.Execute(...)&lt;/code&gt; เพื่อกัน double-submit จาก client:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// handler — retry loop + idempotency store ทำงานร่วมกัน&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Idempotency-Key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ErrCodeInvalidInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"missing idempotency key"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idempotency&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// retry loop — ทำงานเฉพาะตอนได้ lock จาก idempotency store เท่านั้น&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lastErr&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;  &lt;span class="c"&gt;// ← จำ error ไว้ — loop จบแล้ว err ใน scope นี้หาย&lt;/span&gt;
            &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c"&gt;// backoff + jitter...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"max retries exceeded: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ← preserve retryable wrap&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Error → HTTP Status Code — map แบบ declarative
&lt;/h2&gt;

&lt;p&gt;Handler ไม่ควรมี switch-case ยาว ๆ — ใช้ helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// handler/errors.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrConflict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusConflict&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrInvalidInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusUnauthorized&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// handler/user.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"failed to get user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;   &lt;span class="c"&gt;// ← log ใส่ internal details ได้&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// ❌ อย่าส่ง err.Error() กลับไป client — leak internal details (SQL, stack, paths)&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ← "Not Found" ไม่ใช่ "FindByID: pq: relation..."&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ข้อดี:&lt;/strong&gt; map error → status แบบรวมศูนย์ — เพิ่ม error type ใหม่แค่เพิ่ม case ใน &lt;code&gt;httpStatus()&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced — error ที่บอก status code เอง
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AuthError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusUnauthorized&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpErr&lt;/span&gt; &lt;span class="n"&gt;HTTPError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;httpErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;httpErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Error กับ Metric — วัดว่าระบบสุขภาพดีไหม
&lt;/h2&gt;

&lt;p&gt;log อย่างเดียวไม่พอ — ต้องมี metric:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// middleware/metrics.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;MetricsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;responseStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// ✅ normalize path — /users/123 → /users/{id} — กัน cardinality ระเบิด&lt;/span&gt;
        &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;routePattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// เช่น chi.RouteContext(r.Context()).RoutePattern()&lt;/span&gt;

        &lt;span class="n"&gt;requestDuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c"&gt;// ← route template&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;requestCounter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ ห้ามใช้ &lt;code&gt;r.URL.Path&lt;/code&gt; เป็น label — ถ้า path มี dynamic segment (&lt;code&gt;/users/123&lt;/code&gt;, &lt;code&gt;/orders/456&lt;/code&gt;) — แต่ละ ID คือ timeseries ใหม่ → cardinality ระเบิด Prometheus — ต้อง normalize เป็น route template (&lt;code&gt;/users/{id}&lt;/code&gt;) — chi/gin/echo ทุกตัวมี &lt;code&gt;RoutePattern()&lt;/code&gt; ให้&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  metric + trace + log — อย่าแยกกัน
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// error log ต้องมี trace_id + span_id — ต่อกับ OTel ได้&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"create order failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"span_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  circuit breaker — อย่า retry ใส่ downstream ที่กำลังป่วย
&lt;/h3&gt;

&lt;p&gt;ถ้า payment gateway ล่ม — retry ทุกรอบมีแต่จะซ้ำเติม:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/sony/gobreaker/v2"&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gobreaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCircuitBreaker&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;gobreaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"payment-gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c"&gt;// เปิด circuit นานสุด 30 วิ&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ChargeReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gobreaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrOpenState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Charge: circuit open — gateway is down"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error Budget / SLO — ทำไมต้องมี
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLO: 99.9% ของ requests ต้องสำเร็จภายใน 1 เดือน
Error Budget: 0.1% = ~43 นาที downtime/เดือน

ถ้า error rate สูงเกิน error budget → freeze feature, focus on stability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;metric + alert:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// alert rule (Prometheus):&lt;/span&gt;
&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="s"&gt;"5.."&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; 
  &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests_total&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0.001&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;pager&lt;/span&gt; &lt;span class="n"&gt;ดัง&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;ดู&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. ภาพรวม — error flow จาก repo ถึง user
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user → handler → service → repo → database
                           ↑ error เกิดที่นี่

สิ่งที่ควรเกิดขึ้น:
1. repo:    return fmt.Errorf("FindByID: %w", err)     → wrap, ไม่ log
2. service: ตัดสินว่า error คืออะไร:
              errors.Is(err, sql.ErrNoRows) → return ErrNotFound
              errors.Is(err, timeout) → return Retryable(err)
3. handler: 
              errors.As(err, &amp;amp;valErr) → 400 Bad Request
              errors.As(err, &amp;amp;retryable) → retry
              errors.Is(err, ErrNotFound) → 404
              else → 500 + log + alert
4. metric:  requestCounter{status=500} → check SLO
5. alert:   error_rate &amp;gt; 0.1% → pager → on-call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Request ID — เชื่อม error กับ support ticket
&lt;/h2&gt;

&lt;p&gt;user แจ้งปัญหา: "ผมกดสั่งซื้อแล้ว error" — ops จะ grep log ด้วยอะไร?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// middleware — generate request_id ให้ทุก request&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;RequestIDMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;requestID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Request-ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;requestID&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;requestID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Request-ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;requestIDKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทุก log ควรมี request_id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;traceID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;user ส่ง &lt;code&gt;X-Request-ID: abc123&lt;/code&gt; → ops &lt;code&gt;grep abc123&lt;/code&gt; → เจอทุก log, metric, trace ที่เกี่ยวข้อง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 request_id ≠ trace_id — request_id เกิดที่ edge (client/load balancer) — trace_id เกิดที่ tracing system — request_id ใช้ grep log แบบง่าย — trace_id ใช้ดู flow ข้าม service — ควรมีทั้งคู่&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Error Codes — API contract ที่ machine อ่านได้
&lt;/h2&gt;

&lt;p&gt;HTTP status อย่างเดียวไม่พอ — client (mobile app, partner API) ต้องรู้ว่า error อะไรแน่:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// pkg/errors/codes.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ErrCodeNotFound&lt;/span&gt;      &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ERR_USER_NOT_FOUND"&lt;/span&gt;
    &lt;span class="n"&gt;ErrCodeDuplicate&lt;/span&gt;     &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ERR_DUPLICATE_EMAIL"&lt;/span&gt;
    &lt;span class="n"&gt;ErrCodeInvalidInput&lt;/span&gt;  &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ERR_INVALID_INPUT"&lt;/span&gt;
    &lt;span class="n"&gt;ErrCodeUnauthorized&lt;/span&gt;  &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ERR_UNAUTHORIZED"&lt;/span&gt;
    &lt;span class="n"&gt;ErrCodeInternal&lt;/span&gt;      &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ERR_INTERNAL"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// เข้ากับ custom error type&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;AppError&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Code&lt;/span&gt;    &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="s"&gt;`json:"code"`&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
    &lt;span class="n"&gt;Err&lt;/span&gt;     &lt;span class="kt"&gt;error&lt;/span&gt;     &lt;span class="s"&gt;`json:"-"`&lt;/span&gt;  &lt;span class="c"&gt;// ไม่ expose internal details&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// handler&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;writeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;appErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;appErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;appErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;client จะได้:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERR_USER_NOT_FOUND"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user with id 42 not found"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;mobile app ใช้ &lt;code&gt;code&lt;/code&gt; switch แสดงข้อความภาษาไทย — partner API ใช้ &lt;code&gt;code&lt;/code&gt; automate retry logic — ไม่ต้อง parse string&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;User enumeration attack (OWASP):&lt;/strong&gt; ห้ามใช้ error code granular สำหรับ auth-sensitive endpoint — ถ้า login ใช้ &lt;code&gt;ERR_USER_NOT_FOUND&lt;/code&gt; กับ &lt;code&gt;ERR_INVALID_PASSWORD&lt;/code&gt; แยกกัน — attacker ยิง email ทีละตัว ดู response ก็รู้ว่า email ไหนมีในระบบ — สำหรับ login/password reset/register ต้อง return error code เดียวกัน (&lt;code&gt;ERR_INVALID_LOGIN&lt;/code&gt;) ไม่ว่าจะ user not found หรือ wrong password — ส่วน resource endpoint ทั่วไป (get product, update profile) ใช้ granular code ได้ตามปกติ&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  idempotency — อย่าปล่อยให้ client double-submit
&lt;/h3&gt;

&lt;p&gt;ส่ง &lt;code&gt;Idempotency-Key&lt;/code&gt; ไปที่ payment gateway — แต่ถ้า client เอง retry เรียก &lt;code&gt;CreateOrder&lt;/code&gt; ซ้ำ (double-click, network flake ฝั่ง frontend) — service ต้องป้องกันเอง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// pkg/idempotency/store.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// SETNX — atomic lock กัน race condition&lt;/span&gt;
    &lt;span class="n"&gt;lockTTL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt;  &lt;span class="c"&gt;// lock สั้น แต่ต้องยาวกว่า timeout ของ downstream เสมอ&lt;/span&gt;
                                      &lt;span class="c"&gt;// ถ้า s.payment.Charge timeout=15s → lock ควร ≥30s&lt;/span&gt;
    &lt;span class="n"&gt;resultTTL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;            &lt;span class="c"&gt;// result เก็บนานกว่า — 5-15 นาที&lt;/span&gt;

    &lt;span class="n"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetNX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"processing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lockTTL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idempotency store unavailable: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;locked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// มีอีก request กำลังประมวลผล key นี้ — poll รอ result&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idempotency: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"processing"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c"&gt;// ยังไม่เสร็จ&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"failed-ambiguous"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idempotency: previous attempt failed — check with gateway"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idempotency: timeout waiting for concurrent request"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// ได้ lock — รัน fn จริง&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryableError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c"&gt;// reuse type เดียวกับ retry loop — ไม่สร้างเงื่อนไขคู่ขนาน&lt;/span&gt;
            &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed-ambiguous"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultTTL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idempotency: ambiguous failure — check with gateway: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// non-retryable — safe, error เกิดก่อนถึง gateway&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultTTL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Reconciliation job:&lt;/strong&gt; &lt;code&gt;"failed-ambiguous"&lt;/code&gt; ไม่ใช่คำตอบสุดท้าย — ระบบ payment จริงต้องมี background job ที่ query transaction status จาก gateway (Stripe/Omise มี endpoint เช็ค charge status ด้วย idempotency key) แล้วอัปเดต state เป็น success/failed จริง — ไม่ใช่ปล่อยให้เห็น error ค้างจน TTL หมด&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  สรุป — เช็คลิสต์
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Sentinel errors สำหรับ "รู้จักกันทั่ว" (ErrNotFound, ErrConflict)
✅ Custom error types เมื่อต้องการพกข้อมูล — พร้อม Unwrap()
✅ ⚠️ typed-nil interface trap — return nil แบบไม่มี type
✅ Retryable error + idempotency key + loop/backoff/jitter + max attempts
✅ Circuit breaker — อย่า retry ใส่ downstream ที่กำลังป่วย
✅ httpStatus() — map error → status แบบรวมศูนย์
✅ ห้าม leak err.Error() กลับ client — ใช้ http.StatusText + error code
✅ Metric — cardinality-safe (route template, ไม่ใช้ raw path)
✅ Metric + trace + log ต้อง correlate กัน (trace_id + span_id)
✅ Request ID — grep ได้ตอน user แจ้งปัญหา
✅ Error Codes — machine-readable สำหรับ API contract
✅ Alert — error budget หมด → pager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gophernment/double-report-emuue-error-thuuk-log-chamchaakain-go-1607"&gt;Double Report — เมื่อ Error ถูก Log ซ้ำซากใน Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gophernment/log-ain-microservice-khwr-log-aair-emuueaihr-aelaaairthiiaimkhwr-log-1enk"&gt;log ใน microservice — ควร log อะไร เมื่อไหร่&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sre.google/workbook/error-budget-policy/" rel="noopener noreferrer"&gt;Google SRE Book: Error Budgets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>errors</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>log ใน microservice — ควร log อะไร เมื่อไหร่ และอะไรที่ไม่ควร log</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Thu, 09 Jul 2026 13:01:46 +0000</pubDate>
      <link>https://dev.to/gophernment/log-ain-microservice-khwr-log-aair-emuueaihr-aelaaairthiiaimkhwr-log-1enk</link>
      <guid>https://dev.to/gophernment/log-ain-microservice-khwr-log-aair-emuueaihr-aelaaairthiiaimkhwr-log-1enk</guid>
      <description>&lt;h2&gt;
  
  
  log ใน microservice — ควร log อะไร เมื่อไหร่ และอะไรที่ไม่ควร log
&lt;/h2&gt;

&lt;p&gt;เคยเปิด log ใน production แล้วเจอแบบนี้ไหม:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  request received
INFO  processing...
INFO  done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สามบรรทัดนี้ — บอกอะไรเราไม่ได้เลย — request ไหน? ใช้เวลากี่ ms? สำเร็จหรือพัง?&lt;/p&gt;

&lt;p&gt;หรืออีกแบบ — log ทุกอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG user_id=42 name=สมชาย email=somchai@email.com 
       credit_card=4111-xxxx-xxxx-1234 amount=1500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PII เต็ม log — GDPR ปรับเป็นล้าน — และ storage ก็ไม่ฟรี&lt;/p&gt;

&lt;p&gt;การ log ใน microservice ไม่ใช่แค่ "ใส่ &lt;code&gt;print&lt;/code&gt; เยอะ ๆ" — มันคือการตัดสินใจว่า &lt;strong&gt;อะไรควรเก็บ อะไรไม่ควรเก็บ&lt;/strong&gt; — และบทความนี้จะว่ากันทีละเรื่อง&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อมูลพื้นฐานที่ทุก log ควรมี
&lt;/h2&gt;

&lt;p&gt;ไม่ว่าจะเป็น service อะไร — log ทุกบรรทัดควรมี 5 สิ่งนี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ts=2026-07-09T03:15:30Z level=INFO 
  msg="order created" trace_id=abc123 span_id=def456
  service=order-svc user_id=42 duration_ms=120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ต้องมี&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;th&gt;ทำไม&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;timestamp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ts=2026-07-09T03:15:30Z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;รู้ว่าเกิดเมื่อไหร่ — ISO 8601, UTC เสมอ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;level=INFO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;กรองได้ — DEBUG/INFO/WARN/ERROR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;message&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;msg="order created"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;คนอ่านรู้ว่าเกิดอะไรขึ้น — สั้น กระชับ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;trace_id&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;trace_id=abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ตาม request นี้ข้าม service ได้&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;service=order-svc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;รู้ว่า log มาจาก service ไหน&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  trace_id — สำคัญที่สุด
&lt;/h3&gt;

&lt;p&gt;ถ้าจะเลือกทำแค่ 1 อย่าง — ทำ trace_id — เพราะมันเชื่อม log ของทุก service ที่ request นี้ผ่าน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;อย่า reinvent เอง — ใช้ W3C Trace Context (&lt;code&gt;traceparent&lt;/code&gt; header):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
              │  │                                      │              │
              ver trace_id                               span_id        flags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นี่คือมาตรฐานที่ Jaeger, Datadog, Tempo, Grafana รองรับทั้งหมด — ไม่ต้อง generate เอง ไม่ต้อง invent header เอง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"go.opentelemetry.io/otel"&lt;/span&gt;
    &lt;span class="s"&gt;"go.opentelemetry.io/otel/propagation"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// middleware — extract trace_id จาก W3C traceparent header&lt;/span&gt;
&lt;span class="c"&gt;// suboptimal: อ่าน trace_id ได้ถูก แต่ไม่ได้สร้าง span (ไม่มี duration/status)&lt;/span&gt;
&lt;span class="c"&gt;// production ควรใช้ otelhttp.NewHandler(mux, "order-service") จาก otelcontrib&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TracingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;propagator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceContext&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;propagator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HeaderCarrier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ใน handler — trace_id ได้จาก span โดยตรง ไม่ต้อง context.WithValue&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;traceID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"order fetched"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;traceID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c"&gt;// ← ได้ฟรีจาก OTel&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ข้อดีของวิธีนี้:&lt;/strong&gt; ไม่มี custom header, ไม่มี context.WithValue string key, trace_id ตามข้าม service ได้อัตโนมัติผ่าน HTTP propagation — ทุก tool ใน ecosystem รองรับ&lt;/p&gt;




&lt;h2&gt;
  
  
  log เมื่อไหร่ — 3 จุดที่ควร log + 1 จุดที่ต้องคิดให้ดี
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Error — log ทุกครั้งเสมอ
&lt;/h3&gt;

&lt;p&gt;ไม่มีข้อแม้ — error ต้องมี log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"database query failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"INSERT INTO orders"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ไม่ควร log แบบนี้:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ❌ ไม่มี context — อ่านไม่รู้เรื่อง&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Call external service
&lt;/h3&gt;

&lt;p&gt;ทุกครั้งที่เรียก service อื่น หรือ external API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;notifyWarehouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warehouseClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"warehouse notification"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"warehouse-svc"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Milliseconds&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Business event
&lt;/h3&gt;

&lt;p&gt;สิ่งที่เกิดขึ้นแล้วมีผลทางธุรกิจ — log ทุกครั้ง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"order paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment_completed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Request/Response — ไม่จำเป็นต้อง log ทุก service
&lt;/h3&gt;

&lt;p&gt;ข้อนี้ต้องแยกให้ดี — &lt;strong&gt;log ทุก request ในทุก service = storage พุ่งแน่&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ตัวอย่าง — 20 services, 1,000 req/s, แต่ละ request ผ่าน 5 services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ถ้า log ทุก request ในทุก service:
  INFO request → 5 services × 1,000 req/s = 5,000 log lines/วินาที
  ต่อวัน = 432 ล้านบรรทัด
  ต่อเดือน = 13 พันล้านบรรทัด → storage เป็น TB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;minimum requirement ที่พอดี:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ log ที่ API Gateway (entry point) เท่านั้น:
   INFO request method=POST path=/orders trace_id=abc123

❌ log ทุก service ที่ request ผ่าน:
   INFO order-svc: request received   ← ไม่จำเป็น
   INFO payment-svc: request received ← ไม่จำเป็น
   INFO inventory-svc: request received ← ไม่จำเป็น

✅ ใช้ tracing แทน:
   trace_id=abc123 → ตามดูทุก service ได้ โดยไม่ต้อง log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าอยากรู้ว่า request ผ่าน service ไหนบ้าง — tracing คือคำตอบ — ถูกกว่าและข้อมูลมากกว่า:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// tracing — ถูกกว่า log ตรงที่:&lt;/span&gt;
&lt;span class="c"&gt;// 1. sampling ได้ (10% แทน 100%)&lt;/span&gt;
&lt;span class="c"&gt;// 2. ข้อมูลดีกว่า — เห็น parent-child relationship&lt;/span&gt;
&lt;span class="c"&gt;// 3. storage มักถูกกว่า (compressed binary แทน text)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"CreateOrder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// ... business logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;สรุปเรื่อง request/response:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ที่ไหน&lt;/th&gt;
&lt;th&gt;log request?&lt;/th&gt;
&lt;th&gt;เพราะ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;✅ ควร&lt;/td&gt;
&lt;td&gt;รู้ว่ามี request อะไรเข้ามา — แค่นี้ก็พอ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal service&lt;/td&gt;
&lt;td&gt;❌ ไม่ควร&lt;/td&gt;
&lt;td&gt;ใช้ tracing แทน — ถูกกว่า&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;td&gt;✅ ทุกที่&lt;/td&gt;
&lt;td&gt;error ต้อง log เสมอ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business event&lt;/td&gt;
&lt;td&gt;✅ ทุกที่&lt;/td&gt;
&lt;td&gt;นี่คือสิ่งที่ stakeholders สนใจ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  อย่าสับสน — บทความนี้ไม่ได้บอกว่า "อย่า log"
&lt;/h3&gt;

&lt;p&gt;กำลังบอกว่า &lt;strong&gt;อย่า log request ในทุก service&lt;/strong&gt; — เพราะมันเปลือง storage โดยไม่จำเป็น — ใช้ tracing แทน&lt;/p&gt;

&lt;p&gt;แต่ ERROR, business event, call external — log ได้เลย ไม่ต้องคิดมาก&lt;/p&gt;




&lt;h2&gt;
  
  
  Structured Logging — log แบบให้เครื่องอ่านรู้เรื่อง
&lt;/h2&gt;

&lt;p&gt;ใช้ &lt;code&gt;slog&lt;/code&gt; (Go 1.21+) — text format สำหรับ dev, JSON format สำหรับ production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// development — text format (อ่านง่าย)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTextHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;// ts=2026-07-09T03:15:30Z level=INFO msg="order created" order_id=ORD-001&lt;/span&gt;

&lt;span class="c"&gt;// production — JSON format (เอาเข้า Grafana/Loki/Datadog)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;// {"ts":"2026-07-09T03:15:30Z","level":"INFO","msg":"order created","order_id":"ORD-001"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ข้อดีของ structured log:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;search ด้วย field ได้ — &lt;code&gt;level=ERROR AND service=payment&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;aggregate ได้ — นับ orders/ชม&lt;/li&gt;
&lt;li&gt;alert ได้ — &lt;code&gt;ERROR &amp;gt; 10 ใน 5 นาที → pager&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Business Log — log เพื่อธุรกิจ ไม่ใช่เพื่อ debug
&lt;/h2&gt;

&lt;p&gt;นอกจาก log ทางเทคนิค — ควรมี log ที่ตอบคำถามทางธุรกิจ:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;คำถาม&lt;/th&gt;
&lt;th&gt;log แบบไหน&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"วันนี้ขายได้เท่าไหร่?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;event=order_paid amount=1500&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"feature ใหม่มีคนใช้ไหม?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;event=feature_used feature=new_search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"ลูกค้าหลุดตอนขั้นตอนไหน?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;event=checkout_abandoned step=payment&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"ของใกล้หมดไหม?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;event=stock_low sku=BKK-001 remaining=3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;เทคนิค:&lt;/strong&gt; ใส่ &lt;code&gt;event=&lt;/code&gt; prefix — ทำให้ search ใน log ง่าย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"checkout_completed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c"&gt;// ← search ด้วยคำนี้&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  PII — สิ่งที่ห้าม log เด็ดขาด
&lt;/h2&gt;

&lt;p&gt;PII (Personally Identifiable Information) คือข้อมูลที่ระบุตัวบุคคลได้ — และมันมีกฏหมายควบคุม (GDPR, PDPA)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;❌ ห้าม log&lt;/th&gt;
&lt;th&gt;✅ log แบบนี้แทน&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email=somchai@email.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ อย่า log — ใช้ &lt;code&gt;user_id=42&lt;/code&gt; แทน การ hash email แบบ plain SHA256 ไม่ปลอดภัย — email space มันจำกัด โดน rainbow table / brute force ง่าย — ต้องใช้ HMAC-SHA256 with secret key เท่านั้น ซึ่งยุ่งยากเกินเหตุ — user_id ก็พอแล้ว&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;credit_card=4111-xxxx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;payment_method=credit_card last_four=1234&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;password=secret123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ ไม่มีเหตุผลที่ต้อง log เลย&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssn=123-45-6789&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ ไม่มีเหตุผล — ใช้ user_id แทน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;phone=0812345678&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ อย่า log — hash plain SHA256 ก็ไม่ปลอดภัยด้วยเหตุผลเดียวกับ email — ไม่เก็บเลยดีที่สุด&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;address=123 ถนนสุขุมวิท&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;province=กรุงเทพ&lt;/code&gt; (เอาแค่ระดับจังหวัด)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ip=192.168.1.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ อย่า log — แม้ mask เหลือ &lt;code&gt;192.168.*.*&lt;/code&gt; ก็ยังเป็น PII ภายใต้ GDPR — ถ้าไม่ได้ใช้ทำ security/geo-block จริงจัง ไม่ต้องเก็บ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full_name=สมชาย ใจดี&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_id=42&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  วิธีป้องกัน — ทำที่ middleware
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;SanitizeLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// strip PII fields ก่อน log&lt;/span&gt;
    &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Attrs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
           &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"credit_card"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"ssn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;  &lt;span class="c"&gt;// ไม่ log field นี้&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;กฏง่าย ๆ:&lt;/strong&gt; ถ้าคิดว่า "log นี้จะถูกอ่านโดยคนนอกบริษัท" — ข้อมูลนั้นไม่ควรอยู่ใน log&lt;/p&gt;




&lt;h2&gt;
  
  
  log level — เมื่อไหร่ใช้อะไร
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;เมื่อไหร่&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEBUG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ระหว่าง dev — production ปิด&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEBUG query params: {...}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INFO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;business event, request flow ปกติ&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INFO order paid amount=1500&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WARN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สิ่งที่ไม่ควรเกิด แต่ไม่พัง&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WARN retry 3/5 connection refused&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ERROR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สิ่งที่พัง — ต้องมีคนดู&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ERROR payment failed order_id=001&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FATAL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;service หยุดทำงาน&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FATAL cannot connect to database&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;อย่าใช้ ERROR สำหรับสิ่งที่คาดเดาได้&lt;/strong&gt; — &lt;code&gt;user not found&lt;/code&gt; = INFO (ไม่ใช่ ERROR ไม่ใช่ WARN) — เพราะมันคือ expected client error เกิดถี่มาก (login attempt ผิด, search ไม่เจอ) ถ้าเป็น WARN จะ pollute alert threshold — สงวน WARN ไว้กับ signal ที่ operator ต้องสนใจจริง (retry, degraded, near-limit) เท่านั้น&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ตัวอย่าง — log ใน service จริง
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Checkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CheckoutReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 1. business validation — INFO เพราะเป็น expected client error&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid checkout amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrInvalidAmount&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 2. call external — log duration + success&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment processed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Milliseconds&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 3. business event — event= สำหรับ search&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"order completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"checkout_completed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Milliseconds&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  จุดที่มักถูกมองข้าม
&lt;/h2&gt;

&lt;h3&gt;
  
  
  timestamp ต้องเป็น UTC เสมอ ไม่ใช่ local time
&lt;/h3&gt;

&lt;p&gt;distributed system มี server หลาย timezone — ถ้า log A ใช้ &lt;code&gt;+07:00&lt;/code&gt; log B ใช้ &lt;code&gt;+00:00&lt;/code&gt; — sort log ข้าม service พังทันที&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ local time — sort ข้าม service ไม่ได้&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c"&gt;// ✅ UTC เสมอ + มี NTP sync ทุกเครื่อง&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ทุกเครื่องใน cluster ต้อง sync NTP&lt;/strong&gt; — clock skew แค่ 5 วิ ก็ทำให้ trace ดูไม่ออกว่า A เกิดก่อน B หรือเปล่า&lt;/p&gt;

&lt;h3&gt;
  
  
  log ต้อง correlate กับ trace — ไม่ใช่แยกกัน
&lt;/h3&gt;

&lt;p&gt;บทความพูดถึง &lt;code&gt;slog&lt;/code&gt; กับ OTel แยกกัน — แต่ใน production ควร integrate ให้มันเป็นเรื่องเดียวกัน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ log มี trace_id + span_id — คลิกจาก Grafana ไปดู log ได้&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"order paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"span_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า trace_id ใน log ตรงกับ trace_id ใน tracing backend — คุณคลิกจาก dashboard ไปดู log ของ request นั้นได้ทันที&lt;/p&gt;

&lt;h3&gt;
  
  
  ป้องกัน log injection
&lt;/h3&gt;

&lt;p&gt;user input มี &lt;code&gt;\n&lt;/code&gt; หรือ ANSI escape — inject เข้า log ได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ user input เข้า log โดยตรง — attacker ใส่ \n → ปลอมแปลง log entry&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c"&gt;// q=hello\n2026-07-09T10:00:00Z INFO admin logged in  ← ปลอม!&lt;/span&gt;

&lt;span class="c"&gt;// ✅ sanitize ก่อน log&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"strconv"&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;// escape \n, \t, control chars&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  error log ที่จุดเดียว — จุดสูงสุดของ call chain
&lt;/h3&gt;

&lt;p&gt;error bubble ผ่าน 3 layers แล้ว log ทุกชั้น = duplicate stack trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ log error ซ้ำทุก layer (pseudocode — ฟังก์ชันจริง syntax จะสมบูรณ์กว่านี้)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// → 3 log lines สำหรับ 1 error&lt;/span&gt;

&lt;span class="c"&gt;// ✅ log ที่ top-level handler — จุดเดียว&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FindByID: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetUser: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// → 1 log line — ครอบคลุมทั้ง chain (ผ่าน %w)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  business event = metric + log — ไม่ใช่แค่ log
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;order paid amount=1500&lt;/code&gt; — เก็บเป็น log อย่างเดียวไม่พอ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ counter (low-cardinality labels เท่านั้น) + histogram (สำหรับ value ต่อเนื่อง)&lt;/span&gt;
&lt;span class="n"&gt;orderCounter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment_completed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"THB"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c"&gt;// ← low-cardinality OK&lt;/span&gt;
    &lt;span class="c"&gt;// ❌ ห้ามใช้ attribute.Float64("amount", amount) — cardinality ระเบิด!&lt;/span&gt;
    &lt;span class="c"&gt;// แต่ละ order มี amount ไม่ซ้ำกัน → timeseries ใหม่ทุก order → Prometheus พัง&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;paymentAmountHistogram&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c"&gt;// ← ใช้ Histogram สำหรับ value ต่อเนื่อง&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"order paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Metric cardinality คือ mistake ที่พบบ่อยที่สุด:&lt;/strong&gt; ห้ามใช้ value ที่มี unique สูง (order_id, amount, email) เป็น label บน counter — ต้องใช้ Histogram/Gauge แทน มิฉะนั้น Prometheus/OTel backend จะ memory ระเบิดจาก timeseries เป็นล้าน ๆ series&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  rate-limit log — retry storm ต้องไม่ฆ่า storage
&lt;/h3&gt;

&lt;p&gt;circuit breaker fail → WARN ทุก 100ms → 36,000 log lines/ชม:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"golang.org/x/time/rate"&lt;/span&gt;

&lt;span class="c"&gt;// keyed limiter — แต่ละ error type มี limiter ของตัวเอง&lt;/span&gt;
&lt;span class="c"&gt;// ไม่งั้น db timeout จะบัง cache miss&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;limiters&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt; &lt;span class="c"&gt;// map[string]*rate.Limiter&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;warnWithLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;limiters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadOrStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lim&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Limiter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WarnContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dynamic log level — debug production โดยไม่ deploy
&lt;/h3&gt;

&lt;p&gt;เปลี่ยน log level เป็น DEBUG ตอน runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;globalLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LevelVar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ← mutable&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;globalLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c"&gt;// HTTP endpoint สำหรับเปลี่ยน level — ต้องอยู่หลัง internal-only network/mTLS/auth middleware&lt;/span&gt;
&lt;span class="c"&gt;// ❌ ห้ามเปิด public — DEBUG level จะ leak sensitive data + เป็น DoS vector&lt;/span&gt;
&lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/debug/loglevel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// debug, info, warn, error&lt;/span&gt;
    &lt;span class="n"&gt;globalLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parseLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# debug production incident — เปลี่ยน level ชั่วคราว&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://payment-svc:8080/debug/loglevel?level=debug"&lt;/span&gt;
&lt;span class="c"&gt;# แก้เสร็จ → กลับมา info&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://payment-svc:8080/debug/loglevel?level=info"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  retention tiering — hot → cold → delete
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hot (7 วัน, SSD): Loki/Grafana Cloud → fast search
warm (30 วัน, HDD): S3 Standard → search ได้แต่ช้า
cold (90 วัน, Glacier): S3 Glacier → ต้อง restore ก่อน
delete (&amp;gt;90 วัน): ไม่เก็บ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;policy นี้สำคัญกว่าแค่รู้ว่า "storage แพง" — ต้องมี tier จริง&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON log กับ multiline — ต้อง escape
&lt;/h3&gt;

&lt;p&gt;stack trace มี &lt;code&gt;\n&lt;/code&gt; — JSON parser พังถ้าไม่ escape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ JSON log — stack trace ทำ JSON แตก&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"panic: runtime error&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;goroutine 1 [running]:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ✅ ใช้ json.Marshal หรือ slog.Attr&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"panic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
&lt;span class="c"&gt;// slog จัดการ escape \n → \\n ให้อัตโนมัติ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  access log ≠ application log
&lt;/h3&gt;

&lt;p&gt;"log เฉพาะที่ API Gateway" — หมายถึง application log (ที่ dev เขียนเอง) — แต่ access log เป็นคนละเรื่อง:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Access Log&lt;/th&gt;
&lt;th&gt;Application Log&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ใครเขียน&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;infra (nginx, envoy, load balancer)&lt;/td&gt;
&lt;td&gt;dev (ในโค้ด)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เก็บอะไร&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;method, path, status, latency, IP&lt;/td&gt;
&lt;td&gt;business event, error, debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;volume&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;มหาศาล (ทุก request)&lt;/td&gt;
&lt;td&gt;น้อยกว่า (เฉพาะที่สำคัญ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ต่ำ — infra-level, compressed binary&lt;/td&gt;
&lt;td&gt;ปานกลาง — JSON text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ทำไมต้องมี&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;security audit, traffic analysis&lt;/td&gt;
&lt;td&gt;debug, business analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;หลาย org เก็บ access log ทุก ingress — แต่นั่นเป็น infra-layer ไม่ใช่ application code — และ cost structure ต่างกันกับที่บทความคำนวณ&lt;/p&gt;




&lt;h2&gt;
  
  
  สรุป — checklist
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ ทุก log มี: timestamp (UTC), level, message, trace_id + span_id
✅ log: error (ที่ top-level), call external, business event
✅ structured log (JSON) ใน production
✅ business event = metric (Prometheus) + log — ไม่ใช่แค่ log
✅ PII — ไม่ต้อง hash เพราะ hash plain SHA256 ไม่ปลอดภัย — ไม่ log เลย
✅ ใช้ log level ให้ถูก — user not found = INFO ไม่ใช่ WARN
✅ rate-limit WARN/ERROR — retry storm ต้องไม่ระเบิด storage
✅ dynamic log level — เปลี่ยน runtime โดยไม่ deploy
✅ sanitize user input ก่อน log — กัน log injection (\n, ANSI escape)
✅ retention: hot 7d → warm 30d → cold 90d → delete
✅ access log (infra) ≠ application log — cost structure ต่างกัน
✅ NTP sync ทุกเครื่อง — clock skew &amp;gt;5 วิ = sort log ข้าม service พัง
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pkg.go.dev/log/slog" rel="noopener noreferrer"&gt;slog package&lt;/a&gt; — Go 1.21+ structured logging&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/docs/languages/go/" rel="noopener noreferrer"&gt;OpenTelemetry Go&lt;/a&gt; — tracing + logging&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gdpr-info.eu/" rel="noopener noreferrer"&gt;GDPR &amp;amp; logging&lt;/a&gt; — ข้อกำหนด PII&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>microservices</category>
      <category>devops</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>microservice ต้องมี observability แค่ไหน ถึงจะคุ้ม</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Wed, 08 Jul 2026 23:39:09 +0000</pubDate>
      <link>https://dev.to/gophernment/microservice-tngmii-observability-aekhaihn-thuengcchakhum-1m1g</link>
      <guid>https://dev.to/gophernment/microservice-tngmii-observability-aekhaihn-thuengcchakhum-1m1g</guid>
      <description>&lt;h2&gt;
  
  
  microservice ต้องมี observability แค่ไหน ถึงจะคุ้ม
&lt;/h2&gt;

&lt;p&gt;มีเรื่องเล่าในทีมที่ผมเคยทำงานด้วย:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ระบบล่มตอนตี 2 — pager ดัง — dev ตื่นมาเปิด laptop&lt;br&gt;
ใช้เวลา 45 นาทีแกะว่า error เกิดจาก service ไหน&lt;br&gt;
พอรู้ว่าเป็น &lt;code&gt;payment-service&lt;/code&gt; — ใช้เวลาอีก 20 นาทีไล่ว่า request นี้ผ่าน &lt;code&gt;auth-service&lt;/code&gt; → &lt;code&gt;order-service&lt;/code&gt; → &lt;code&gt;payment-service&lt;/code&gt; → แล้วพังที่ &lt;code&gt;payment-service&lt;/code&gt; เพราะ &lt;code&gt;inventory-service&lt;/code&gt; ตอบกลับมา 500&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 ชั่วโมง 5 นาที — เพื่อรู้ว่า "พังที่ inventory"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ถ้ามี tracing — รู้ใน 2 นาที&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;นี่คือเหตุผลที่ observability เกิด — แต่มันก็มีราคาที่ต้องจ่าย&lt;/p&gt;




&lt;h2&gt;
  
  
  observability คืออะไร
&lt;/h2&gt;

&lt;p&gt;ไม่ใช่แค่ monitoring — monitoring บอกว่า "CPU 90%" — observability บอกว่า "CPU 90% เพราะเมื่อกี้มี request จาก &lt;code&gt;order-service&lt;/code&gt; มา 10,000 requests ใน 1 นาที — และ 30% ของ requests พังที่ &lt;code&gt;SELECT * FROM orders WHERE...&lt;/code&gt;"&lt;/p&gt;

&lt;p&gt;3 เสาหลัก:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;ถามอะไร&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📊 &lt;strong&gt;Metrics&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;"เกิดอะไรขึ้น?"&lt;/td&gt;
&lt;td&gt;CPU 90%, latency 2s, error rate 5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📜 &lt;strong&gt;Logs&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;"เกิดเพราะอะไร?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EROR: connection refused dial tcp 10.0.1.5:5432&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔍 &lt;strong&gt;Traces&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;"ผ่านมาทางไหน?"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gateway → auth → order → payment → inventory&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;monitoring = metrics&lt;/strong&gt; — รู้ว่ามีปัญหา&lt;br&gt;
&lt;strong&gt;observability = metrics + logs + traces&lt;/strong&gt; — รู้ว่าปัญหาคืออะไร และเกิดที่ไหน&lt;/p&gt;


&lt;h2&gt;
  
  
  ทำไม microservice ถึงต้องการ observability มากกว่า monolith
&lt;/h2&gt;
&lt;h3&gt;
  
  
  เพราะ request เดียวผ่าน service เยอะ
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monolith:
  request → [app] → response
  log อยู่ที่เดียว — เปิดไฟล์เดียวเจอ

Microservice:
  request → gateway → auth → user → order → payment → inventory → ...
  log กระจาย 7 services — ไม่มีทางไล่เองไหว
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;จำนวน service กับเวลา debug:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;จำนวน service&lt;/th&gt;
&lt;th&gt;เวลาที่ใช้แกะว่า error อยู่ตรงไหน (เฉลี่ย)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (monolith)&lt;/td&gt;
&lt;td&gt;5-10 นาที&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;15-30 นาที&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;30-60 นาที&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20+&lt;/td&gt;
&lt;td&gt;"ขอไปชงกาแฟก่อน"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;นี่คือ exponential growth ของความซับซ้อน — ไม่ใช่ linear&lt;/p&gt;
&lt;h3&gt;
  
  
  เพราะไม่มีใคร "เห็นภาพรวม"
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dev-A: "ผมดู log ของ payment-service แล้ว — มันพังที่ call external API"
dev-B: "แต่ external API มันรับ request จาก payment-service ปกตินะ"
dev-C: "เดี๋ยวนะ — payment-service ใช้ connection pool เก่าที่ถูก reset โดย auth-service"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ทุกคนเห็นแค่สิ่งที่ตัวเองดูแล — ไม่มีใครเห็น flow ทั้งหมด — &lt;strong&gt;tracing คือเครื่องมือเดียวที่แก้ปัญหานี้ได้&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  ต้องแลกด้วยอะไร
&lt;/h2&gt;
&lt;h3&gt;
  
  
  เงิน
&lt;/h3&gt;

&lt;p&gt;Datadog ทีม 20 services: ~$2,500/เดือน&lt;br&gt;
Grafana self-host: ~$250/เดือน + engineer time&lt;/p&gt;

&lt;p&gt;แต่เงินไม่ใช่ต้นทุนเดียว&lt;/p&gt;
&lt;h3&gt;
  
  
  เวลาตั้งค่า — และ maintain
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;สิ่งที่ต้องทำ:
1. ติดตั้ง OpenTelemetry SDK ในทุก service
2. ตั้งค่า collector — รับ data → ส่งไป backend
3. ตั้งค่า backend — Grafana/Prometheus/Datadog
4. สร้าง dashboard — อันนี้ใช้เวลาเยอะที่สุด
5. ตั้ง alert — ไม่งั้นมี dashboard แต่ไม่มีใครดู
6. onboard ทีม — สอนให้ทุกคนใช้เป็น
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ขั้นตอน 1-3 ใช้เวลาเป็นวัน ขั้นตอน 4-6 ใช้เวลาเป็นสัปดาห์ ถึงเดือน&lt;/p&gt;
&lt;h3&gt;
  
  
  Cognitive load
&lt;/h3&gt;

&lt;p&gt;ทุก tool ที่เพิ่ม = ทุกอย่างที่ทีมต้องเรียนรู้และ maintain&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ก่อน: รู้แค่ Go + Postgres + Docker
หลัง: รู้ Go + Postgres + Docker + Prometheus + Grafana + Loki + Tempo + OTel + ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าทีมมี 3 คน — เพิ่ม observability = เพิ่มภาระคนละ 20-30% — อาจไม่คุ้ม&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance overhead
&lt;/h3&gt;

&lt;p&gt;OTel SDK เพิ่ม latency ~1-5ms ต่อ request — และกิน memory ~10-50MB ขึ้นอยู่กับ sampling rate&lt;/p&gt;

&lt;p&gt;Sampling 100% = เห็นทุก request แต่ performance hit สูง&lt;br&gt;
Sampling 10% = เห็นแค่บางส่วน แต่แทบไม่มี overhead&lt;/p&gt;
&lt;h3&gt;
  
  
  Storage — ต้นทุนที่คนลืมคิด
&lt;/h3&gt;

&lt;p&gt;traces กับ logs ใช้ storage มหาศาล — และ storage ไม่ได้ฟรี&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ตัวอย่างจริง — 20 services, 1,000 req/s, 8 ชม/วัน:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;Traces (sampling 100%, เก็บ 7 วัน)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;1,000 req/s × 3,600s × 8h = 28.8M requests/วัน&lt;/span&gt;
  &lt;span class="s"&gt;เฉลี่ย 10 spans/request × ~2KB/span = ~576 GB/วัน&lt;/span&gt;
  &lt;span class="s"&gt;เก็บ 7 วัน = ~4 TB&lt;/span&gt;

  &lt;span class="s"&gt;ค่า storage (AWS S3)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~$90/เดือน&lt;/span&gt;
  &lt;span class="s"&gt;ค่า storage (Grafana Cloud)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~$500/เดือน&lt;/span&gt;
  &lt;span class="s"&gt;ค่า storage (Datadog)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~$2,000+/เดือน (คิดตาม ingestion + retention)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;และนี่คือ sampling 100%&lt;/strong&gt; — ทีมส่วนใหญ่ sampling แค่ 10-20% — แต่ก็ยังเป็นตัวเลขที่มีนัยสำคัญ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traces โตเร็วกว่าที่คิด (self-hosted storage — S3-compatible @ ~$22.50/TB):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;เดือนที่ 1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20 services, เก็บ 7 วัน → 4 TB → $90&lt;/span&gt;
&lt;span class="na"&gt;เดือนที่ 6&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;เพิ่ม 10 services, เก็บ 30 วัน → 30 TB → $675&lt;/span&gt;
&lt;span class="na"&gt;เดือนที่ 12&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trace ละเอียดขึ้น, เก็บ 90 วัน → 120 TB → $2,700&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;traces โตตาม services × retention × detail — ทั้งสามตัวมีแต่เพิ่ม ไม่เคยลด&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;วิธีคุม cost:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Sampling แบบหัว-ท้าย:
   - 100% เก็บเฉพาะ entry span (request เข้า)
   - 10% เก็บเต็มทุก span
   - error 100% เก็บหมด

2. Tail-based sampling:
   - ส่งทุก trace เข้า collector
   - collector ตัดสินใจเก็บเฉพาะ trace ที่ผิดปกติ (error, latency สูง)

3. ตั้ง retention policy:
   - traces: 3-7 วัน (พอ debug incident ล่าสุด)
   - metrics: 30-90 วัน (ดู trend)
   - logs: 7-14 วัน (debug)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;กฏง่าย ๆ:&lt;/strong&gt; storage cost ของ traces มักจะมากกว่า CPU cost ของ services ที่คุณ monitor — ถ้า traces cost &amp;gt; infra cost — คุณทำอะไรผิดซักอย่าง&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  แล้วเมื่อไหร่ถึงคุ้ม
&lt;/h2&gt;

&lt;h3&gt;
  
  
  คุ้ม — เมื่อ
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. ใช้เวลาดีบั๊ก production เกิน 30 นาทีต่อครั้ง&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ถามตัวเอง: incident ครั้งล่าสุด — ใช้เวลาเท่าไหร่กว่าจะรู้ว่าเกิดอะไรขึ้น?&lt;/p&gt;

&lt;p&gt;ถ้าตอบว่า "ไม่รู้" หรือ "เกิน 30 นาที" — observability คุ้มแน่&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. มี service &amp;gt;10 ตัว หรือทีม &amp;gt;5 คน&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เส้นแบ่งคร่าว ๆ: monolith → observability "nice to have" — 5+ services → "ควรมี" — 10+ services → "ต้องมี"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. downtime มีผลต่อ revenue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ถ้าทุกนาทีที่ระบบล่ม = เสียเงิน — observability คือประกัน — จ่าย $2K/เดือน ดีกว่าเสีย $50K/นาทีตอนระบบล่ม&lt;/p&gt;

&lt;h3&gt;
  
  
  ไม่คุ้ม — เมื่อ
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. มี service &amp;lt;5 ตัว — และทีม &amp;lt;3 คน&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เปิด log ทีละ service ก็ยังไหว — observability อาจ overkill&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. ยังไม่มี incident บ่อย&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ถ้า production ยังนิ่ง — เงินก้อนนั้นไปเพิ่ม feature ดีกว่า&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. กำลัง validate product-market fit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;startup ระยะแรก — metrics สำคัญกว่า observability — รู้ว่า revenue เท่าไหร่สำคัญกว่ารู้ว่า request ผ่านกี่ hop&lt;/p&gt;




&lt;h2&gt;
  
  
  เริ่มยังไง — แบบไม่เจ็บ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;อย่าเริ่มด้วย "ลง observability platform"&lt;/strong&gt; — เริ่มด้วยการถาม:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"incident ครั้งล่าสุด — อะไรคือคำถามที่เราใช้เวลาหาคำตอบนานที่สุด?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ระดับ 1: Structured Logging (ฟรี ใช้เวลา 1 วัน)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// แทนที่จะ log แบบนี้&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user %s login failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// log แบบนี้&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid_password"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;traceID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;trace_id&lt;/code&gt; คือกุญแจ — มันเชื่อม log ทุก service ที่ request นี้ผ่าน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Level 1 ใช้ request-scoped ID (เช่น UUID จาก middleware) — ยังไม่ใช่ OTel trace ID จริง — Level 2 จะแทนที่ด้วย trace_id จาก span context ที่เชื่อมข้าม service ได้จริง&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ระดับ 2: Tracing (ฟรี ค่อยเป็นค่อยไป)
&lt;/h3&gt;

&lt;p&gt;เริ่มจากเพิ่ม span ในแต่ละ function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-service"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GetUser"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// ... โค้ดเดิม&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่นั่นทำให้เห็นแค่ span ภายใน process เดียว — ยังไม่เห็น flow ข้าม service&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สิ่งที่ขาด — trace context propagation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// HTTP client — inject traceparent header ให้เรียกข้าม service&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultTransport&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://payment-service/charge"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// trace_id ตามไปอัตโนมัติ&lt;/span&gt;

&lt;span class="c"&gt;// HTTP server — extract traceparent จาก request ขาเข้า&lt;/span&gt;
&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"order-service"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า order-service ใช้ &lt;code&gt;otelhttp.NewTransport&lt;/code&gt; ตอนเรียก payment-service — span ของทั้งสอง service จะถูก link เป็น trace เดียวกัน — นี่คือสิ่งที่ทำให้ "รู้ใน 2 นาที" จากบทนำเป็นจริง&lt;/p&gt;

&lt;p&gt;การเพิ่ม span ใช้ ~3 บรรทัดต่อ function — &lt;code&gt;otelhttp.NewTransport&lt;/code&gt;/&lt;code&gt;NewHandler&lt;/code&gt; ใช้ 1 บรรทัด — ได้ tracing ทั้งระบบ โดยไม่ต้องเขียน propagation logic เอง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 หลักการเดียวกับ PII ใน log — ใช้กับ span attributes ด้วย: ห้ามใส่ email, user_id, order_id เป็น span tag — ใช้ trace_id + log correlation แทน&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ระดับ 3: Dashboard + Alert (เริ่มมีค่าใช้จ่าย)
&lt;/h3&gt;

&lt;p&gt;เริ่มจาก Grafana Cloud Free → ดู metrics + traces → ตั้ง alert เฉพาะ critical path&lt;/p&gt;




&lt;h2&gt;
  
  
  สรุป — คุ้มไหม
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;monolith → observability = nice to have
microservice → observability = requirement ไม่ใช่ feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ requirement ไม่ได้แปลว่าต้องทำเต็มรูปแบบ — เริ่มจาก structured logging + tracing (ฟรี, ใช้เวลาไม่นาน) — แล้วค่อยขยับไป dashboard + alert เมื่อเริ่มรู้สึกว่า "หา bug นานเกินไป"&lt;/p&gt;

&lt;p&gt;และที่สำคัญ: &lt;strong&gt;อย่าตั้ง budget observability จากการ copy-paste plan ของทีม 300 คน&lt;/strong&gt; — ทีม 5 คนต้องการ observability คนละแบบกับทีม 50 คน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/docs/languages/go/" rel="noopener noreferrer"&gt;OpenTelemetry Go&lt;/a&gt; — tracing SDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grafana.com/products/cloud/" rel="noopener noreferrer"&gt;Grafana Cloud Free&lt;/a&gt; — เริ่มต้น observability ฟรี&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>devops</category>
      <category>microservices</category>
      <category>monitoring</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Go 1.27 รับรอง QUERY — HTTP Method ใหม่ที่ GET ทำไม่ได้</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Wed, 08 Jul 2026 12:21:11 +0000</pubDate>
      <link>https://dev.to/gophernment/go-127-rabrng-query-http-method-aihmthii-get-thamaimaid-mde</link>
      <guid>https://dev.to/gophernment/go-127-rabrng-query-http-method-aihmthii-get-thamaimaid-mde</guid>
      <description>&lt;h2&gt;
  
  
  Go 1.27 รับรอง QUERY — HTTP Method ใหม่ที่ GET ทำไม่ได้
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Go 1.27 ยังไม่ release — คาดเดือนสิงหาคม 2026 — QUERY method อยู่ในระหว่าง development (issue #80134)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ถ้าคุณเคยเขียน search API — คุณน่าจะเคยเจอปัญหานี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/search?q=golang&amp;amp;category=book&amp;amp;year=2025&amp;amp;author=donovan
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;พารามิเตอร์เยอะมากจน URL ยาวเป็นหางว่าว — แล้วยิ่งถ้าต้องส่ง query ที่ซับซ้อน (nested conditions, OR, range queries) — GET แทบจะใช้งานไม่ได้&lt;/p&gt;

&lt;p&gt;แล้วก็มีคนเสนอว่า: "งั้นใช้ POST สิ" — แต่ POST ไม่ใช่ safe method — มันไม่ควร cache, search engine ไม่ index, CDN ไม่ cache&lt;/p&gt;

&lt;p&gt;ทางออกที่วงการ HTTP ออกแบบมาตั้งแต่ปี 2022: &lt;strong&gt;QUERY method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;และตอนนี้ Go 1.27 กำลังจะรับรองมันอย่างเป็นทางการ&lt;/p&gt;




&lt;h2&gt;
  
  
  QUERY คืออะไร
&lt;/h2&gt;

&lt;p&gt;QUERY คือ HTTP method ที่ถูกกำหนดใน &lt;strong&gt;RFC 9110&lt;/strong&gt; (HTTP Semantics, มิถุนายน 2022)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;QUERY&lt;/span&gt; &lt;span class="nn"&gt;/api/search&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"golang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"th"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;คุณสมบัติ&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;POST&lt;/th&gt;
&lt;th&gt;QUERY&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Safe&lt;/strong&gt; (ไม่เปลี่ยน state)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Idempotent&lt;/strong&gt; (เรียกซ้ำได้ผลลัพธ์เดิม)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;มี request body ได้&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ (ทำได้แต่ไม่ควร)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache ได้&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search engine index&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;QUERY = GET + request body — ปลอดภัย, cache ได้, ส่ง query ซับซ้อนได้&lt;/p&gt;




&lt;h2&gt;
  
  
  ทำไมถึงต้องมี QUERY — ปัญหาที่ GET แก้ไม่ได้
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ปัญหาที่ 1: URL ยาวเกิน
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/search?q=golang&amp;amp;tags=programming,language,backend&amp;amp;...
→ URL 500+ ตัวอักษร — หลาย browser/proxy มี limit ที่ 2,048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QUERY ส่ง body แทน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;QUERY /api/search
Content-Type: application/json
{"q": "golang", "tags": ["programming", "language", "backend"], ...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ปัญหาที่ 2: ข้อมูล sensitive ใน URL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/search?email=user@example.com&amp;amp;ssn=1234
→ URL ถูก log โดย proxy, server, CDN — SSN รั่ว!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QUERY: ข้อมูลอยู่ใน body — ไม่ปรากฏใน log&lt;/p&gt;

&lt;h3&gt;
  
  
  ปัญหาที่ 3: Query ซับซ้อนเกิน query string
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Elasticsearch-style&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;เขียนใน&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ไม่ได้&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-01-01"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"should"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"golang"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"go programming"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  การมี QUERY เปลี่ยนอะไร
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ก่อน QUERY — ทางเลือกที่มี
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. GET + query string → URL ยาว, sensitive data รั่ว
2. POST + body → ใช้ได้ แต่ผิด semantics → ไม่ cache, SEO ไม่ดี
3. POST + body + X-HTTP-Method-Override → workaround แต่ไม่ standard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  หลัง QUERY — ทางเลือกใหม่
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;QUERY /api/search → body = query → safe, cacheable, clean
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Go 1.27 — วิธีใช้
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;searchHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Go 1.27 — ใช้ MethodQuery แทน string "QUERY"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodQuery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"method not allowed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusMethodNotAllowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// อ่าน request body — เหมือน POST&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;SearchQuery&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"QUERY /api/search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ก่อน Go 1.27:&lt;/strong&gt; ต้องใช้ string &lt;code&gt;"QUERY"&lt;/code&gt; ตรง ๆ — &lt;code&gt;http.MethodQuery&lt;/code&gt; ยังไม่มี&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go 1.27:&lt;/strong&gt; เพิ่ม &lt;code&gt;http.MethodQuery&lt;/code&gt; — constant มาตรฐาน เหมือน &lt;code&gt;http.MethodGet&lt;/code&gt;, &lt;code&gt;http.MethodPost&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  คนทำงานควรปรับตัวอย่างไร
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ถ้าคุณเป็น Backend Developer
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. ใช้ QUERY สำหรับ search endpoint&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- r.HandleFunc("POST /api/search", searchHandler)
&lt;/span&gt;&lt;span class="gi"&gt;+ r.HandleFunc("QUERY /api/search", searchHandler)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ประโยชน์:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ CDN cache ได้ — ลด load server&lt;/li&gt;
&lt;li&gt;✅ Semantics ถูกต้อง — POST ไม่ใช่วิธีที่ถูกสำหรับการ search&lt;/li&gt;
&lt;li&gt;✅ API ของคุณ self-documenting — &lt;code&gt;QUERY&lt;/code&gt; บอกว่า "นี่คือการอ่านข้อมูล"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. อัปเดต API documentation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old: POST /api/search (ใช้ POST เพราะ query ซับซ้อนเกิน GET)
New: QUERY /api/search (ใช้ QUERY — safe, cacheable)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. รองรับทั้งสองวิธีในช่วงเปลี่ยนผ่าน&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;searchHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodQuery&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodPost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"method not allowed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusMethodNotAllowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ... logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ถ้าคุณเป็น Frontend Developer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// จาก POST&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// เป็น QUERY&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;QUERY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ถ้าคุณเป็น DevOps / Infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CDN:&lt;/strong&gt; อัปเดต config ให้ cache &lt;code&gt;QUERY&lt;/code&gt; requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy/Load Balancer:&lt;/strong&gt; เช็คว่ารองรับ &lt;code&gt;QUERY&lt;/code&gt; method หรือไม่ (บางตัวอาจ reject)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging/Monitoring:&lt;/strong&gt; เพิ่ม &lt;code&gt;QUERY&lt;/code&gt; ใน metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAF/Firewall:&lt;/strong&gt; allow &lt;code&gt;QUERY&lt;/code&gt; method&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  สถานะปัจจุบัน
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RFC 9110:        ✅ Published (June 2022)
Browser support: ⚠️ fetch() รองรับ แต่ยังไม่ widespread
Go support:      🚧 Go 1.27 (August 2026) — ในระหว่างพัฒนา
GitHub Issue:    #80134 (darkweak, opened June 2026)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;QUERY คือ GET ที่มี body — safe, idempotent, cacheable — ออกแบบมาสำหรับ search และ query ที่ซับซ้อน&lt;/p&gt;

&lt;p&gt;Go 1.27 เป็น milestone สำคัญที่ทำให้ QUERY กลายเป็น first-class citizen ใน ecosystem — หลังจากนี้ เราน่าจะเห็น API เปลี่ยนจาก &lt;code&gt;POST /search&lt;/code&gt; เป็น &lt;code&gt;QUERY /search&lt;/code&gt; มากขึ้น&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc9110" rel="noopener noreferrer"&gt;RFC 9110: HTTP Semantics&lt;/a&gt; — QUERY method definition&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/golang/go/issues/80134" rel="noopener noreferrer"&gt;Go Issue #80134&lt;/a&gt; — net/http: add QUERY HTTP method&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/golang/go/issues/80058" rel="noopener noreferrer"&gt;Go Issue #80058&lt;/a&gt; — proposal: MethodQuery constant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://go.dev/doc/go1.27" rel="noopener noreferrer"&gt;Go 1.27 Release Notes&lt;/a&gt; — draft&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📅 เขียนเมื่อ: กรกฎาคม 2026 | Go 1.27 อยู่ในระหว่างพัฒนา&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>http</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Go errors .Is กับ .As เป็นเพื่อนกัน</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Wed, 08 Jul 2026 04:31:44 +0000</pubDate>
      <link>https://dev.to/gophernment/go-errors-is-kab-as-epnephuuenkan-2ebl</link>
      <guid>https://dev.to/gophernment/go-errors-is-kab-as-epnephuuenkan-2ebl</guid>
      <description>&lt;h2&gt;
  
  
  Go errors .Is กับ .As เป็นเพื่อนกัน
&lt;/h2&gt;

&lt;p&gt;เวลาเริ่มเขียน Go — เรามักเช็ค error ด้วย &lt;code&gt;==&lt;/code&gt; ตรง ๆ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// handle&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่พอเริ่มใช้ &lt;code&gt;fmt.Errorf("...: %w", err)&lt;/code&gt; wrap error — &lt;code&gt;==&lt;/code&gt; ใช้ไม่ได้แล้ว&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// err = "GetUser: sql: no rows in result set"&lt;/span&gt;
&lt;span class="c"&gt;// err == sql.ErrNoRows → false! เพราะมันถูก wrap แล้ว&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go เลยมี 2 ฟังก์ชันให้: &lt;strong&gt;&lt;code&gt;errors.Is&lt;/code&gt;&lt;/strong&gt; และ &lt;strong&gt;&lt;code&gt;errors.As&lt;/code&gt;&lt;/strong&gt; — หน้าตาคล้ายกัน แต่หน้าที่ต่างกัน&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;fmt.Errorf&lt;/code&gt; กับ &lt;code&gt;%w&lt;/code&gt; — จุดเริ่มต้นของทุกอย่าง
&lt;/h2&gt;

&lt;p&gt;ก่อนจะเข้าใจ &lt;code&gt;Is&lt;/code&gt; กับ &lt;code&gt;As&lt;/code&gt; — ต้องเข้าใจ &lt;code&gt;%w&lt;/code&gt; ก่อน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getUser(%s): %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;//                          ↑↑↑↑↑↑↑↑↑↑↑&lt;/span&gt;
        &lt;span class="c"&gt;//                          wrap err ด้วย %w&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;%w&lt;/code&gt; คือ verb พิเศษที่ &lt;strong&gt;"ห่อ"&lt;/strong&gt; error ไว้ข้างใน — ไม่ใช่แค่ต่อ string แบบ &lt;code&gt;%v&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;
&lt;span class="n"&gt;wrapped&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getUser: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c"&gt;// "getUser: sql: no rows in result set"&lt;/span&gt;

&lt;span class="c"&gt;// หน้าตาภายใน:&lt;/span&gt;
&lt;span class="c"&gt;// wrapped = &amp;amp;wrapError{&lt;/span&gt;
&lt;span class="c"&gt;//     msg: "getUser",&lt;/span&gt;
&lt;span class="c"&gt;//     err: sql.ErrNoRows  ← เก็บ original ไว้!&lt;/span&gt;
&lt;span class="c"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;%w&lt;/code&gt; ทำให้เกิด &lt;strong&gt;error chain&lt;/strong&gt; — error ลูกถูกซ้อนอยู่ใน error แม่ — และ &lt;code&gt;Is&lt;/code&gt; กับ &lt;code&gt;As&lt;/code&gt; รู้วิธีเดิน chain นี้&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;errors.Is&lt;/code&gt; — "ใช่ error นี้หรือเปล่า?"
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"errors"&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// err = "getUser(42): sql: no rows in result set"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ✅ true — Is เดินตาม chain แล้วเจอ sql.ErrNoRows ข้างใน&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;errors.Is&lt;/code&gt;&lt;/strong&gt; ถามว่า: &lt;strong&gt;"error นี้ (หรือ error ที่ถูก wrap ไว้ข้างใน) คือ &lt;code&gt;sql.ErrNoRows&lt;/code&gt; หรือไม่?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;หลักการทำงาน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. เปรียบเทียบ err == sql.ErrNoRows? → false (คนละ type)
2. err มี Unwrap() ไหม? → มี (เพราะใช้ %w)
3. unwrap → "sql: no rows..."
4. เปรียบเทียบ unwrapped == sql.ErrNoRows? → ✅ true!
5. ตอบ true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างเพิ่มเติม:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// เช็คหลายระดับ&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"handler: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"service: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ✅ true — ลงลึก 2 ชั้น&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;errors.As&lt;/code&gt; — "เป็น error type นี้หรือเปล่า?"
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pathErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathError&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/nonexistent/file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pathErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ✅ true — err เป็น *os.PathError (หรือมี *os.PathError อยู่ใน chain)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c"&gt;// "/nonexistent/file"&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"op:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c"&gt;// "open"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;errors.As&lt;/code&gt;&lt;/strong&gt; ถามว่า: &lt;strong&gt;"error นี้ (หรือ error ที่ถูก wrap ไว้ข้างใน) เป็น type &lt;code&gt;*os.PathError&lt;/code&gt; หรือไม่? — ถ้าใช่ ใส่ค่าให้ตัวแปรที่ส่งเข้าไป"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;หลักการทำงาน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. err เป็น *os.PathError? → ใช่!
2. ใส่ค่า err เข้าไปใน pathErr (ผ่าน pointer ที่ส่งเข้า)
3. ตอบ true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ข้อแตกต่างสำคัญ:&lt;/strong&gt; &lt;code&gt;As&lt;/code&gt; รับ &lt;strong&gt;pointer to error type&lt;/strong&gt; — ไม่ใช่ error value — เพราะมันต้อง &lt;strong&gt;assign&lt;/strong&gt; ค่ากลับมา&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ ผิด — As ต้องการ pointer&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathError&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ✅ ถูก — pointer to error type&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathError&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ✅ ถูก — ใช้ interface ก็ได้&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;netErr&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;netErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;Is&lt;/code&gt; vs &lt;code&gt;As&lt;/code&gt; — ต่างกันตรงไหน?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;errors.Is&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;errors.As&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;คำถาม&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"ใช่ error นี้หรือเปล่า?"&lt;/td&gt;
&lt;td&gt;"เป็น type นี้หรือเปล่า?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เทียบกับ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;value&lt;/strong&gt; (sentinel error)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;type&lt;/strong&gt; (error struct)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;รับ parameter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;error value โดยตรง&lt;/td&gt;
&lt;td&gt;pointer to error type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ได้อะไรกลับ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true/false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true/false&lt;/code&gt; + ใส่ค่าให้ตัวแปร&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ใช้เมื่อ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;อยากรู้ว่าใช่ error ที่รู้จักไหม&lt;/td&gt;
&lt;td&gt;อยากรู้ว่าเป็น type อะไร และเข้าถึง field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ตัวอย่าง&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;errors.Is(err, sql.ErrNoRows)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;errors.As(err, &amp;amp;pathErr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  วิธีจำ — "Is เช็คชื่อ, As เช็ค type"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is → "Is this THAT error?" → เปรียบเทียบ value
As → "As WHAT type?" → ตรวจสอบ type + extract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ตัวอย่างที่ใช้ทั้งคู่
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 1. เช็คก่อนว่า "ใช่ not found ไหม?"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 2. เช็คต่อว่า "เป็น path error ไหม?" — ถ้าใช่ ดูว่าไฟล์ไหน&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pathErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pathErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"can't open %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 3. เช็ค transient network error&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;netErr&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;netErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;netErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Temporary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"temporary — retry"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unexpected: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Custom Error Types — ใช้กับ &lt;code&gt;Is&lt;/code&gt; และ &lt;code&gt;As&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Sentinel error — ใช้กับ Is&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ErrPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"permission denied"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// 2. Custom error type — ใช้กับ As&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid %s: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ใช้ Is กับ sentinel&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrPermission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"no permission"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ใช้ As กับ custom type&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ValidationError&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field %s is invalid&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧠 Hint สำหรับจำ
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is  →  "Is this error the SAME as...?"
        → เปรียบเทียบค่า เหมือน == แต่เดิน chain ให้

As  →  "As WHAT TYPE?"
        → ตรวจ type + extract value เหมือน type assertion แต่เดิน chain ให้
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;สองคำ:&lt;/strong&gt; &lt;code&gt;Is&lt;/code&gt; = identity, &lt;code&gt;As&lt;/code&gt; = extraction&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 สรุป
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ฟังก์ชัน&lt;/th&gt;
&lt;th&gt;ใช้กับ&lt;/th&gt;
&lt;th&gt;ใช้เมื่อ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fmt.Errorf("...: %w", err)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;สร้าง error chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;errors.Is(err, target)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sentinel errors (&lt;code&gt;var ErrX = errors.New(...)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;เช็คว่าใช่ error นี้ไหม&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;errors.As(err, &amp;amp;target)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error types (&lt;code&gt;*PathError&lt;/code&gt;, &lt;code&gt;net.Error&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;เช็คว่าเป็น type นี้ไหม + ดึงค่า&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/blog/go1.13-errors" rel="noopener noreferrer"&gt;Go Blog: Working with Errors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/errors" rel="noopener noreferrer"&gt;errors package docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/fmt" rel="noopener noreferrer"&gt;fmt package — %w verb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>errors</category>
      <category>beginners</category>
    </item>
    <item>
      <title>ภาษาโปรแกรมมิ่งที่ syntax ง่าย AI ช่วยเราได้ดีกว่า จริงหรือ?</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Tue, 07 Jul 2026 01:25:59 +0000</pubDate>
      <link>https://dev.to/gophernment/phaasaaopraekrmmingthii-syntax-ngaay-thamaih-ai-hlnnylng-cchringhruue-5al9</link>
      <guid>https://dev.to/gophernment/phaasaaopraekrmmingthii-syntax-ngaay-thamaih-ai-hlnnylng-cchringhruue-5al9</guid>
      <description>&lt;h2&gt;
  
  
  ภาษาโปรแกรมมิ่งที่ syntax ง่าย AI ช่วยเราได้ดีกว่า จริงหรือ?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"เขียน Go แล้ว Copilot แม่นกว่าเขียน Java" — เคยได้ยินไหม?&lt;br&gt;
หรือ "Python มันง่าย AI เลยเก่ง" — จริงหรือแค่ confirmation bias?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;นิยาม "ดีกว่า" ในบทความนี้: token ประหยัดกว่า, over-generation (babbling) น้อยกว่า, pass rate สูงกว่า — ไม่ใช่ hallucination (invented API, เรียก library ที่ไม่มีจริง) — งานวิจัยเรื่อง hallucination ข้ามภาษายังไม่มี&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;บทความนี้พาไปดู &lt;strong&gt;3 งานวิจัย&lt;/strong&gt; ที่ตอบคำถามนี้ด้วยข้อมูล&lt;/p&gt;




&lt;h2&gt;
  
  
  📄 งานวิจัยที่ 1: Token Sugar — "ยิ่ง verbose ยิ่งเปลือง token"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Token Sugar: Making Source Code Sweeter for LLMs through Token-Efficient Shorthand&lt;/strong&gt; (ASE 2025)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;หมายเหตุ: งานวิจัยนี้วัดประสิทธิภาพการลด token ภายใน corpus — ไม่ใช่การเปรียบเทียบข้ามภาษา — ตัวอย่าง Java vs Go ด้านล่างเป็นการตีความของผู้เขียนเพื่อให้เห็นภาพ&lt;br&gt;
โดย Zhensu Sun, Chengran Yang และคณะ — &lt;a href="https://arxiv.org/abs/2512.08266" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2512.08266&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;ข้อค้นพบ:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The inherent verbosity of programming languages, such as unnecessary formatting elements and lengthy boilerplate code, leads to inflated token counts"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ภาษาโปรแกรมมิ่งที่ verbose — มี boilerplate เยอะ, มี formatting elements มาก — ทำให้ token count สูงขึ้นโดยไม่จำเป็น&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ตัวเลข:&lt;/strong&gt; Token Sugar สามารถลด token ได้ &lt;strong&gt;15.1%&lt;/strong&gt; ใน source code และ &lt;strong&gt;11.2%&lt;/strong&gt; ระหว่าง generation — โดย &lt;strong&gt;คงค่า Pass@1 เท่าเดิม&lt;/strong&gt; — แปลว่าที่ลดไปไม่ใช่เนื้อหาสำคัญ แต่มันคือ "ขยะ" ทาง syntax ที่ LLM ต้อง generate โดยเปล่าประโยชน์&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;นัยสำคัญ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nl"&gt;Java:&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nl"&gt;Go:&lt;/span&gt;   &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;Java:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;span class="nl"&gt;Go:&lt;/span&gt;   &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;ฟังก์ชันเดียวกัน&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="nc"&gt;Go&lt;/span&gt; &lt;span class="n"&gt;ใช้&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;น้อยกว่าครึ่ง&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทุก token ที่ประหยัดได้ = เงินที่ประหยัดได้ = context window ที่เหลือไว้ให้ logic จริง&lt;/p&gt;




&lt;h2&gt;
  
  
  📄 งานวิจัยที่ 2: Babbling Suppression — "Java พล่ามมากกว่า Python"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Babbling Suppression: Making LLMs Greener One Token at a Time&lt;/strong&gt; (2026)&lt;br&gt;
โดย Lola Solovyeva, Fernando Castor — &lt;a href="https://arxiv.org/abs/2604.06755" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2604.06755&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อค้นพบ:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Babbling occurs across all tested models, with higher frequency in Java than in Python"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Babbling" (การพูดพล่าม) คือการที่ LLM สร้าง output ที่มากเกินจำเป็น — เช่น generate getter/setter ทั้งคลาสทั้งที่ขอแค่ method เดียว, ใส่ import ที่ไม่ได้ใช้, comment ยาวเกินเหตุ&lt;/p&gt;

&lt;p&gt;งานวิจัยนี้พบว่า &lt;strong&gt;Java เกิด babbling มากกว่า Python อย่างมีนัยสำคัญ&lt;/strong&gt; — และลด energy consumption ได้มากถึง &lt;strong&gt;65% สำหรับ Python, 62% สำหรับ Java&lt;/strong&gt; โดยการ suppress babbling&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ทำไม Java ถึงพล่ามมากกว่า?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;           &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;บรรทัด&lt;/span&gt;
&lt;span class="n"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;         &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="n"&gt;ต&lt;/span&gt;&lt;span class="err"&gt;้&lt;/span&gt;&lt;span class="n"&gt;องมี&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bracket&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java บังคับให้ LLM ต้อง "พูด" เยอะกว่าเพื่อแสดงความตั้งใจเดียวกัน — ยิ่งต้องพูดเยอะ = ยิ่งมีโอกาสพูดผิด&lt;/p&gt;




&lt;h2&gt;
  
  
  📄 งานวิจัยที่ 3: MultiPL-E — "ภาษาไหน LLM เก่งสุด?"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MultiPL-E: A Scalable and Extensible Approach to Benchmarking Neural Code Generation&lt;/strong&gt; (2022)&lt;br&gt;
โดย Federico Cassano และคณะ — &lt;a href="https://arxiv.org/abs/2208.08227" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2208.08227&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อค้นพบ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สร้าง benchmark ที่ทดสอบ LLM ข้าม &lt;strong&gt;18+ ภาษา&lt;/strong&gt; พร้อมกัน&lt;/li&gt;
&lt;li&gt;พบว่า &lt;strong&gt;"language frequency"&lt;/strong&gt; (ปริมาณ training data ของภาษานั้น) เป็นปัจจัยอันดับ 1&lt;/li&gt;
&lt;li&gt;ภาษาใกล้เคียง Python มักทำคะแนนได้ดี (เพราะ Python มี training data มหาศาล)&lt;/li&gt;
&lt;li&gt;ภาษาที่ training data น้อย → คะแนนต่ำ แม้ syntax จะเรียบง่าย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;สรุปจาก MultiPL-E:&lt;/strong&gt; Syntax ง่าย ≠ AI เก่งเสมอไป — ถ้าภาษานั้นไม่มี training data — AI ก็ทำไม่ได้&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Recency caveat:&lt;/strong&gt; MultiPL-E (2022) ใช้ Codex/CodeGen/InCoder — โมเดล pre-2024 — ปัจจุบัน coding agent ใช้ test-execution feedback loop และ RAG ซึ่งอาจลดผลของตัวภาษาลงจากที่ paper วัดไว้ — ข้อสรุปจากงานนี้จึงอาจ conservative เมื่อเทียบกับความสามารถของโมเดลปัจจุบัน&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📊 สรุป — 3 ปัจจัยที่ตัดสินว่า AI จะเก่งกับภาษาไหน
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;อันดับ&lt;/th&gt;
&lt;th&gt;ปัจจัย&lt;/th&gt;
&lt;th&gt;หลักฐาน&lt;/th&gt;
&lt;th&gt;น้ำหนัก&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🥇&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Training Data&lt;/strong&gt; — ภาษานั้นมีโค้ดบน GitHub เยอะแค่ไหน&lt;/td&gt;
&lt;td&gt;MultiPL-E (cross-language, 18+ languages)&lt;/td&gt;
&lt;td&gt;✅ หนักแน่น&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥈&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Verbosity&lt;/strong&gt; — ภาษานั้น verbose แค่ไหน (กระทบ token cost)&lt;/td&gt;
&lt;td&gt;Token Sugar (ไม่ใช่ cross-language)&lt;/td&gt;
&lt;td&gt;⚠️ circumstantial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥉&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Syntax Complexity&lt;/strong&gt; — ซับซ้อน → babbling มากขึ้น&lt;/td&gt;
&lt;td&gt;Babbling Suppression (2 ภาษาเท่านั้น)&lt;/td&gt;
&lt;td&gt;⚠️ circumstantial&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;หมายเหตุ: MultiPL-E เป็นงานเดียวที่ทดสอบข้ามภาษาโดยตรง — อีก 2 paper เป็นหลักฐานแวดล้อม (ไม่ cross-language / ทดสอบแค่ 2 ภาษา) — generalization ไปยังภาษาอื่นเป็นการ extrapolate ของผู้เขียน&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  ภาพรวม — จัดกลุ่มภาษา
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;นี่คือการตีความของผู้เขียน — ไม่ใช่ finding จากงานวิจัยโดยตรง:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;กลุ่ม&lt;/th&gt;
&lt;th&gt;ภาษา&lt;/th&gt;
&lt;th&gt;AI เก่ง?&lt;/th&gt;
&lt;th&gt;Token cost&lt;/th&gt;
&lt;th&gt;Babbling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🟢 &lt;strong&gt;Sweet Spot&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Python, JavaScript, TypeScript, Go&lt;/td&gt;
&lt;td&gt;✅ เก่ง&lt;/td&gt;
&lt;td&gt;ต่ำ&lt;/td&gt;
&lt;td&gt;น้อย&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🟡 &lt;strong&gt;พอใช้ได้&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Ruby, Rust, Kotlin, Swift&lt;/td&gt;
&lt;td&gt;⚠️ ปานกลาง&lt;/td&gt;
&lt;td&gt;กลาง&lt;/td&gt;
&lt;td&gt;กลาง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔴 &lt;strong&gt;ท้าทาย&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Java, C++, C#, PHP&lt;/td&gt;
&lt;td&gt;⚠️ เก่งในเรื่องทั่วไป แต่ verbose&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;มาก&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Go อยู่ในกลุ่ม Sweet Spot&lt;/strong&gt; — syntax เรียบง่ายเหมือน Python — token cost ต่ำ — babbling น้อย — ทำให้ LLM ทำงานกับ Go ได้ดีเทียบเคียง Python&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🎯 คำตอบสุดท้าย
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"ภาษา syntax ง่ายทำให้ AI หลอนน้อยลง จริงไหม?"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ จริง — แต่แค่ส่วนนึงของสมการ

ปัจจัยอันดับ 1 คือ training data
ปัจจัยอันดับ 2 คือ verbosity (กระทบ token cost)
ปัจจัยอันดับ 3 คือ syntax complexity (กระทบ babbling)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ภาษาที่ "ใช่" สำหรับ AI-assisted coding คือภาษาที่:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;มี training data มาก → AI แม่น&lt;/li&gt;
&lt;li&gt;syntax กระชับ → ประหยัด token&lt;/li&gt;
&lt;li&gt;ไม่บังคับ boilerplate → AI ไม่ต้องพล่าม&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Python ชนะขาดเรื่อง training data — Go ชนะเรื่องความกระชับ — JavaScript/TypeScript สมดุลทั้งสองด้าน — Java/C++ เก่งในเชิงความสามารถ แต่แพ้เรื่อง token cost&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2512.08266" rel="noopener noreferrer"&gt;Token Sugar (ASE 2025)&lt;/a&gt; — verbosity → token inefficiency&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2604.06755" rel="noopener noreferrer"&gt;Babbling Suppression (2026)&lt;/a&gt; — Java babbles more than Python&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2208.08227" rel="noopener noreferrer"&gt;MultiPL-E (2022)&lt;/a&gt; — cross-language LLM benchmark&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.blog/ai-and-ml/github-copilot/evaluating-performance-and-efficiency-of-the-github-copilot-agentic-harness-across-models-and-tasks/" rel="noopener noreferrer"&gt;GitHub Copilot: Token Efficiency&lt;/a&gt; — June 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>research</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Null, nil, None — คำว่า 'ไม่มีค่า' ในแต่ละภาษา</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:20:00 +0000</pubDate>
      <link>https://dev.to/gophernment/null-nil-none-khamwaa-aimmiikhaa-ainaetlaphaasaa-b6m</link>
      <guid>https://dev.to/gophernment/null-nil-none-khamwaa-aimmiikhaa-ainaetlaphaasaa-b6m</guid>
      <description>&lt;h2&gt;
  
  
  Null, nil, None — คำว่า "ไม่มีค่า" ในแต่ละภาษา
&lt;/h2&gt;

&lt;p&gt;เวลาเราเขียนโปรแกรม — วันนึงเราจะต้องเจอ "การไม่มีค่า"&lt;/p&gt;

&lt;p&gt;บางภาษามันคือ &lt;code&gt;null&lt;/code&gt; บางภาษามันคือ &lt;code&gt;nil&lt;/code&gt; บางภาษามันคือ &lt;code&gt;None&lt;/code&gt; — แต่มันคือสิ่งเดียวกันใช่ไหม?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ไม่ใช่&lt;/strong&gt; — และความต่างเล็ก ๆ นี่แหละที่ทำให้โปรแกรมพังได้&lt;/p&gt;




&lt;h2&gt;
  
  
  ภาพรวม — แต่ละภาษาเรียกชื่ออะไร
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ภาษา&lt;/th&gt;
&lt;th&gt;ชื่อเรียก&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String s = null;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;null&lt;/code&gt; + &lt;code&gt;undefined&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;let x = null; let y;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x = None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nil&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x = nil&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C/C++&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;NULL&lt;/code&gt; / &lt;code&gt;nullptr&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int* p = NULL;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nil&lt;/code&gt; + &lt;strong&gt;zero values&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;var s *string = nil&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;เห็นว่า Ruby กับ Go ใช้ &lt;code&gt;nil&lt;/code&gt; เหมือนกัน — แต่เบื้องหลังต่างกันโดยสิ้นเชิง&lt;/p&gt;




&lt;h2&gt;
  
  
  Java — &lt;code&gt;null&lt;/code&gt; คือตัวปัญหา
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// 💥 NullPointerException!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Java — &lt;code&gt;null&lt;/code&gt; คือ &lt;strong&gt;"การไม่มี object"&lt;/strong&gt; — และทุกครั้งที่คุณเรียก method บน &lt;code&gt;null&lt;/code&gt; — โปรแกรมพัง&lt;/p&gt;

&lt;p&gt;Java แก้ด้วยการเช็ค &lt;code&gt;if (x != null)&lt;/code&gt; ทุกที่ — หรือใช้ &lt;code&gt;Optional&lt;/code&gt; (Java 8+) — แต่ &lt;code&gt;null&lt;/code&gt; ก็ยังแฝงตัวอยู่ทุกมุม&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java Optional — พยายามกำจัด null&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  JavaScript — มีสองแบบ ซวยสองเท่า
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// "ฉันจงใจให้มันว่าง"&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// undefined — "ฉันลืมใส่ค่า"&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// null&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// undefined&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// "object" ← bug ในภาษา!&lt;/span&gt;

&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// true  ← อ้าว?&lt;/span&gt;
&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// false ← อ้าววว?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript มีทั้ง &lt;code&gt;null&lt;/code&gt; และ &lt;code&gt;undefined&lt;/code&gt; — และการที่ &lt;code&gt;typeof null === "object"&lt;/code&gt; เป็น bug ที่ฝังมาตั้งแต่ปี 1995 — แก้ไม่ได้แล้วเพราะคนใช้กันทั้งโลก&lt;/p&gt;




&lt;h2&gt;
  
  
  Python — &lt;code&gt;None&lt;/code&gt; คือ singleton
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# True — None มีตัวเดียวในจักรวาล
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Python — &lt;code&gt;None&lt;/code&gt; เป็น &lt;strong&gt;singleton&lt;/strong&gt; — มีแค่ instance เดียวในหน่วยความจำ — &lt;code&gt;is&lt;/code&gt; ใช้เช็คได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# "ไม่เจอ"
&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ไม่พบผู้ใช้&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สะอาด ตรงไปตรงมา — ไม่มี &lt;code&gt;NullPointerException&lt;/code&gt; — แต่ก็ไม่ได้ช่วยให้ปลอดภัยขึ้น — ถ้าลืมเช็ค &lt;code&gt;None&lt;/code&gt; — โปรแกรมก็พังตอนใช้ &lt;code&gt;user.name&lt;/code&gt; อยู่ดี&lt;/p&gt;




&lt;h2&gt;
  
  
  Go — &lt;code&gt;nil&lt;/code&gt; คืออะไร? และทำไมมันถึงหลอกเรา
&lt;/h2&gt;

&lt;p&gt;Go ใช้ &lt;code&gt;nil&lt;/code&gt; — แต่ความหมายของมันเปลี่ยนไปตาม &lt;strong&gt;type&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;        &lt;span class="c"&gt;// nil pointer&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;       &lt;span class="c"&gt;// nil slice — แต่ใช้ append ได้!&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;  &lt;span class="c"&gt;// nil map — อ่านได้ แต่เขียนไม่ได้!&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;   &lt;span class="c"&gt;// nil channel — block ตลอดกาล&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;     &lt;span class="c"&gt;// nil function — เรียกแล้ว panic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;nil slice:&lt;/strong&gt; ต่างจากภาษาอื่น — คุณใช้ &lt;code&gt;append&lt;/code&gt; บน nil slice ได้!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;       &lt;span class="c"&gt;// nil slice — len=0&lt;/span&gt;
&lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ✅ ได้เลย! ไม่ต้อง make ก่อน&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;// [Alice]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;nil map:&lt;/strong&gt; อ่านได้ แต่เขียนไม่ได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;     &lt;span class="c"&gt;// nil map&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;             &lt;span class="c"&gt;// ✅ ได้ 0 (zero value)&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;             &lt;span class="c"&gt;// 💥 panic: assignment to entry in nil map&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  หลุมพรางใน Go — &lt;code&gt;nil&lt;/code&gt; interface ≠ &lt;code&gt;nil&lt;/code&gt; pointer
&lt;/h2&gt;

&lt;p&gt;นี่คือ bug ที่คนเขียน Go เจอแล้วเจออีก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;  &lt;span class="c"&gt;// ไม่มี user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUserAsInterface&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c"&gt;// return nil pointer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getUserAsInterface&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"มี error!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// ❌ มันเข้าเงื่อนไขนี้ทั้งที่ควรเป็น nil!&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ทำไม?&lt;/strong&gt; interface ใน Go ไม่ใช่แค่ "ค่า" — มันคือ &lt;strong&gt;(type, value)&lt;/strong&gt; — tuple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// true — pointer nil&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;      &lt;span class="c"&gt;// เก็บ (*User, nil) — type ไม่ใช่ nil!&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// false!?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;interface &lt;code&gt;err&lt;/code&gt; มี &lt;code&gt;type = *User&lt;/code&gt; (ไม่ใช่ nil) — แต่ &lt;code&gt;value = nil&lt;/code&gt; — interface โดยรวมจึง &lt;strong&gt;ไม่ใช่ nil&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;วิธีแก้:&lt;/strong&gt; return &lt;code&gt;nil&lt;/code&gt; แบบไม่มี type — ไม่ใช่แค่ pointer nil:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUserAsInterface&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;  &lt;span class="c"&gt;// ✅ nil แบบไม่มี type — interface nil จริง&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Zero Values — Go ไม่มี "ไม่มีค่า"
&lt;/h2&gt;

&lt;p&gt;แนวคิดที่ต่างจากภาษาอื่นมาก: &lt;strong&gt;Go ไม่มี uninitialized variable&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;        &lt;span class="c"&gt;// 0&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;     &lt;span class="c"&gt;// "" (empty string — ไม่ใช่ nil!)&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;       &lt;span class="c"&gt;// false&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="c"&gt;// nil (แต่ type ชัดเจนว่าเป็น *int)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Java/Python/JS — ตัวแปรที่ยังไม่ assign ค่าอาจเป็น &lt;code&gt;null&lt;/code&gt;/&lt;code&gt;None&lt;/code&gt;/&lt;code&gt;undefined&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ใน Go — ทุกอย่างมี &lt;strong&gt;zero value&lt;/strong&gt; — ค่าเริ่มต้นของ type นั้น — คุณไม่ต้องกลัวว่าจะลืม initialize&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;  &lt;span class="c"&gt;// "" (ไม่ใช่ nil)&lt;/span&gt;
    &lt;span class="n"&gt;Age&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;     &lt;span class="c"&gt;// 0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// "" — ไม่พัง!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เปรียบเทียบกับ Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// null!&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// 💥 NullPointerException!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  เทียบกัน — ภาษาไหนชนะ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;null error&lt;/th&gt;
&lt;th&gt;zero value&lt;/th&gt;
&lt;th&gt;nil slice ใช้ได้&lt;/th&gt;
&lt;th&gt;interface nil trap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ (แต่มี interface trap)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ หลุมพราง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💥 NullPointerException&lt;/td&gt;
&lt;td&gt;❌ (Object = null)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💥 undefined + null&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💥 AttributeError&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💥 NoMethodError&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ภาษา&lt;/th&gt;
&lt;th&gt;ชื่อ&lt;/th&gt;
&lt;th&gt;จุดอ่อน&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;NullPointerException ทุกที่ — &lt;code&gt;Optional&lt;/code&gt; ช่วยได้นิดหน่อย&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;สองแบบ — &lt;code&gt;typeof null === "object"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;สะอาดสุด — แต่ต้องเช็ค &lt;code&gt;is None&lt;/code&gt; เอง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nil&lt;/code&gt; + zero values&lt;/td&gt;
&lt;td&gt;ปลอดภัยกว่า — แต่ interface nil trap หลอกได้&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;หัวใจของ Go:&lt;/strong&gt; ตัวแปรทุกตัวมีค่าเสมอ — แม้จะเป็น "ค่าเริ่มต้น" — คุณจะไม่เจอ NullPointerException ใน Go — แต่คุณจะเจอ &lt;strong&gt;"interface nil trap"&lt;/strong&gt; แทน&lt;/p&gt;

&lt;p&gt;และนั่นคือบทเรียนที่คนเขียน Go ทุกคนต้องผ่าน — &lt;code&gt;nil&lt;/code&gt; กับ &lt;code&gt;nil&lt;/code&gt; มันไม่เหมือนกัน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/faq#nil_error" rel="noopener noreferrer"&gt;Go FAQ: Why is my nil error value not equal to nil?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.dev/tour/methods/12" rel="noopener noreferrer"&gt;Understanding nil in Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>ทำไม Go ถึง compile ไว — เล่าจากห้องเซิร์ฟเวอร์ Google ที่ร้อนระอุ</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Mon, 06 Jul 2026 06:27:28 +0000</pubDate>
      <link>https://dev.to/gophernment/thamaim-go-thueng-compile-aiw-elaacchaakhngechirfewr-google-thiirnrau-72o</link>
      <guid>https://dev.to/gophernment/thamaim-go-thueng-compile-aiw-elaacchaakhngechirfewr-google-thiirnrau-72o</guid>
      <description>&lt;h2&gt;
  
  
  ทำไม Go ถึง compile ไว — เล่าจากห้องเซิร์ฟเวอร์ Google ที่ร้อนระอุ
&lt;/h2&gt;

&lt;p&gt;ปี 2007 — ห้องเซิร์ฟเวอร์ของ Google ร้อนจนแทบละลาย&lt;/p&gt;

&lt;p&gt;ไม่ใช่เพราะอากาศ — แต่เพราะ &lt;strong&gt;คอมไพเลอร์ C++&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;นักพัฒนาที่ Google สามคน — &lt;strong&gt;Robert Griesemer, Rob Pike, และ Ken Thompson&lt;/strong&gt; — กำลังรอโค้ด compile&lt;/p&gt;

&lt;p&gt;Ken Thompson คือใคร? เขาคือคนที่สร้าง &lt;strong&gt;Unix&lt;/strong&gt;, สร้าง &lt;strong&gt;ภาษา B&lt;/strong&gt; (บรรพบุรุษของ C), ร่วมคิดค้น &lt;strong&gt;UTF-8&lt;/strong&gt;, และได้รับ &lt;strong&gt;Turing Award&lt;/strong&gt; — รางวัลโนเบลของวงการคอมพิวเตอร์&lt;/p&gt;

&lt;p&gt;สามคนนี้... นั่งรอโค้ด compile เป็นสิบนาที&lt;/p&gt;

&lt;p&gt;Pike เล่าในภายหลังว่า: "เราเบื่อที่จะรอ — ก็เลยสร้างภาษาใหม่"&lt;/p&gt;




&lt;h2&gt;
  
  
  เรื่องเล่าก่อน Go — ทำไม C++ ที่ Google ถึง compile ช้า
&lt;/h2&gt;

&lt;p&gt;ลองนึกภาพ: Google มีโค้ด C++ มหาศาล — หนึ่งไฟล์ &lt;code&gt;#include&lt;/code&gt; อีกไฟล์ — &lt;code&gt;#include&lt;/code&gt; ซ้อน &lt;code&gt;#include&lt;/code&gt; — กลายเป็น chain ยาวเป็นกิโลเมตร&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.cc&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"server.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// server.h&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"handler.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// handler.h&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"request.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// request.h&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"database.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// database.h — #include อีก 20 ไฟล์...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เปลี่ยนโค้ด 1 บรรทัดใน &lt;code&gt;database.h&lt;/code&gt; → ทุกไฟล์ที่ &lt;code&gt;#include&lt;/code&gt; chain มาถึงต้อง compile ใหม่หมด → &lt;strong&gt;เป็นชั่วโมง&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;และอย่าลืม — C++ มี template, มี preprocessor, มี header files, มี forward declaration, มี ODR (One Definition Rule) — ทุกอย่างทำให้ compiler ต้องทำงานหนักขึ้น&lt;/p&gt;

&lt;p&gt;สามคนนี้ตัดสินใจว่า: &lt;strong&gt;"ภาษาใหม่ต้อง compile ไว — มาก"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 1: ไม่มี Header Files — อ่านตรง ๆ
&lt;/h2&gt;

&lt;p&gt;Go ไม่มี &lt;code&gt;.h&lt;/code&gt; ไฟล์ — compiler อ่าน &lt;code&gt;.go&lt;/code&gt; โดยตรง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go — import อย่างเดียว ไม่มี include&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;compiler รู้ว่า package &lt;code&gt;net/http&lt;/code&gt; export อะไรบ้าง — อ่านครั้งเดียว — จบ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C++ ต้องทำอะไร?&lt;/strong&gt; compiler อ่าน &lt;code&gt;header.h&lt;/code&gt; → preprocessor ไล่ &lt;code&gt;#include&lt;/code&gt; → ต่อ chain → อ่านซ้ำ 20 รอบ — source file เดียวอาจใหญ่เป็นแสนบรรทัดก่อนถึง compiler จริง&lt;/p&gt;

&lt;p&gt;Go: 1 ไฟล์ = 1 ไฟล์ — compiler รู้ทันทีว่าต้องอ่านอะไร&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 2: Dependency Graph — ห้ามวกกลับมาที่เดิม
&lt;/h2&gt;

&lt;p&gt;Go ออกแบบ dependency เป็น &lt;strong&gt;DAG (Directed Acyclic Graph)&lt;/strong&gt; — ห้าม circular import&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ ถูก — dependency เป็น tree&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;

&lt;span class="c"&gt;// ❌ ผิด — circular — compile ไม่ผ่าน&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;  &lt;span class="c"&gt;// compile error!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ทำไมถึงเร็ว?&lt;/strong&gt; ถ้า dependency เป็น DAG — compiler สามารถ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เรียงลำดับการ compile ได้ — compile C ก่อน, แล้ว B, แล้ว A&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ทำ parallel ได้&lt;/strong&gt; — compile C, B, A พร้อมกันถ้าไม่ขึ้นต่อกัน&lt;/li&gt;
&lt;li&gt;รู้ว่าไฟล์ไหนต้อง compile ใหม่ — เปลี่ยน C อย่างเดียว → A, B ไม่ต้อง compile ใหม่ถ้า interface ไม่เปลี่ยน&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go ใช้ DAG ตั้งแต่ดีไซน์ — ไม่ใช่ afterthought&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 3: Import ที่ไม่ได้ใช้ = Error
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"os"&lt;/span&gt;  &lt;span class="c"&gt;// ❌ compile error: imported and not used&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ฟังดูน่ารำคาญ — แต่มันมีเหตุผล: &lt;strong&gt;compiler ไม่ต้องเสียเวลาอ่าน package ที่คุณ import มาแต่เปล่าประโยชน์&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ใน C++ — คุณ &lt;code&gt;#include &amp;lt;vector&amp;gt;&lt;/code&gt; แล้วไม่ได้ใช้ — compiler ยังต้องอ่านทั้งไฟล์ — สิ้นเปลือง&lt;/p&gt;

&lt;p&gt;Go: &lt;code&gt;goimports&lt;/code&gt; จัดการให้ — import เอง, ลบเอง — คุณไม่ต้องคิด&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 4: Object Files อัจฉริยะ — metadata อยู่ส่วนหัว
&lt;/h2&gt;

&lt;p&gt;เวลา Go compile package — มันสร้าง &lt;code&gt;.a&lt;/code&gt; (object file) ที่ &lt;strong&gt;สมบูรณ์ในตัวเอง&lt;/strong&gt; — และสิ่งที่สำคัญคือการจัดเรียงข้อมูล&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;math.a:
  ┌─────────────────────────────┐
  │ METADATA (ส่วนหัว)            │
  │  - รายชื่อ symbols ที่ export   │
  │  - รายชื่อ dependencies       │
  │  - type information (สำหรับ  │
  │    reflection)              │
  ├─────────────────────────────┤
  │ MACHINE CODE (ส่วนท้าย)       │
  │  - โค้ดจริงของ package math   │
  └─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ประเด็นสำคัญ:&lt;/strong&gt; Metadata อยู่ที่ &lt;strong&gt;ส่วนหัวของไฟล์&lt;/strong&gt; — เวลา package B import math — compiler อ่านแค่ metadata (ไม่กี่ KB) — ไม่ต้อง parse source code ทั้งหมด (หลายพันบรรทัด) — ไม่ต้อง re-type-check&lt;/p&gt;

&lt;p&gt;เปรียบเทียบกับ C++:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C++ — ทุกครั้งที่ compile main.cc&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"math.h"&lt;/span&gt;&lt;span class="c1"&gt;   // ← ต้องเปิด math.h → parse → type-check → expand&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;                    &lt;span class="c1"&gt;//    math.h อาจ include math_impl.h → parse → expand&lt;/span&gt;
                    &lt;span class="c1"&gt;//    math_impl.h อาจ include vector → parse → expand&lt;/span&gt;
                    &lt;span class="c1"&gt;//    ทั้งหมดนี้เกิดขึ้นซ้ำในทุก .cc ที่ใช้ math.h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Go:&lt;/strong&gt; อ่าน metadata ครั้งเดียวต่อ package — &lt;strong&gt;C++:&lt;/strong&gt; parse headers ซ้ำในทุก translation unit&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 ลองนึกภาพมี 100 ไฟล์ import &lt;code&gt;net/http&lt;/code&gt; — C++ parse header ซ้ำ 100 รอบ — Go อ่าน metadata ของ &lt;code&gt;net/http.a&lt;/code&gt; 100 รอบ แต่การอ่าน metadata (ไม่กี่ KB) เร็วกว่า parse source (หลายพันบรรทัด) หลายเท่า&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ข้อที่ 5: Plan 9 Assembly — "ภาษาเดียว ทุก CPU"
&lt;/h2&gt;

&lt;p&gt;Go ใช้ &lt;strong&gt;Plan 9 assembly&lt;/strong&gt; — assembly language ที่ออกแบบโดยทีมเดียวกับ Unix — Ken Thompson เป็นหนึ่งในนั้น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Plan 9 assembly ของ Go&lt;/span&gt;
&lt;span class="n"&gt;TEXT&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SB&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;NOSPLIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;
    &lt;span class="n"&gt;MOVQ&lt;/span&gt;    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;AX&lt;/span&gt;
    &lt;span class="n"&gt;ADDQ&lt;/span&gt;    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;AX&lt;/span&gt;
    &lt;span class="n"&gt;MOVQ&lt;/span&gt;    &lt;span class="n"&gt;AX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;RET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  แล้วมันต่างจากภาษาอื่นยังไง?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;C/C++:&lt;/strong&gt; compiler ใช้ assembly ของ CPU นั้นโดยตรง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// x86 assembly → C compiler สร้าง
add eax, ebx     ← ใช้ได้บน x86 เท่านั้น

// ARM assembly → คนละโลก
add r0, r0, r1   ← ใช้ได้บน ARM เท่านั้น
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;การจะ cross-compile C — คุณต้องมี &lt;strong&gt;cross-compiler toolchain&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux → ARM: ต้องติดตั้ง &lt;code&gt;arm-linux-gnueabihf-gcc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;macOS → Windows: ต้องติดตั้ง &lt;code&gt;x86_64-w64-mingw32-gcc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;แต่ละคู่ platform = toolchain ใหม่ — ติดตั้งครั้งละเป็นชั่วโมง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rust:&lt;/strong&gt; ใช้ LLVM เป็น backend — LLVM มี IR (Intermediate Representation) ของตัวเอง — ข้อดีคือ LLVM optimize เก่งมาก — แต่ LLVM เองเป็นโค้ด C++ มหาศาล — หนัก ช้า&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go เลือกทางสายกลาง:&lt;/strong&gt; เขียน compiler backend เองทั้งหมด — ไม่ใช้ LLVM, ไม่ใช้ GCC, ไม่ใช้ toolchain ภายนอกเลยแม้แต่นิดเดียว&lt;/p&gt;

&lt;h3&gt;
  
  
  Mapping — แผนที่
&lt;/h3&gt;

&lt;p&gt;Go เขียนโค้ด → คอมไพล์เป็น &lt;strong&gt;Plan 9 assembly&lt;/strong&gt; (IR กลาง) → แล้วคอมไพล์ต่อเป็น machine code โดยตรง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;write.go (Go source)
    │
    ▼
[Go compiler frontend] → parse, type-check
    │
    ▼
Plan 9 assembly (IR กลาง — architecture-independent)
    │
    ├──→ [x86-64 backend]  → MOVQ, ADDQ, RET (Intel/AMD)
    ├──→ [ARM64 backend]   → MOV, ADD, RET   (Apple Silicon, Pi)
    ├──→ [ARM backend]     → MOV, ADD, RET   (Pi 32-bit)
    ├──→ [RISC-V backend]  → ...              (SiFive, etc.)
    └──→ [MIPS, s390x, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทุก backend อยู่ใน Go compiler เรียบร้อย — ไม่ต้องติดตั้งอะไรเพิ่ม&lt;/p&gt;

&lt;h3&gt;
  
  
  เปรียบเทียบ 3 ภาษา — ใครต้อง setup อะไรบ้าง
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Go&lt;/th&gt;
&lt;th&gt;Rust&lt;/th&gt;
&lt;th&gt;C/C++&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-compile ไป ARM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GOARCH=arm go build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rustup target add&lt;/code&gt; + linker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apt install cross-gcc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;เขียนเอง (Go)&lt;/td&gt;
&lt;td&gt;LLVM (C++)&lt;/td&gt;
&lt;td&gt;GCC/LLVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ขนาด toolchain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~100MB&lt;/td&gt;
&lt;td&gt;~2GB+&lt;/td&gt;
&lt;td&gt;~500MB-2GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เวลา setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0 (ติด Go แล้ว cross-compile ได้เลย)&lt;/td&gt;
&lt;td&gt;5-10 นาที (add target)&lt;/td&gt;
&lt;td&gt;1-3 ชม (หา toolchain, build)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;ตัวอย่างจริง — ผมเคย cross-compile Go ให้ Raspberry Pi:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# บน Mac — สร้าง binary สำหรับ Raspberry Pi&lt;/span&gt;
&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; myapp-pi &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# copy ไป Pi&lt;/span&gt;
scp myapp-pi pi@raspberrypi:~

&lt;span class="c"&gt;# รัน — ใช้ได้ทันที&lt;/span&gt;
ssh pi@raspberrypi ./myapp-pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 บรรทัด — ไม่ต้องหา ARM cross-compiler — ไม่ต้องติดตั้ง sysroot — เพราะ Go compiler มี ARM backend อยู่แล้ว — มันรู้ว่า ARM ใช้ instruction อะไร, register ชื่ออะไร, calling convention เป็นยังไง — ทุกอย่าง built-in&lt;/p&gt;

&lt;p&gt;นี่คือเหตุผลที่ Go เป็นราชาแห่ง cross-compilation — ไม่ใช่เพราะมันมีฟีเจอร์วิเศษ — แต่เพราะมันไม่พึ่งพาใครเลย&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 6: Static Linking — ไม่ต้องพึ่งระบบ
&lt;/h2&gt;

&lt;p&gt;Go compile เป็น &lt;strong&gt;static binary&lt;/strong&gt; — รวมทุกอย่างไว้ในไฟล์เดียว:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; myapp
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 8.2M myapp    &lt;span class="c"&gt;# ← 8MB — ทั้ง app ในไฟล์เดียว&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;ldd myapp
not a dynamic executable  &lt;span class="c"&gt;# ← ไม่พึ่งพา .so ใด ๆ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C/C++ ต้องทำอะไร?&lt;/strong&gt; compile เป็น binary → ต้องมี &lt;code&gt;libc.so&lt;/code&gt;, &lt;code&gt;libstdc++.so&lt;/code&gt;, &lt;code&gt;libpthread.so&lt;/code&gt; ฯลฯ — เปลี่ยน distro → พัง&lt;/p&gt;

&lt;p&gt;Go: binary เดียว — copy ไปเครื่องไหนก็รันได้ — &lt;strong&gt;compile บน Mac, deploy บน Linux, รันบน Raspberry Pi&lt;/strong&gt; — ง่ายมาก&lt;/p&gt;




&lt;h2&gt;
  
  
  ข้อที่ 7: Compiler เขียนด้วย Go — เร็วขึ้นเรื่อย ๆ
&lt;/h2&gt;

&lt;p&gt;Go compiler ยุคแรกเขียนด้วย C — แต่ตั้งแต่ Go 1.5 (2015) — compiler &lt;strong&gt;เขียนด้วย Go เอง&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;นี่คือ "bootstrapping" — Go compile ตัวเอง — และทุกครั้งที่ Go เวอร์ชันใหม่เร็วขึ้น — compiler ก็เร็วขึ้นตาม&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Go 1.0 (2012): compiler เขียนด้วย C
Go 1.5 (2015): compiler เขียนด้วย Go — ช้าลงเล็กน้อย
Go 1.7 (2016): compiler เร็วขึ้น 20-30% ด้วย SSA backend
Go 1.9 (2017): compiler เร็วขึ้นอีก
...
Go 1.22 (2024): compiler แทบจะ instantaneous สำหรับโปรเจกต์ขนาดกลาง
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ข้อที่ 8: Grammar Simplicity — อ่านโค้ดรอบเดียวจบ
&lt;/h2&gt;

&lt;p&gt;Go แตกต่างจาก C++ ตรงที่ &lt;strong&gt;ไวยากรณ์ถูกออกแบบมาเพื่อ compiler — ไม่ใช่เพื่อโปรแกรมเมอร์ที่ชอบความซับซ้อน&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hand-Written Recursive Descent — parser ที่มนุษย์เขียนเอง
&lt;/h3&gt;

&lt;p&gt;Go ใช้ &lt;strong&gt;hand-written recursive descent parser&lt;/strong&gt; — คือ parser ที่เขียนด้วยมือ ไม่ได้ generate จาก grammar (ต่างจาก yacc/bison ที่ใช้ใน C compiler หลายตัว)&lt;/p&gt;

&lt;p&gt;การเขียน parser ด้วยมือมีข้อดี: compiler ควบคุมทุกอย่างได้ — ไม่มี overhead จาก table-driven parsing — และ error message อ่านรู้เรื่องเพราะมนุษย์เขียนเอง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📜 &lt;strong&gt;เกร็ดประวัติศาสตร์:&lt;/strong&gt; Go 1.0-1.4 ใช้ yacc (ซึ่งใช้ LALR(1)) สำหรับบางส่วนของ parser — แต่ตั้งแต่ Go 1.5 ที่ compiler ถูก rewrite ด้วย Go ทั้งหมด — parser เปลี่ยนเป็น hand-written recursive descent เต็มตัว&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;แต่ไม่ว่าจะใช้ yacc หรือ recursive descent — หัวใจสำคัญคือ Go grammar ถูกออกแบบให้ &lt;strong&gt;ง่ายพอที่ parser แบบไหนก็ทำงานได้ใน single pass&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ทุกประโยคใน Go — compiler รู้ทันทีว่าคืออะไร&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    &lt;span class="c"&gt;// ← function declaration — ไม่ใช่อย่างอื่น&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c"&gt;// ← short variable declaration — ชัดเจน&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;// ← variable declaration — var นำหน้า&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;compiler ไม่ต้องถามว่า "นี่คือ function call หรือ variable declaration?" — เพราะ syntax บังคับให้เดาไม่ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  C++: Most Vexing Parse — compiler เองก็สับสน
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C++ — Most Vexing Parse: compiler ไม่รู้ว่านี่คืออะไร&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;w&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❓ function declaration (คืนค่า Widget) — หรือ variable declaration?&lt;/span&gt;
             &lt;span class="c1"&gt;// คำตอบ: มันคือ function declaration! (Most Vexing Parse)&lt;/span&gt;

&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ variable declaration — ใช้ {} แทน&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นี่คือตัวอย่างของ &lt;strong&gt;context-sensitive grammar&lt;/strong&gt; ใน C++ — compiler ต้องดูบริบทว่ามี type ชื่อ &lt;code&gt;Widget&lt;/code&gt; ไหม — มี constructor แบบไหน — มันคือ function หรือ variable — ทั้งหมดนี้ใช้เวลา CPU มหาศาล&lt;/p&gt;

&lt;h3&gt;
  
  
  ตารางเทียบ — อะไรทำให้ Go ง่าย C++ ยาก
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Go&lt;/th&gt;
&lt;th&gt;C++&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grammar complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ออกแบบให้ unambiguous — parser เลือกใช้ได้ (yacc หรือ recursive descent)&lt;/td&gt;
&lt;td&gt;Context-sensitive — parser ต้องเดาจากบริบท&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parsing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single-pass — รอบเดียวจบ&lt;/td&gt;
&lt;td&gt;Multiple-pass + template instantiation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preprocessor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ ไม่มี&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;#include&lt;/code&gt;, &lt;code&gt;#define&lt;/code&gt;, &lt;code&gt;#ifdef&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forward declaration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ ไม่ต้อง — order อิสระ&lt;/td&gt;
&lt;td&gt;✅ ต้อง — &lt;code&gt;class A;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Template&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ (generics 1.18+ เป็นคนละเรื่อง)&lt;/td&gt;
&lt;td&gt;✅ template metaprogramming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compile-time computation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;constexpr&lt;/code&gt;, SFINAE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ทำไม Go ถึงไม่มี preprocessor?
&lt;/h3&gt;

&lt;p&gt;Preprocessor ใน C/C++ ทำให้ compiler ต้องทำงาน 2 รอบ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;preprocessor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;define&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทุกไฟล์ &lt;code&gt;.c&lt;/code&gt; ผ่าน preprocessor → ขยายเป็นไฟล์ใหญ่ยักษ์ → ส่งให้ compiler จริง — นี่คือ 2x งาน&lt;/p&gt;

&lt;p&gt;Go: source code → compiler โดยตรง — ไม่มีขั้นกลาง — &lt;strong&gt;1 pass, 1 job&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ผลลัพธ์ในโลกจริง
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kubernetes (Go):      ~3 ล้านบรรทัด — compile ทั้งโปรเจกต์ใน ~2 นาที
Chromium (C++):      ~35 ล้านบรรทัด — compile ใช้เวลา 30-60 นาที (แม้ใช้ distributed build!)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สัดส่วน: Kubernetes มีโค้ด 1/10 ของ Chromium — แต่ compile เร็วกว่า 15-30 เท่า — ไม่ใช่แค่เพราะขนาดเล็กกว่า — แต่เพราะ Go compiler ถูกออกแบบมาให้ "เร็ว" ตั้งแต่แรก&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 สรุป — อะไรทำให้ Go compile ไว
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ปัจจัย&lt;/th&gt;
&lt;th&gt;ทำอะไร&lt;/th&gt;
&lt;th&gt;ทำไมถึงเร็ว&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ไม่มี Header Files&lt;/td&gt;
&lt;td&gt;อ่าน &lt;code&gt;.go&lt;/code&gt; ตรง&lt;/td&gt;
&lt;td&gt;compiler ไม่ต้องไล่ &lt;code&gt;#include&lt;/code&gt; chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAG Dependency&lt;/td&gt;
&lt;td&gt;ห้าม circular import&lt;/td&gt;
&lt;td&gt;เรียงลำดับ compile ได้, parallel ได้&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unused Import = Error&lt;/td&gt;
&lt;td&gt;compiler ไม่โหลด package ที่ไม่ใช้&lt;/td&gt;
&lt;td&gt;ลดงาน compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Files อัจฉริยะ&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.a&lt;/code&gt; มีข้อมูลครบ&lt;/td&gt;
&lt;td&gt;ไม่ต้องเปิด source หา dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan 9 Assembly&lt;/td&gt;
&lt;td&gt;IR กลาง&lt;/td&gt;
&lt;td&gt;cross-compile ได้ทันที โดยไม่ต้อง toolchain ใหม่&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static Linking&lt;/td&gt;
&lt;td&gt;binary ในไฟล์เดียว&lt;/td&gt;
&lt;td&gt;deploy ง่าย ไม่พึ่งระบบ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compiler เขียนด้วย Go&lt;/td&gt;
&lt;td&gt;bootstrapping&lt;/td&gt;
&lt;td&gt;compiler เร็วขึ้นทุกเวอร์ชัน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple Grammar&lt;/td&gt;
&lt;td&gt;hand-written parser, ไม่มี preprocessor, single-pass&lt;/td&gt;
&lt;td&gt;ไม่ต้อง parse ซ้ำ, ไม่คลุมเครือ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 ทำไมเรื่องนี้ถึงสำคัญ
&lt;/h2&gt;

&lt;p&gt;ไม่ใช่แค่ "compile ไว = สะดวก" — แต่มันเปลี่ยน &lt;strong&gt;วิธีคิด&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C++: compile → รอ 5 นาที → เช็คมือถือ → ลืมว่าทำอะไรอยู่
Go:  compile → 2 วิ → test → แก้ → compile → test → flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fast feedback loop คือ superpower&lt;/strong&gt; — คุณทดลองได้เร็ว, กล้า refactor, ไม่กลัว compile error — เพราะมันใช้เวลาแค่ 2 วิ&lt;/p&gt;

&lt;p&gt;นี่คือสิ่งที่ Ken Thompson, Rob Pike, และ Robert Griesemer ตั้งใจ — ไม่ใช่แค่ "สร้างภาษาใหม่" — แต่คือการสร้าง &lt;strong&gt;ประสบการณ์&lt;/strong&gt; ใหม่ในการเขียนโปรแกรม&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://go.dev/talks/2012/splash.article" rel="noopener noreferrer"&gt;Go at Google: Language Design in the Service of Software Engineering&lt;/a&gt; — Rob Pike (2012)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gopl.io/" rel="noopener noreferrer"&gt;The Go Programming Language&lt;/a&gt; — Donovan &amp;amp; Kernighan&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/faq" rel="noopener noreferrer"&gt;Go FAQ: Why does Go compile so quickly?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>history</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Agent Skills — Taxonomy ฉบับสมบูรณ์ของ Claude Code</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Sun, 05 Jul 2026 07:48:22 +0000</pubDate>
      <link>https://dev.to/gophernment/agent-skills-taxonomy-chbabsmbuurnkhng-claude-code-5g2k</link>
      <guid>https://dev.to/gophernment/agent-skills-taxonomy-chbabsmbuurnkhng-claude-code-5g2k</guid>
      <description>&lt;h2&gt;
  
  
  Agent Skills — Taxonomy ฉบับสมบูรณ์ของ Claude Code
&lt;/h2&gt;

&lt;p&gt;ตอนใช้ Claude Code ครั้งแรก — เรามักเข้าใจว่า "ทุกอย่างอยู่ใน &lt;code&gt;CLAUDE.md&lt;/code&gt;"&lt;/p&gt;

&lt;p&gt;แต่พอใช้ไปสักพัก — เราเริ่มเจอของแปลก: &lt;code&gt;/deploy&lt;/code&gt; ทำงานได้เอง, มี hook ตรวจจับตอนเซฟไฟล์, มี subagent ที่ spawn ไปทำงานแล้วกลับมาบอกผล&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"นี่มันไปเอาคำสั่งพวกนี้มาจากไหน?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Claude Code มีระบบ instruction ที่เป็น &lt;strong&gt;ชั้น ๆ (layers)&lt;/strong&gt; — ไม่ใช่แค่ &lt;code&gt;CLAUDE.md&lt;/code&gt; ไฟล์เดียว — แต่มันคือ 4 กลุ่มใหญ่ 20+ กลไก ที่ทำงานใน persistence model ต่างกัน&lt;/p&gt;




&lt;h2&gt;
  
  
  4 Persistence Models — ไม่ใช่แค่ One-Shot กับ Persistent
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;หลักการ&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔒 &lt;strong&gt;Persistent&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;ตั้งครั้งเดียว อยู่ทุก session&lt;/td&gt;
&lt;td&gt;CLAUDE.md, settings, skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔄 &lt;strong&gt;Context-Triggered&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;โหลดเมื่อบริบทตรงเงื่อนไข&lt;/td&gt;
&lt;td&gt;Path-scoped rules, nested CLAUDE.md&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ &lt;strong&gt;Event-Driven&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;ทำงานอัตโนมัติเมื่อเหตุการณ์เกิด&lt;/td&gt;
&lt;td&gt;Hooks (30+ event types)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎯 &lt;strong&gt;Session-Bound&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;อยู่แค่ session นี้&lt;/td&gt;
&lt;td&gt;CLI flags, env vars, conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔒 กลุ่มที่ 1: Persistent — ตั้งครั้งเดียว อยู่ทุก Session
&lt;/h2&gt;

&lt;p&gt;นี่คือ "กระดูกสันหลัง" ของ Claude Code — โหลดทุก session โดยคุณไม่ต้องทำอะไร&lt;/p&gt;

&lt;h3&gt;
  
  
  CLAUDE.md — 4 ระดับ
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ลำดับ&lt;/th&gt;
&lt;th&gt;ตำแหน่ง&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;ใช้สำหรับ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/etc/claude-code/CLAUDE.md&lt;/code&gt; (Linux)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ทั้งองค์กร&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;นโยบายความปลอดภัย, มาตรฐานบริษัท&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ส่วนตัว&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ความชอบ, ทางลัดเครื่องมือ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;โปรเจกต์&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สถาปัตยกรรม, มาตรฐานโค้ด&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./CLAUDE.local.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ส่วนตัว+โปรเจกต์&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ข้อมูลทดสอบ, URL sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;หลักการ:&lt;/strong&gt; โหลดจาก scope กว้าง → แคบ — คำสั่งโปรเจกต์ override คำสั่งผู้ใช้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# ./CLAUDE.md&lt;/span&gt;
&lt;span class="gu"&gt;## Stack: Go 1.22+, PostgreSQL, chi router&lt;/span&gt;
&lt;span class="gu"&gt;## Rules:&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Interface at consumer
&lt;span class="p"&gt;-&lt;/span&gt; Error: %w wrapping
&lt;span class="p"&gt;-&lt;/span&gt; Test: table-driven + httptest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Skills — คลังคำสั่งที่เรียกใช้เมื่อต้องการ
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/&amp;lt;name&amp;gt;/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migration&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;files"&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-haiku-4-5&lt;/span&gt;      &lt;span class="c1"&gt;# ใช้ model ถูกเพื่องานง่าย&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;read_file&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;write_file&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;1. Ask for table name and fields&lt;/span&gt;
&lt;span class="s"&gt;2. Create timestamped SQL file in db/migrations/&lt;/span&gt;
&lt;span class="s"&gt;3. Include both UP and DOWN migration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เรียกใช้ด้วย &lt;code&gt;/migration&lt;/code&gt; — หรือ Claude auto-detect จาก context&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto Memory — Claude เขียนบันทึกเอง
&lt;/h3&gt;

&lt;p&gt;คุณแค่บอกว่า "จำไว้ว่าใช้ &lt;code&gt;go test -race&lt;/code&gt; เสมอ" — Claude เขียนลง &lt;code&gt;~/.claude/projects/&amp;lt;name&amp;gt;/memory/&lt;/code&gt; — ครั้งหน้ามันจะจำได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLAUDE.md: คุณเขียน | Auto Memory: Claude เขียน
CLAUDE.md: กฎและมาตรฐาน | Auto Memory: สิ่งที่เรียนรู้
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Settings — ควบคุมพฤติกรรม
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ไฟล์&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ผู้ใช้ (ทุกโปรเจกต์)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;โปรเจกต์&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;โปรเจกต์ (ส่วนตัว)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm test:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(~/*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(rm:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write(/etc/*)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔄 กลุ่มที่ 2: Context-Triggered — โหลดเฉพาะเมื่อจำเป็น
&lt;/h2&gt;

&lt;p&gt;นี่คือ skills ที่ "ฉลาด" — ไม่โหลดเข้าทุก session — แต่โหลดเมื่อบริบทตรงเงื่อนไข&lt;/p&gt;

&lt;h3&gt;
  
  
  Path-Scoped Rules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/rules/api-design.md
.claude/rules/css-styling.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude จะโหลด &lt;code&gt;api-design.md&lt;/code&gt; เมื่อทำงานกับไฟล์ใน &lt;code&gt;src/api/&lt;/code&gt; — และไม่โหลด &lt;code&gt;css-styling.md&lt;/code&gt; ถ้าคุณไม่ได้แตะ CSS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# .claude/rules/api-design.md&lt;/span&gt;
&lt;span class="gu"&gt;## When: working in src/api/**/*.go&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Always return JSON with &lt;span class="sb"&gt;`{data, error}`&lt;/span&gt; envelope
&lt;span class="p"&gt;-&lt;/span&gt; Rate limit: 100 req/min per user
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`context.Context`&lt;/span&gt; for cancellation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nested CLAUDE.md
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── CLAUDE.md            ← โหลดเสมอ
├── backend/
│   └── CLAUDE.md        ← โหลดเมื่อเข้าถึง backend/
└── frontend/
    └── CLAUDE.md        ← โหลดเมื่อเข้าถึง frontend/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใช้สำหรับ monorepo — แต่ละ sub-project มี context ของตัวเอง&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill Content — โหลดเมื่อเรียกใช้
&lt;/h3&gt;

&lt;p&gt;Skills metadata (ชื่อ + description) อยู่ใน context ตลอด — แต่&lt;strong&gt;เนื้อหาเต็ม&lt;/strong&gt;ของ skill โหลดเฉพาะตอน &lt;code&gt;/skill&lt;/code&gt; หรือตอน Claude auto-detect&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ กลุ่มที่ 3: Event-Driven — ทำงานอัตโนมัติเมื่อเหตุการณ์เกิด
&lt;/h2&gt;

&lt;p&gt;นี่คือ layer ที่ทรงพลังที่สุด — และคนส่วนใหญ่ไม่รู้ว่ามี&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooks — 30+ Event Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;กลุ่ม&lt;/th&gt;
&lt;th&gt;Events&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔵 &lt;strong&gt;Session&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;SessionStart, Setup, PreCompact, PostCompact, SessionEnd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🟢 &lt;strong&gt;User&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;UserPromptSubmit, MessageDisplay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🟡 &lt;strong&gt;Tool&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🟠 &lt;strong&gt;Subagent&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;SubagentStart, SubagentStop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔴 &lt;strong&gt;System&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Notification, ConfigChange, FileChanged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;ตัวอย่าง Hook:&lt;/strong&gt; "ก่อน Claude จะรัน &lt;code&gt;git push&lt;/code&gt; — รัน test ก่อนเสมอ"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm test &amp;amp;&amp;amp; npm run build"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5 ประเภท Handler:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Handler&lt;/th&gt;
&lt;th&gt;ใช้เมื่อ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shell script (รับ JSON stdin)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;เรียก API ภายนอก&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt-based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ใช้ LLM ตรวจสอบ semantic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent-based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;งานซับซ้อนหลายขั้นตอน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;เชื่อมต่อเครื่องมือ MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Subagents — ตัวแทนย่อย
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/agents/reviewer.md
.claude/agents/tester.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;reviewer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;—&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;checks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Go&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;idioms&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;naming"&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-haiku-4-5&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;read_file&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;glob&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;You review Go code for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Interface at consumer, not producer&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Error wrapping with %w&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Table-driven tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude จะ spawn subagent ไปทำงาน — ผลลัพธ์ไม่เข้า context window ของ parent — &lt;strong&gt;ประหยัด token&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 กลุ่มที่ 4: Session-Bound — อยู่แค่ Session นี้
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;กลไก&lt;/th&gt;
&lt;th&gt;ตัวอย่าง&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI Flags&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--model claude-opus-4-8&lt;/code&gt;, &lt;code&gt;--permission-mode plan&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Env Vars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;, &lt;code&gt;CLAUDE_CODE_MODEL&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Styles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/config&lt;/code&gt; → เลือก Default/Proactive/Explanatory/Learning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inline Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/memory&lt;/code&gt; → แก้ไขระหว่าง session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conversation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;การสนทนาทั้งหมดใน session นี้&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📊 ลำดับการโหลด — เกิดอะไรขึ้นตอน Claude Code เริ่มทำงาน
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. SYSTEM PROMPT (built-in)
   └── Output style ปรับแต่ง

2. CLAUDE.MD (เรียงลำดับ)
   └── Managed → User → Project → Local

3. AUTO MEMORY
   └── 200 บรรทัดแรก

4. SKILLS METADATA
   └── ชื่อ + description

5. MCP TOOLS
   └── Tool definitions

6. HOOKS FIRE
   └── SessionStart → Setup → InstructionsLoaded

7. USER PROMPT
   └── ข้อความแรกจากคุณ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 ติดตั้ง Skills จากข้างนอก — เราได้อะไรมาบ้าง?
&lt;/h2&gt;

&lt;p&gt;คำสั่งยอดนิยมที่เห็นใน community:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills@latest add mattpocock/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือจาก marketplace อื่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin &lt;span class="nb"&gt;install&lt;/span&gt; &amp;lt;name&amp;gt;@&amp;lt;marketplace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ได้อะไรมาบ้าง? — มันไม่ใช่แค่ "skill" ธรรมดา
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;mattpocock/skills&lt;/code&gt; (157K ⭐ บน GitHub) คือ &lt;strong&gt;Claude Code Plugin&lt;/strong&gt; — ไม่ใช่แค่ไฟล์ skill เดียว — แต่มันคือ package ที่รวมทุก layer ใน taxonomy พร้อมกัน:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;สิ่งที่ได้&lt;/th&gt;
&lt;th&gt;อยู่ในโฟลเดอร์&lt;/th&gt;
&lt;th&gt;คือ layer ไหน&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skills (&lt;code&gt;/review&lt;/code&gt;, &lt;code&gt;/test&lt;/code&gt;, &lt;code&gt;/plan&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.claude-plugin/&lt;/code&gt; → skills&lt;/td&gt;
&lt;td&gt;Persistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagents (code-reviewer, tester)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.agents/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Event-Driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hooks (auto-format, lint on save)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.claude-plugin/&lt;/code&gt; → hooks&lt;/td&gt;
&lt;td&gt;Event-Driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commands&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.claude-plugin/&lt;/code&gt; → commands&lt;/td&gt;
&lt;td&gt;Persistent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;สรุป:&lt;/strong&gt; การ &lt;code&gt;install&lt;/code&gt; ครั้งเดียว — ได้ทั้ง skills, subagents, hooks, commands — ครบทุก layer&lt;/p&gt;

&lt;h3&gt;
  
  
  ตรวจสอบว่าได้อะไรมาบ้าง
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. ดู skills ทั้งหมดที่มี (รวมที่ติดตั้งจาก plugin)&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; .claude/skills/
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/skills/

&lt;span class="c"&gt;# 2. ดู plugin ที่ติดตั้ง&lt;/span&gt;
claude plugin list
&lt;span class="c"&gt;# หรือ&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/plugins/

&lt;span class="c"&gt;# 3. ใน Claude Code — ลองเรียก&lt;/span&gt;
/skill-name      &lt;span class="c"&gt;# เรียกใช้ skill โดยตรง&lt;/span&gt;

&lt;span class="c"&gt;# 4. ดูว่า subagent ใหม่มีอะไรบ้าง&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; .claude/agents/
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/agents/

&lt;span class="c"&gt;# 5. ดู hooks ที่เพิ่มมา&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; .claude/settings.json | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 20 hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ติดตั้งแล้วอยู่ตรงไหน?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mattpocock/skills (GitHub repo)
       │
       ▼  npx skills@latest add
       │
~/.claude/plugins/&amp;lt;name&amp;gt;/
       │
       ├── skills/          → ปรากฏเป็น /skill-name
       ├── agents/          → ปรากฏเป็น subagent
       ├── hooks/           → ปรากฏใน settings hooks
       └── commands/        → ปรากฏเป็น /command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ระวังอะไรบ้าง
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plugin อาจ override settings ของคุณ&lt;/strong&gt; — ตรวจสอบว่ามันแก้ &lt;code&gt;settings.json&lt;/code&gt; หรือไม่&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ดู source ก่อน install&lt;/strong&gt; — plugin มีสิทธิ์รัน shell commands ผ่าน hooks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ลองใน project test ก่อน&lt;/strong&gt; — อย่าเพิ่งลงใน project production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;อัปเดตเรื่อย ๆ&lt;/strong&gt; — &lt;code&gt;@latest&lt;/code&gt; ดึงเวอร์ชันล่าสุด&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  สรุป — เลือกใช้ Layer ให้ถูก
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;อยากทำ...&lt;/th&gt;
&lt;th&gt;ใช้...&lt;/th&gt;
&lt;th&gt;เพราะ...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;บอก Claude ว่า project ใช้ Go&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;โหลดทุก session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;เพิ่มกฎเฉพาะ API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/rules/api.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;โหลดเมื่อแตะ API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ตรวจสอบก่อน push&lt;/td&gt;
&lt;td&gt;Hook &lt;code&gt;PreToolUse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ทำงานอัตโนมัติ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;สร้าง migration file&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/migration&lt;/code&gt; skill&lt;/td&gt;
&lt;td&gt;เรียกเมื่อต้องการ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ให้ Claude จำ pattern&lt;/td&gt;
&lt;td&gt;Auto Memory&lt;/td&gt;
&lt;td&gt;Claude เขียนเอง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;บล็อก &lt;code&gt;rm -rf&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;deny rule ถาวร&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ใช้ model ถูกเพื่องาน review&lt;/td&gt;
&lt;td&gt;Subagent + &lt;code&gt;model:&lt;/code&gt; field&lt;/td&gt;
&lt;td&gt;แยก context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;อ่านต่อ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/claude-directory" rel="noopener noreferrer"&gt;Claude Code: Settings &amp;amp; CLAUDE.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;Claude Code: Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/skills" rel="noopener noreferrer"&gt;Claude Code: Skills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/subagents" rel="noopener noreferrer"&gt;Claude Code: Subagents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📅 เขียนเมื่อ: กรกฎาคม 2026 | Claude Code v1.x&lt;br&gt;
⚠️ ข้อมูลอ้างอิงจากเอกสารทางการของ Anthropic — features อาจมีการเปลี่ยนแปลง&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>tools</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
