<?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>REPL — วิธีคุยกับคอมพิวเตอร์ที่ LISP สอนโลก</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Wed, 05 Aug 2026 10:14:29 +0000</pubDate>
      <link>https://dev.to/gophernment/repl-withiikhuykabkhmphiwetrthii-lisp-snolk-31dg</link>
      <guid>https://dev.to/gophernment/repl-withiikhuykabkhmphiwetrthii-lisp-snolk-31dg</guid>
      <description>&lt;p&gt;คุณเคยนึกไหมว่าก่อนที่จะมี &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; ใน Python,&lt;br&gt;
ก่อน &lt;code&gt;node&lt;/code&gt; console,&lt;br&gt;
ก่อน Chrome DevTools —&lt;/p&gt;

&lt;p&gt;การคุยกับคอมพิวเตอร์มันเป็นยังไง?&lt;/p&gt;

&lt;p&gt;คำตอบคือ... &lt;strong&gt;คุณไม่ได้คุยกับมัน&lt;/strong&gt; คุณเขียนโปรแกรมทั้งก้อนบน punch card, ยัดเข้าเครื่อง, รอ output ออกมา, แล้วค่อยรู้ว่ามี bug หรือเปล่า&lt;/p&gt;

&lt;p&gt;REPL เปลี่ยนทั้งหมดนั้น&lt;/p&gt;
&lt;h2&gt;
  
  
  READ → EVAL → PRINT → LOOP
&lt;/h2&gt;

&lt;p&gt;REPL ย่อมาจาก &lt;strong&gt;Read-Eval-Print Loop&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. READ   — อ่านสิ่งที่คุณพิมพ์
2. EVAL   — ประมวลผลมัน
3. PRINT  — แสดงผลลัพธ์
4. LOOP   — กลับไปรอ input ถัดไป
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;วงจรนี้เกิดจาก LISP ในทศวรรษ 1960s — &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, และ &lt;code&gt;print&lt;/code&gt; คือ primitive function สามตัวของภาษา&lt;/p&gt;

&lt;p&gt;เวลาเราเฉลิมฉลอง LISP เรามักพูดถึง garbage collection, lambda, macro — แต่ REPL ต่างออกไป&lt;/p&gt;

&lt;p&gt;REPL ไม่ใช่ "feature"... มันคือ &lt;strong&gt;วิธีคิด&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ก่อน REPL: ยุคที่คอมพิวเตอร์ไม่คุยกับคุณ
&lt;/h2&gt;

&lt;p&gt;ลองนึกภาพ programmer ปี 1965:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เขียนโค้ดบนกระดาษ&lt;/li&gt;
&lt;li&gt;เจาะ punch card&lt;/li&gt;
&lt;li&gt;ยื่นให้ operator&lt;/li&gt;
&lt;li&gt;รอคิว (อาจเป็นชั่วโมง หรือเป็นวัน)&lt;/li&gt;
&lt;li&gt;เครื่องรัน&lt;/li&gt;
&lt;li&gt;ได้ output&lt;/li&gt;
&lt;li&gt;เจอ bug — กลับไปข้อ 1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;นี่คือ &lt;strong&gt;batch processing&lt;/strong&gt; — ทั้งวงการเป็นแบบนี้&lt;/p&gt;

&lt;p&gt;ลองเทียบกับวันนี้: คุณพิมพ์ &lt;code&gt;2 + 2&lt;/code&gt; → enter → &lt;code&gt;4&lt;/code&gt; ปรากฏในเสี้ยววินาที&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REPL ทำให้วงจร feedback จาก "เป็นวัน" เหลือ "เป็นวินาที"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;นี่คือการเปลี่ยน paradigm ที่ใหญ่กว่า feature ใด ๆ ที่ LISP คิด — มันเปลี่ยนวิธีที่มนุษย์ interact กับคอมพิวเตอร์ จาก "สั่งแล้วรอ" เป็น "สนทนา"&lt;/p&gt;

&lt;h2&gt;
  
  
  กำเนิดในห้องแล็บ AI ของ MIT
&lt;/h2&gt;

&lt;p&gt;read-eval-print loop ไม่ได้เกิดจาก philosophy หรือ manifesto — มันเกิดจากความจำเป็น&lt;/p&gt;

&lt;p&gt;AI researchers ที่ MIT กำลังเขียนโปรแกรมที่ต้องลองผิดลองถูกตลอดเวลา เขียนโปรแกรมทั้งก้อนไม่ได้ — เพราะยังไม่รู้ด้วยซ้ำว่าอยากให้มันทำอะไร&lt;/p&gt;

&lt;p&gt;พวกเขาต้องการ &lt;strong&gt;ทดลองทีละ expression&lt;/strong&gt; — เห็นผลลัพธ์ทันที แล้วปรับสมมติฐาน&lt;/p&gt;

&lt;p&gt;LISP — ด้วยความที่ทุกอย่างเป็น expression, ทุก expression ให้ผลลัพธ์ — เหมาะกับวิธีนี้อย่างเป็นธรรมชาติ&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;คำว่า "read-eval-print loop" ปรากฏครั้งแรกใน MACLISP Reference Manual (1974, David Moon)&lt;br&gt;
แต่หลักการนี้ถูกใช้จริงมาตั้งแต่ LISP I system ในปี 1960 (LISP 1.5 Programmer's Manual ปี 1962 คือฉบับที่สมบูรณ์และแพร่หลายที่สุดในยุคนั้น)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ทำไม REPL ถึงสำคัญกว่า "แค่ console"
&lt;/h2&gt;

&lt;p&gt;REPL ไม่ใช่แค่ terminal ที่พิมพ์คำสั่งได้ มันเปลี่ยนวิธีที่เรา:&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;วิธี REPL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;อ่าน documentation → ท่องจำ → เขียน&lt;/td&gt;
&lt;td&gt;ทดลอง → ดูผล → เข้าใจ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;รอ compile ทั้งโปรเจกต์&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;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;code คือ artifacts สำเร็จรูป&lt;/td&gt;
&lt;td&gt;code คือ conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;นี่คือรากฐานของ &lt;strong&gt;exploratory programming&lt;/strong&gt; — ปรัชญาที่บอกว่าเราไม่จำเป็นต้องรู้ทุกอย่างก่อนเริ่มเขียน เราเรียนรู้ระหว่างเขียนได้&lt;/p&gt;

&lt;h2&gt;
  
  
  การเดินทางของ REPL — จากห้องแล็บ AI มาถึงมือคุณ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1960s — LISP
&lt;/h3&gt;

&lt;p&gt;REPL ตัวแรกของโลก — &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, &lt;code&gt;print&lt;/code&gt; ในวงจรอนันต์&lt;/p&gt;

&lt;h3&gt;
  
  
  1990–1991 — Python
&lt;/h3&gt;

&lt;p&gt;Guido van Rossum เริ่มพัฒนา Python ปลายปี 1989 เปิดตัวสู่สาธารณะครั้งแรกในเดือนกุมภาพันธ์ 1991 — REPL (&lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;) เป็นส่วนหนึ่งของภาษามาตั้งแต่แรก โดยเอาไอเดียมาจาก LISP และ ABC language (ภาษาที่ Guido ช่วยพัฒนาก่อนหน้านี้)&lt;/p&gt;

&lt;p&gt;สิ่งที่ Python เพิ่ม: readability — REPL ที่อ่านง่าย, error message ที่มนุษย์เข้าใจ, และ philosophy "มาเพื่อให้คนเขียน, ไม่ใช่ให้ compiler อ่าน"&lt;/p&gt;

&lt;h3&gt;
  
  
  1995 — Ruby IRB
&lt;/h3&gt;

&lt;p&gt;Matz สร้าง IRB (Interactive Ruby) — สำหรับคนที่อยากลอง Ruby แบบ real-time (Ruby มี DNA LISP — Matz บอกว่า "Ruby was a Lisp originally, in theory")&lt;/p&gt;

&lt;h3&gt;
  
  
  2006 — Firebug
&lt;/h3&gt;

&lt;p&gt;Joe Hewitt สร้าง Firebug extension สำหรับ Firefox — เป็นครั้งแรกที่ browser มี interactive console แบบ REPL: inspect DOM, ทดลอง JavaScript, ดู network request — ทั้งหมดใน browser โดยไม่ต้องเปิด terminal แยก วงการ frontend dev เปลี่ยนไปตลอดกาล&lt;/p&gt;

&lt;h3&gt;
  
  
  2009 — Node.js REPL
&lt;/h3&gt;

&lt;p&gt;Ryan Dahl ให้ JavaScript มี REPL นอก browser — frontend dev สามารถลอง JS แบบ interactive โดยไม่ต้องเปิด browser เลย&lt;/p&gt;

&lt;h3&gt;
  
  
  2011 — Chrome DevTools Console
&lt;/h3&gt;

&lt;p&gt;ทีม Chrome ของ Google ต่อยอดจาก WebKit Inspector (ต้นแบบของ Safari) สร้าง DevTools ที่มี Console ในตัว — แนวคิด interactive debugging ใน browser ที่ Firebug บุกเบิกไว้ กลายเป็น standard ในทุก browser หลังจากนั้น&lt;/p&gt;

&lt;h3&gt;
  
  
  2014 — IPython / Jupyter Notebook
&lt;/h3&gt;

&lt;p&gt;Fernando Pérez ต่อยอด REPL ของ Python ให้เป็น notebook — ผสมโค้ด, ผลลัพธ์, markdown, กราฟ ในหน้าเดียว นี่คือ REPL เวอร์ชันที่สมบูรณ์แบบที่สุดสำหรับ data science&lt;/p&gt;

&lt;h3&gt;
  
  
  2015 — Rust Playground
&lt;/h3&gt;

&lt;p&gt;Rust — ภาษาที่ compile ช้าเป็นชั่วโมง — มี playground ออนไลน์ที่ทำตัวเป็น REPL: เขียน → รัน → เห็นผล → ปรับ → รันใหม่ — ทั้งหมดใน browser&lt;/p&gt;

&lt;h2&gt;
  
  
  สิ่งที่ REPL สอนเรา
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feedback loop ที่สั้นที่สุดคืออาวุธที่ทรงพลังที่สุด&lt;/strong&gt; — programmer ที่ทดลองเร็ว เรียนรู้เร็วกว่า programmer ที่รอ compile&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ภาษาไม่ควร "ถูกต้องตั้งแต่ compile" ควร "เรียนรู้ระหว่างเขียน"&lt;/strong&gt; — REPL ทำให้ภาษากลายเป็นเครื่องมือคิด ไม่ใช่แค่เครื่องมือผลิต&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple ideas win&lt;/strong&gt; — read → eval → print → loop — 4 คำ, 4 ขั้นตอน, เปลี่ยนวิธีมนุษย์เขียนโค้ดไปตลอดกาล&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  อ่านเพิ่ม
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dspace.mit.edu/handle/1721.1/6094" rel="noopener noreferrer"&gt;MACLISP Reference Manual (1974)&lt;/a&gt; — ที่มาของคำว่า "read-eval-print loop"&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.paulgraham.com/icad.html" rel="noopener noreferrer"&gt;Paul Graham — "Revenge of the Nerds"&lt;/a&gt; — "Lisp was the first language to have an interactive REPL"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vimeo.com/36579366" rel="noopener noreferrer"&gt;Bret Victor — "Inventing on Principle"&lt;/a&gt; — ทำไม feedback ที่ทันทีถึงเปลี่ยนวิธีเราคิด&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter Project&lt;/a&gt; — REPL สำหรับ data science&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;📅 สิงหาคม 2026 | ⚠️ ตรวจสอบข้อมูล ณ วันที่เขียน&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>history</category>
      <category>programming</category>
      <category>functional</category>
    </item>
    <item>
      <title>LISP — ภาษา 67 ปีก่อน ที่ยังมีชีวิตอยู่ในทุกภาษาที่คุณเขียน</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Tue, 04 Aug 2026 23:13:21 +0000</pubDate>
      <link>https://dev.to/gophernment/lisp-phaasaa-67-piikn-thiiyangmiichiiwityuuainthukphaasaathiikhunekhiiyn-1cfe</link>
      <guid>https://dev.to/gophernment/lisp-phaasaa-67-piikn-thiiyangmiichiiwityuuainthukphaasaathiikhunekhiiyn-1cfe</guid>
      <description>&lt;p&gt;คุณเขียน &lt;code&gt;if-else&lt;/code&gt; ทุกวัน&lt;br&gt;
คุณรัน code ใน terminal แล้ว REPL มันตอบกลับมา&lt;br&gt;
คุณ &lt;code&gt;lambda&lt;/code&gt; ใน Python, arrow function ใน JavaScript, closure ใน Rust&lt;/p&gt;

&lt;p&gt;— ทั้งหมดนี้ &lt;strong&gt;เกิดจากภาษา LISP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;และที่น่าทึ่งคือ... LISP ไม่เคยถูก planned ให้เป็นภาษาโปรแกรมด้วยซ้ำ&lt;/p&gt;
&lt;h2&gt;
  
  
  กระดาษแผ่นเดียวที่เปลี่ยนโลก
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1958&lt;/strong&gt; — John McCarthy เริ่มพัฒนาแนวคิด LISP ที่ MIT&lt;br&gt;
&lt;strong&gt;เมษายน 1960&lt;/strong&gt; — McCarthy วัย 32 ตีพิมพ์ paper ใน Communications of the ACM (vol. 3, หน้า 184-195)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ใน paper 12 หน้านี้ McCarthy เสนอไอเดียของภาษาโปรแกรมที่:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;จัดการกับสัญลักษณ์ (symbols) ไม่ใช่แค่ตัวเลข&lt;/li&gt;
&lt;li&gt;ใช้ recursion เป็นตัวควบคุมการทำงาน แทนการใช้ loop&lt;/li&gt;
&lt;li&gt;เก็บ function เป็น first-class citizen — ส่งต่อกันได้เหมือนข้อมูล&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;McCarthy เขียนมันขึ้นมาเป็น &lt;strong&gt;ทฤษฎีทางคณิตศาสตร์&lt;/strong&gt; — ไม่ได้ตั้งใจ implement&lt;/p&gt;

&lt;p&gt;แต่ก่อน paper จะตีพิมพ์ — ระหว่างปี 1958-59 Steve Russell นักศึกษา grad student อ่าน manuscript&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I told him, 'Steve, why don't you program this eval?' and he said to me, 'Oh, I misread what you meant. I thought you meant I should implement the interpreter.'"&lt;/p&gt;

&lt;p&gt;— John McCarthy, ACM interview&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;นักศึกษา &lt;strong&gt;เขียน interpreter ให้ทฤษฎีของอาจารย์&lt;/strong&gt; — และภาษา LISP ก็เกิด&lt;/p&gt;

&lt;p&gt;code ตัวแรกที่ Russell เขียน ใช้เวลาแค่ 2-3 วัน (ตัว LISP 1.5 Programmer's Manual ฉบับเต็มออกตามมาทีหลัง ในปี 1962)&lt;/p&gt;
&lt;h2&gt;
  
  
  สิ่งที่ LISP สร้างไว้ (และเราใช้มันทุกวัน)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. if-then-else
&lt;/h3&gt;

&lt;p&gt;ย้อนไปปี 1958 — ภาษาส่วนใหญ่มีแค่ &lt;code&gt;GOTO&lt;/code&gt; กับ branch แบบ assembly&lt;/p&gt;

&lt;p&gt;McCarthy ให้กำเนิด &lt;code&gt;cond&lt;/code&gt; (conditional expression) — จุดเริ่มต้นของ &lt;code&gt;if-else&lt;/code&gt; ที่เราเห็นแทบจะในทุกภาษา&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cond&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;'negative&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;'zero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt; &lt;span class="ss"&gt;'positive&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C, Java, Python, JavaScript, Go, Rust — ภาษาเหล่านี้ได้รับมรดกนี้มาหมด Conditional branching ในทุกภาษาสมัยใหม่มีโครงสร้างแบบเดียวกับที่ McCarthy คิดไว้ตั้งแต่ก่อนมนุษย์ไปดวงจันทร์&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Garbage Collection
&lt;/h3&gt;

&lt;p&gt;ก่อน LISP — programmer จัดการ memory เอง 100% ทุกบรรทัดของ &lt;code&gt;malloc&lt;/code&gt; และ &lt;code&gt;free&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;LISP สร้าง garbage collection ตัวแรกของโลก — ต้นฉบับคือ &lt;strong&gt;mark-and-sweep&lt;/strong&gt; algorithm (implement โดย Daniel Edwards นักศึกษา MIT)&lt;/p&gt;

&lt;p&gt;ทุกวันนี้ GC คือ default ในเกือบทุกภาษา high-level — Java, Python, JavaScript, Go, C#, Ruby ล้วนใช้แนวคิดนี้ต่อยอด&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Lambda — function เป็นข้อมูล
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LISP ทำให้ function เป็น &lt;strong&gt;first-class citizen&lt;/strong&gt; — ส่ง function เป็น parameter ได้, return function ได้, เก็บลง variable ได้เหมือนเป็น string หรือ integer&lt;/p&gt;

&lt;p&gt;นี่คือต้นทางของ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt; — arrow function &lt;code&gt;(x) =&amp;gt; x * x&lt;/code&gt; (Brendan Eich ถูกจ้างไป Netscape เพื่อทำ Scheme ใน browser — แต่ management เปลี่ยนใจให้ syntax เหมือน Java)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; — &lt;code&gt;lambda x: x * x&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby&lt;/strong&gt; — blocks, procs, lambdas (Matz บอกว่า "Ruby was a Lisp originally, in theory")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java&lt;/strong&gt; — lambda expressions (Java 8, 2014 — ใช้เวลา 19 ปีกว่าจะตามทัน)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C++&lt;/strong&gt; — lambda (C++11)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. REPL — ปรัชญา "คุยกับภาษา"
&lt;/h3&gt;

&lt;p&gt;Read-Eval-Print Loop — LISP ให้กำเนิดมันในทศวรรษ 1960s&lt;/p&gt;

&lt;p&gt;ก่อนหน้านั้น: เขียน code → compile → run → debug → repeat&lt;br&gt;
หลังจากนั้น: พิมพ์ expression → กด enter → เห็นผลทันที&lt;/p&gt;

&lt;p&gt;ทุกวันนี้ถ้าคุณเปิด Python REPL (&lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;), Node.js console, Ruby IRB, Chrome DevTools, Rust Playground, หรือ Elixir IEx — คุณกำลังนั่งอยู่ในห้องเรียนเดียวกับ programmer LISP เมื่อ 60 ปีที่แล้ว&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Homoiconicity — code = data
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;; ← นี่คือ list&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;; ← นี่คือ code ที่รัน list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;LISP เขียนด้วย... LISP — code กับ data ใช้โครงสร้างเดียวกัน (S-expression)&lt;/p&gt;

&lt;p&gt;แปลว่า &lt;strong&gt;โปรแกรมแก้โปรแกรมตัวเองได้&lt;/strong&gt; — ไม่ต้องใช้ parser แยก AST, ไม่ต้องเขียน transformer&lt;/p&gt;

&lt;p&gt;นี่คือรากฐานของ &lt;strong&gt;macro system&lt;/strong&gt; ที่ทรงพลังที่สุดในสายภาษาโปรแกรม&lt;/p&gt;

&lt;p&gt;ไม่มีภาษาไหนทำได้เต็มระบบเท่า LISP — แต่แนวคิด "code as data" ไปอยู่ใน:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elixir&lt;/strong&gt; — macro (รับ AST, คืน AST)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia&lt;/strong&gt; — macro + multiple dispatch ("We want a language that's homoiconic, with true macros like Lisp" — Julia manifesto, 2012)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust&lt;/strong&gt; — macro system (proc macro)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clojure&lt;/strong&gt; — macro (ของ Clojure ก็คือ LISP dialect นั่นเอง)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  มรดกจาก LISP
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LISP (1958)
├── Scheme (1975) — minimalist, lexical scoping
│   ├── JavaScript (1995) — Brendan Eich ตั้งใจทำ Scheme-like ใน browser
│   │   └── arrow functions, closure, first-class functions
│   └── Racket (1995) — ภาษาเพื่อการสอนและการวิจัย
├── Common Lisp (1984) — ภาคอุตสาหกรรม, pragmatic
│   └── Emacs Lisp (1985) — editor scripting (GNU Emacs)
├── Clojure (2007) — LISP บน JVM, immutable by default
│   └── จุดประกาย functional programming ในโลก enterprise
└── Python, Ruby, Elixir, Julia, Rust, Swift — ทุกภาษาเอาแนวคิด LISP ไปปรับใช้
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ทำไม LISP ถึงไม่ใช่ภาษาหลักวันนี้
&lt;/h2&gt;

&lt;p&gt;ทั้งที่สร้างนวัตกรรมเกือบทุกอย่างที่เราใช้ — ทำไม LISP ถึงไม่ชนะ?&lt;/p&gt;

&lt;p&gt;Paul Graham (ผู้ก่อตั้ง Y Combinator, แฟนพันธุ์แท้ LISP) อธิบายไว้ใน essay "Beating the Averages":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Syntax ต่างจากทุกอย่าง&lt;/strong&gt; — วงเล็บทำให้คนกลัว&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ไม่มี standard library ในยุคแรก&lt;/strong&gt; — Common Lisp มากอบกู้ในปี 1984 แต่ช้าไปแล้ว&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Winter&lt;/strong&gt; — LISP ผูกกับ AI research; เมื่องบ AI หาย LISP ก็หายด้วย&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling&lt;/strong&gt; — compiler ช้า, IDE ห่วย (เทียบกับ Visual Studio ยุค 90s)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Graham ยืนยันว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Lisp is a language that was discovered, not invented."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ฝั่งนักวิจารณ์ LISP (รวมถึงคนที่เคยใช้ใน production แล้วเปลี่ยนไปภาษาอื่น) ชี้ปัญหาเพิ่มเติมที่ Graham ไม่พูดถึง:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic typing ในระบบใหญ่&lt;/strong&gt; — refactoring ยาก, compiler จับ error ได้น้อย, debugging ใช้เวลานานใน codebase ระดับแสนบรรทัด&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance จนถึงยุค 2000s&lt;/strong&gt; — ก่อน SBCL และ Chez Scheme, LISP runtime บน hardware ยุคนั้นช้ากว่า C/C++ มากโดยเฉพาะงาน numerical&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragmentation&lt;/strong&gt; — Common Lisp กับ Scheme ไม่เคยตกลงกันได้; community แตกเป็นเสี่ยง ๆ แทนที่จะรวมพลัง&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;สรุป: ไม่มีสาเหตุเดียว — มันคือ &lt;strong&gt;perfect storm&lt;/strong&gt; ของ syntax ต่าง + เกิดผิดเวลา + community แตก + ไม่มี corporate sponsor (ต่างจาก Java ที่ Sun ทุ่ม, C# ที่ Microsoft ทุ่ม)&lt;/p&gt;
&lt;h2&gt;
  
  
  มรดกที่มองไม่เห็น
&lt;/h2&gt;

&lt;p&gt;บทความนี้ไม่ได้ตั้งใจจะบอกว่า "คุณควรเขียน LISP" &lt;/p&gt;

&lt;p&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;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;squared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers&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 javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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 rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;squared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;— คุณกำลังเขียน LISP โดยไม่รู้ตัว&lt;/p&gt;




&lt;h3&gt;
  
  
  อ่านเพิ่ม
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dspace.mit.edu/handle/1721.1/6094" rel="noopener noreferrer"&gt;McCarthy's Original 1960 Paper&lt;/a&gt; — "Recursive Functions of Symbolic Expressions"&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.paulgraham.com/avg.html" rel="noopener noreferrer"&gt;Paul Graham — "Beating the Averages"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.paulgraham.com/icad.html" rel="noopener noreferrer"&gt;Paul Graham — "Revenge of the Nerds"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mitpress.mit.edu/sites/default/files/sicp/index.html" rel="noopener noreferrer"&gt;Structure and Interpretation of Computer Programs (SICP)&lt;/a&gt; — ตำรา Scheme ในตำนานของ MIT&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.infoq.com/presentations/Simple-Made-Easy/" rel="noopener noreferrer"&gt;Rich Hickey — "Simple Made Easy"&lt;/a&gt; — ปรัชญาเบื้องหลัง Clojure&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;📅 สิงหาคม 2026 | ⚠️ ตรวจสอบข้อมูล ณ วันที่เขียน&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>history</category>
      <category>programming</category>
      <category>functional</category>
    </item>
    <item>
      <title>GNU — จุดเริ่มต้นของ Linux ที่หลายคนไม่เคยรู้</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:51:23 +0000</pubDate>
      <link>https://dev.to/gophernment/gnu-cchuderimtnkhng-linux-thiihlaaykhnaimekhyruu-4491</link>
      <guid>https://dev.to/gophernment/gnu-cchuderimtnkhng-linux-thiihlaaykhnaimekhyruu-4491</guid>
      <description>&lt;p&gt;เวลาเราเรียก Linux — เรามักนึกถึงระบบปฏิบัติการที่รันเซิร์ฟเวอร์ 99% ของโลก รัน Android รัน Raspberry Pi แต่สิ่งที่หลายคนไม่รู้คือ &lt;strong&gt;Linux ไม่ใช่ "ระบบปฏิบัติการที่สมบูรณ์" ตั้งแต่แรก&lt;/strong&gt; มันเป็นแค่ kernel — หัวใจที่คอยคุยกับฮาร์ดแวร์&lt;/p&gt;

&lt;p&gt;ส่วน shell, compiler, text editor, core utilities — ทั้งหมดนั่นมาจาก &lt;strong&gt;โครงการ GNU&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เรื่องราวของ GNU และ Linux มันเริ่มจากการที่เครื่องพิมพ์เครื่องหนึ่ง... พัง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;ออกเสียงอย่างไร:&lt;/strong&gt; GNU อ่านว่า "กะ-นู" (hard g, พยางค์เดียว) — GNU's Not Unix&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  เรื่องเล่าจากเครื่องพิมพ์
&lt;/h2&gt;

&lt;p&gt;ปี 1980 Richard Stallman ทำงานที่ MIT AI Lab เครื่องพิมพ์ Xerox ในห้องแล็บ jam กระดาษบ่อยมาก เขาเลยอยากแก้ซอฟต์แวร์ให้มันแจ้งเตือนตอน jam แต่ Xerox ไม่ยอมให้ source code&lt;/p&gt;

&lt;p&gt;สำหรับคนยุค 2026 นี่คือเรื่องปกติ — บริษัทไม่ให้ source code ก็หาอันอื่นสิ&lt;/p&gt;

&lt;p&gt;แต่สำหรับ Stallman และวัฒนธรรม hacker ใน MIT AI Lab ยุค 1970s — &lt;strong&gt;นี่คือการทรยศ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เพื่อนบ้าน hack รั้วบ้านคุณ และคุณไม่มีสิทธิ์แม้แต่จะรู้ว่ารั้วมันสร้างยังไง&lt;/p&gt;

&lt;p&gt;Stallman เขียนไว้ในภายหลังว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"ผมบอกตัวเองไม่ได้ว่า non-disclosure agreement มันไม่มีพิษมีภัย"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  กำเนิด GNU
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;27 กันยายน 1983&lt;/strong&gt; — Stallman โพสต์ประกาศโครงการ GNU บน Usenet ภายใต้หัวข้อ "new Unix implementation"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"ผมเชื่อว่ากฎทอง (golden rule) กำหนดว่า ถ้าผมชอบโปรแกรมใด ผมต้องแบ่งปันมันกับคนอื่นที่ชอบมันด้วย ผมไม่สามารถเซ็นสัญญาไม่เปิดเผยข้อมูล (NDA) หรือสัญญาอนุญาตซอฟต์แวร์แบบปิด ด้วยจิตสำนึกที่ดีได้"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— Richard Stallman, GNU Initial Announcement, 27 ก.ย. 1983&lt;/p&gt;

&lt;p&gt;เป้าหมาย: สร้างระบบปฏิบัติการแบบ Unix ที่ &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;ฟรี ไม่ได้แปลว่า gratis (ของฟรี)
ฟรี แปลว่า libre (เสรีภาพ)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;— Free Software Foundation&lt;/p&gt;

&lt;p&gt;Stallman เริ่มเขียนทีละ component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GNU Emacs&lt;/strong&gt; — text editor ในตำนาน (1985)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GCC&lt;/strong&gt; — GNU C Compiler (1987)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;glibc&lt;/strong&gt; — C library (1988)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bash&lt;/strong&gt; — shell ที่เราใช้กันมาจนถึงทุกวันนี้ (1989)&lt;/li&gt;
&lt;li&gt;Coreutils, binutils, gdb... ไล่เรียงไปเรื่อย ๆ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ภายในปี 1990 — เกือบทั้งระบบปฏิบัติการพร้อมแล้ว &lt;strong&gt;ยกเว้น kernel&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hurd — kernel ที่ไม่มีวันเสร็จ
&lt;/h2&gt;

&lt;p&gt;kernel ที่ GNU เลือกใช้คือ &lt;strong&gt;GNU Hurd&lt;/strong&gt; — ออกแบบด้วยสถาปัตยกรรม microkernel บน Mach (CMU) ที่ทะเยอทะยานเกินยุค&lt;/p&gt;

&lt;p&gt;ปัญหาคือ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;รอ Mach เป็น free software&lt;/strong&gt; — CMU ล่าช้าในการปล่อย source code เพราะฝ่ายกฎหมาย&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multithreaded servers สื่อสารด้วย IPC&lt;/strong&gt; — "very hard to debug" (คำพูดของ Stallman เอง)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfect is the enemy of good&lt;/strong&gt; — ดีไซน์ซ้ำไปซ้ำมาเพื่อความ "สมบูรณ์แบบ" ทางทฤษฎี&lt;/li&gt;
&lt;li&gt;Hurd 0.1 ออกปี 1996 — 6 ปีหลังจากเริ่มพัฒนา&lt;/li&gt;
&lt;li&gt;Hurd 0.9 ออกธันวาคม 2016 — &lt;strong&gt;ล่าสุด และไม่เคยถึง 1.0&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stallman พูดตรง ๆ:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"GNU Hurd ไม่เหมาะสำหรับ production use และเราไม่รู้ว่ามันจะเหมาะเมื่อไหร่"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  เด็กฟินแลนด์กับ kernel ของเขา
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;25 สิงหาคม 1991&lt;/strong&gt; — Linus Torvalds นักศึกษาปี 2 มหาวิทยาลัย Helsinki (อายุ 21 ปี) โพสต์บน Usenet group &lt;code&gt;comp.os.minix&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello everybody out there using minix -&lt;/p&gt;

&lt;p&gt;I'm doing a (free) operating system (just a hobby, won't be big and professional like gnu) for 386(486) AT clones.&lt;/p&gt;

&lt;p&gt;I've currently ported bash(1.08) and gcc(1.40), and things seem to work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;สังเกตว่า Linus เองก็พอร์ต &lt;strong&gt;bash&lt;/strong&gt; และ &lt;strong&gt;gcc&lt;/strong&gt; — GNU tools — มาตั้งแต่วันแรก&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;17 กันยายน 1991&lt;/strong&gt; — Linux 0.01 ออกสู่สาธารณะ (คนที่ตั้งชื่อว่า "Linux" คือ Ari Lemmke ผู้ดูแล ftp.funet.fi — Linus อยากให้ชื่อ "Freax" แต่ Lemmke ไม่ชอบ)&lt;/p&gt;

&lt;p&gt;Linus เขียนใน release notes ของ 0.01:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Sadly, a kernel by itself gets you nowhere… Most of the tools used with linux are GNU software and are under the GNU copyleft."&lt;/p&gt;

&lt;p&gt;"น่าเศร้าที่ kernel อย่างเดียวมันไปไหนไม่ได้... เครื่องมือส่วนใหญ่ที่ใช้กับ linux คือซอฟต์แวร์ GNU"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ทำไม Linux ถึงสำเร็จ
&lt;/h2&gt;

&lt;p&gt;ทั้งที่ Hurd มีคนเก่งกว่า มี philosophy ล้ำกว่า ทำไม Linux ถึงชนะ?&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;GNU Hurd&lt;/th&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;สถาปัตยกรรม&lt;/td&gt;
&lt;td&gt;Microkernel&lt;/td&gt;
&lt;td&gt;Monolithic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ออกแบบโดย&lt;/td&gt;
&lt;td&gt;นักทฤษฎี OS&lt;/td&gt;
&lt;td&gt;"ค่าเทอมถูกกว่า Sun"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ปรัชญา&lt;/td&gt;
&lt;td&gt;Perfect from day 1&lt;/td&gt;
&lt;td&gt;Release early, release often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ทีม&lt;/td&gt;
&lt;td&gt;อาสาสมัคร&lt;/td&gt;
&lt;td&gt;ชุมชน (Git แจกตั้งแต่ 2005)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;"Release early, release often"&lt;/strong&gt; — หลักการที่ Linus สอนโลก open source โดยไม่ตั้งใจ&lt;/p&gt;

&lt;p&gt;Linux 0.01 (ก.ย. 1991) — ~10,000 บรรทัด&lt;br&gt;
Linux 0.12 (ก.พ. 1992) — เปลี่ยนไปใช้ &lt;strong&gt;GPL&lt;/strong&gt; — นี่คือจุดเปลี่ยนสำคัญ&lt;br&gt;
Linux 1.0 (มี.ค. 1994) — 176,250 บรรทัด&lt;br&gt;
Linux 6.x (2024) — เกิน 30 ล้านบรรทัด&lt;/p&gt;
&lt;h2&gt;
  
  
  GNU + Linux = ?
&lt;/h2&gt;

&lt;p&gt;เมื่อ Linux ถือกำเนิด มันขาด userspace tools — shell, compiler, basic utilities&lt;/p&gt;

&lt;p&gt;GNU มีของทุกอย่าง — GCC compiler, Bash shell, coreutils&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linux + GNU tools = ระบบปฏิบัติการที่สมบูรณ์&lt;/strong&gt; ครั้งแรกที่โลกมี free Unix ทั้งระบบ&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It is due to Linux that we can actually run a version of the GNU system today."&lt;/p&gt;

&lt;p&gt;— Richard Stallman&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;MCC Interim Linux (กุมภาพันธ์ 1992)&lt;/strong&gt; — ผลงานของ Owen Le Blanc จาก Manchester Computing Centre — คือ distro แรกที่รวม Linux kernel กับ GNU userspace เข้าด้วยกัน ตามด้วย TAMU Linux และ &lt;strong&gt;SLS (Softlanding Linux System)&lt;/strong&gt; ในสิงหาคม 1992&lt;/p&gt;

&lt;p&gt;SLS เป็น distro แรกที่คนใช้กันเยอะ... แต่ package management ของมันพังไม่เป็นท่า จนทำให้ &lt;strong&gt;Ian Murdock&lt;/strong&gt; วัย 20 ปี ประกาศบน Usenet ในเดือนสิงหาคม 1993 ว่าจะสร้าง distro ใหม่ที่ "maintained openly, in the spirit of GNU" — distro นั้นคือ &lt;strong&gt;Debian&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ตามมาด้วย &lt;strong&gt;Slackware&lt;/strong&gt; (กรกฎาคม 1993) ที่พัฒนาโดย Patrick Volkerding — เรียบง่าย ไม่มี dependency resolution แต่ stable จนกลายเป็น distro ที่เก่าแก่ที่สุดที่ยัง maintain อยู่จนถึงทุกวันนี้&lt;/p&gt;

&lt;p&gt;เมื่อ Debian เปลี่ยนชื่อเป็น &lt;strong&gt;"Debian GNU/Linux"&lt;/strong&gt; ในต้นปี 1994 ตามคำขอของ Stallman — การถกเถียงเรื่องชื่อก็เริ่มต้นขึ้น&lt;/p&gt;
&lt;h2&gt;
  
  
  GNU/Linux — การถกเถียงที่ยังไม่จบ
&lt;/h2&gt;

&lt;p&gt;Stallman ยืนกราน: เรียกมันว่า &lt;strong&gt;"GNU/Linux"&lt;/strong&gt; เพราะ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GNU project เริ่มตั้งแต่ 1983 — 7-8 ปีก่อน Linux&lt;/li&gt;
&lt;li&gt;Stallman อ้างอิงตัวเลขจากงานวิจัยภายในของ Red Hat (2001) และ gNewSense (2008) ว่า ใน distro หนึ่ง ๆ: โค้ด GNU ≈ &lt;strong&gt;15-28%&lt;/strong&gt;, โค้ด Linux kernel ≈ &lt;strong&gt;1.5-3%&lt;/strong&gt; (ตัวเลขนี้เป็นที่ถกเถียงในชุมชน — วัดยังไงให้นับ "หน่วย" ของโค้ดอย่างเที่ยงตรง)&lt;/li&gt;
&lt;li&gt;ถ้าไม่มี GNU — Linus ต้องเขียน compiler, shell, utilities เองทั้งหมด&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linus ตอบกลับ:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It doesn't really matter what people call Linux, as long as credit is given where credit is due."&lt;br&gt;
— comp.os.linux.misc, 1996&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;และในหนังสารคดี &lt;em&gt;Revolution OS&lt;/em&gt; (2001):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Calling Linux in general 'GNU Linux' I think is just ridiculous."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;แต่ Linus ก็ไม่ได้ดูถูก GNU — ในหนังสืออัตชีวประวัติ &lt;em&gt;Just for Fun&lt;/em&gt; (2001) เขาเขียนว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Richard Stallman is the God of Free Software."&lt;/p&gt;

&lt;p&gt;"Stallman, who deserves a monument in his honor for giving birth to the GPL…"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  มรดกที่เรายังใช้อยู่ทุกวันนี้
&lt;/h2&gt;

&lt;p&gt;ลองเปิด terminal แล้วพิมพ์:&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;--version&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;GNU coreutils&lt;span class="o"&gt;)&lt;/span&gt; 9.x

&lt;span class="nv"&gt;$ &lt;/span&gt;gcc &lt;span class="nt"&gt;--version&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
gcc &lt;span class="o"&gt;(&lt;/span&gt;GCC&lt;span class="o"&gt;)&lt;/span&gt; 14.x

&lt;span class="nv"&gt;$ &lt;/span&gt;bash &lt;span class="nt"&gt;--version&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
GNU bash, version 5.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GNU คือวิญญาณของ Linux&lt;/strong&gt; — kernel คือหัวใจ แต่ shell, compiler, utilities ที่เราใช้ทุกวันนี้ คือ GNU&lt;/p&gt;

&lt;p&gt;ถ้าไม่มีโครงการ GNU: Linus ต้องเขียนทุกอย่างเองตั้งแต่ compiler, linker, shell... และ kernel "งานอดิเรก" ของเขาคงไม่รอด&lt;/p&gt;

&lt;p&gt;และถ้าไม่มี Linux: GNU มีเกือบทุกอย่างยกเว้น kernel ที่รันได้จริง — Hurd ก็ยังเป็น prototype มาจนถึงทุกวันนี้&lt;/p&gt;

&lt;p&gt;สองโครงการ — หนึ่งปรัชญา หนึ่ง pragmatic — มาบรรจบกันในเวลาที่เหมาะสมพอดี&lt;/p&gt;




&lt;h3&gt;
  
  
  สำหรับคนที่อยากอ่านเพิ่ม
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/gnu/manifesto.html" rel="noopener noreferrer"&gt;The GNU Manifesto (1985)&lt;/a&gt; — Stallman ต้นฉบับ&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/gnu/thegnuproject.html" rel="noopener noreferrer"&gt;The GNU Project&lt;/a&gt; — essay ของ Stallman&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/old-versions/RELNOTES-0.01" rel="noopener noreferrer"&gt;Linux 0.01 Release Notes&lt;/a&gt; — Torvalds, 1991&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/old-versions/RELNOTES-0.12" rel="noopener noreferrer"&gt;Linux 0.12 Release Notes (เปลี่ยนไปใช้ GPL)&lt;/a&gt; — Torvalds, ก.พ. 1992&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://web.archive.org/web/20130515160138/http://groups.google.com/group/comp.os.minix/msg/b813d52cbc5a044b" rel="noopener noreferrer"&gt;Linus โพสต์แรกบน Usenet (25 ส.ค. 1991)&lt;/a&gt; — เก็บใน Wayback Machine&lt;/li&gt;
&lt;li&gt;&lt;a href="https://archive.org/details/justforfun00linu" rel="noopener noreferrer"&gt;Just for Fun — Linus Torvalds autobiography (2001)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Revolution_OS" rel="noopener noreferrer"&gt;Revolution OS — หนังสารคดี open source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;📅 สิงหาคม 2026 | ⚠️ ตรวจสอบข้อมูล ณ วันที่เขียน&lt;/p&gt;

</description>
      <category>linux</category>
      <category>history</category>
      <category>opensource</category>
      <category>gnu</category>
    </item>
    <item>
      <title>"iota ใน Go — อักษรกรีกตัวจิ๋วที่กลายเป็นเครื่องมือทรงพลัง"</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Sun, 02 Aug 2026 03:53:36 +0000</pubDate>
      <link>https://dev.to/gophernment/iota-ain-go-aksrkriiktawcchiwthiiklaayepnekhruuengmuuethrngphlang-213e</link>
      <guid>https://dev.to/gophernment/iota-ain-go-aksrkriiktawcchiwthiiklaayepnekhruuengmuuethrngphlang-213e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📅 เขียนเมื่อ: กรกฎาคม 2026&lt;br&gt;
⚠️ ตรวจสอบข้อมูลจาก Go Specification, APL documentation, และบันทึกของผู้พัฒนา&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;ถ้าคุณเขียน Go มาระยะหนึ่ง คุณคงเคยเห็น &lt;code&gt;iota&lt;/code&gt; — เจ้า identifier ประหลาดที่ไม่มีใครรู้ว่ามันคืออะไรตอนเจอครั้งแรก&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Monday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&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;// 1&lt;/span&gt;
    &lt;span class="n"&gt;Tuesday&lt;/span&gt;            &lt;span class="c"&gt;// 2&lt;/span&gt;
    &lt;span class="n"&gt;Wednesday&lt;/span&gt;          &lt;span class="c"&gt;// 3&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;มันไม่ใช่ keyword, ไม่ใช่ type, ไม่ใช่ function — มันคืออะไรกันแน่?&lt;/p&gt;

&lt;p&gt;และที่สำคัญ — &lt;strong&gt;ทำไมต้องชื่อ &lt;code&gt;iota&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;คำตอบพาเราย้อนกลับไปถึงปี 1962 — ถึงนักคณิตศาสตร์ชาวแคนาดาคนหนึ่ง และภาษาโปรแกรมมิ่งที่เปลี่ยนโลก&lt;/p&gt;




&lt;h2&gt;
  
  
  iota คืออะไรใน Go
&lt;/h2&gt;

&lt;p&gt;ใน Go spec — &lt;code&gt;iota&lt;/code&gt; คือ &lt;strong&gt;predeclared identifier&lt;/strong&gt; ที่ใช้ &lt;strong&gt;เฉพาะใน &lt;code&gt;const&lt;/code&gt; declaration&lt;/strong&gt; เท่านั้น&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 go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&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="no"&gt;iota&lt;/span&gt;          &lt;span class="c"&gt;// 0 (skip)&lt;/span&gt;
    &lt;span class="n"&gt;KB&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 10 = 1024&lt;/span&gt;
    &lt;span class="n"&gt;MB&lt;/span&gt;                    &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 20 = 1,048,576&lt;/span&gt;
    &lt;span class="n"&gt;GB&lt;/span&gt;                    &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 30&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;iota&lt;/code&gt; เริ่มที่ 0 และเพิ่มทีละ 1 ทุกครั้งที่เจอบรรทัดใหม่ใน &lt;code&gt;const&lt;/code&gt; block — แม้ว่าบรรทัดนั้นจะไม่ได้ใช้ &lt;code&gt;iota&lt;/code&gt; ก็ตาม&lt;/p&gt;

&lt;p&gt;สิ่งที่ทำให้ &lt;code&gt;iota&lt;/code&gt; ทรงพลังคือมันเป็น &lt;strong&gt;expression&lt;/strong&gt; (ไม่ใช่แค่ตัวเลข) — คุณเอาไปคูณ บวก ลบ shift ได้หมด&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Read&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;  &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 0 = 1&lt;/span&gt;
    &lt;span class="n"&gt;Write&lt;/span&gt;                 &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 1 = 2&lt;/span&gt;
    &lt;span class="n"&gt;Execute&lt;/span&gt;               &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 2 = 4&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นี่คือวิธีมาตรฐานในการสร้าง enum, bitmask, และ constant series ใน Go — ทั้งหมดด้วย keyword เดียว&lt;/p&gt;




&lt;h2&gt;
  
  
  iota — อักษรกรีกตัวเล็กที่สุด
&lt;/h2&gt;

&lt;p&gt;ก่อนจะเป็นชื่อใน Go — &lt;code&gt;ιώτα&lt;/code&gt; (iota) คืออักษรตัวที่ 9 ของกรีกโบราณ: &lt;strong&gt;ι&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;มันคืออักษรที่ &lt;strong&gt;เล็กที่สุด&lt;/strong&gt; ในภาษากรีก — แค่เส้นตรงหนึ่งเส้น ไม่มีหาง ไม่มีขีด&lt;/p&gt;

&lt;p&gt;ในพระคัมภีร์ไบเบิล มีวลี famous: &lt;em&gt;"not one iota"&lt;/em&gt; — แปลว่า "ไม่แม้แต่นิดเดียว" — เพราะ iota คือสิ่งที่เล็กที่สุด&lt;/p&gt;

&lt;p&gt;และมันคือชื่อที่สมบูรณ์แบบสำหรับสิ่งที่ "เพิ่มทีละหนึ่ง"&lt;/p&gt;




&lt;h2&gt;
  
  
  จุดเริ่มต้น — APL และ Kenneth Iverson
&lt;/h2&gt;

&lt;h3&gt;
  
  
  นักคณิตศาสตร์ผู้สร้างภาษา
&lt;/h3&gt;

&lt;p&gt;ปี 1962 — Kenneth E. Iverson ตีพิมพ์หนังสือ &lt;em&gt;"A Programming Language"&lt;/em&gt; (ที่มาของชื่อ APL)&lt;/p&gt;

&lt;p&gt;Iverson เป็นนักคณิตศาสตร์ชาวแคนาดา (ต่อมาได้ Turing Award ปี 1979) — เขาไม่ได้แค่ออกแบบภาษาใหม่ แต่ปฏิวัติวิธีคิดเรื่อง programming&lt;/p&gt;

&lt;p&gt;แทนที่จะเขียน &lt;code&gt;for i = 1 to 10&lt;/code&gt; — Iverson คิดว่า programming ควรเหมือนคณิตศาสตร์: สั้น, สัญลักษณ์, และทรงพลัง&lt;/p&gt;

&lt;h3&gt;
  
  
  เกิดเป็น ⍳ (iota)
&lt;/h3&gt;

&lt;p&gt;ใน APL, Iverson สร้าง operator &lt;code&gt;⍳&lt;/code&gt; — เรียกว่า &lt;strong&gt;iota&lt;/strong&gt; — ที่ทำสิ่งเดียว: สร้างลำดับเลข&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight j"&gt;&lt;code&gt;&lt;span class="err"&gt;⍳&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="err"&gt;⍳&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(เชื่อกันว่าใน APL ดั้งเดิม ใช้ตัวอักษรกรีก &lt;code&gt;ι&lt;/code&gt; จริง ๆ — Iverson notation ยุค 1957 ใช้สัญลักษณ์คณิตศาสตร์แบบกรีก ก่อนที่ APL จะพัฒนาชุดอักขระเฉพาะของตัวเอง จนได้สัญลักษณ์ &lt;code&gt;⍳&lt;/code&gt; ที่เราเห็นใน APL สมัยใหม่)&lt;/p&gt;

&lt;p&gt;ทำไมต้องชื่อ iota? — Iverson เลือกเพราะมันคือ &lt;strong&gt;อักษรที่เล็กที่สุด&lt;/strong&gt; → เหมาะกับ operator ที่สร้าง "สิ่งที่เล็กที่สุด" (integer unit)&lt;/p&gt;

&lt;p&gt;ปรัชญาของ Iverson: โปรแกรมมิ่งคือภาษาสำหรับ &lt;strong&gt;คิด&lt;/strong&gt; — ไม่ใช่แค่ภาษาสำหรับ &lt;strong&gt;บอกคอมพิวเตอร์ให้ทำงาน&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;APL พิสูจน์ว่า: โค้ดหนึ่งบรรทัดที่เต็มไปด้วยสัญลักษณ์ประหลาด สามารถทำสิ่งที่ภาษาอื่นใช้ 50 บรรทัด&lt;/p&gt;

&lt;p&gt;และ &lt;code&gt;⍳&lt;/code&gt; (iota) คือหัวใจของแนวคิดนี้ — สร้างลำดับจากความว่างเปล่า ด้วยสัญลักษณ์เดียว&lt;/p&gt;

&lt;h3&gt;
  
  
  สิ่งที่ APL iota ทำได้ — มากกว่าแค่นับเลข
&lt;/h3&gt;

&lt;p&gt;APL iota ไม่ได้สร้างแค่ &lt;code&gt;1 2 3&lt;/code&gt; — มันสร้างได้ทุกมิติ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight j"&gt;&lt;code&gt;&lt;span class="err"&gt;⍳&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;matrix&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="err"&gt;ของเลข&lt;/span&gt; &lt;span class="m"&gt;1..9&lt;/span&gt;
&lt;span class="err"&gt;⍳&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="err"&gt;มิติ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;และเมื่อรวมกับ operator ตัวอื่นของ APL — &lt;code&gt;⍳&lt;/code&gt; กลายเป็นอาวุธที่ทรงพลังอย่างไม่น่าเชื่อ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight j"&gt;&lt;code&gt;&lt;span class="nf"&gt;+&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;⍳&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="m"&gt;5050&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;ผลรวม&lt;/span&gt; &lt;span class="m"&gt;1..100&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="err"&gt;ใน&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="err"&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;+/⍳100&lt;/code&gt; — สี่ตัวอักษร คำนวณผลรวม 1 ถึง 100 — โค้ดที่ภาษาอื่นต้องใช้ loop&lt;/p&gt;

&lt;p&gt;นี่คือเหตุผลที่ APL ถูกเรียกว่า "ภาษาโปรแกรมมิ่งที่ทรงพลังที่สุดต่อจำนวนตัวอักษร"&lt;/p&gt;




&lt;h2&gt;
  
  
  จาก APL สู่ Bell Labs — เส้นทางของ iota
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ken Thompson กับ APL
&lt;/h3&gt;

&lt;p&gt;ช่วงปลายยุค 60 — Ken Thompson (ผู้สร้าง Unix) เป็นคนหนึ่งที่หลงรัก APL&lt;/p&gt;

&lt;p&gt;เขาเขียน APL interpreter ด้วยตัวเอง — เช่นเดียวกับสมาชิกทีม Go อีกสองคน (Rob Pike และ Robert Griesemer) ที่ต่างคนต่างเคยเขียน APL interpreter มาก่อนเช่นกัน นี่คือจุดร่วมสำคัญที่ทำให้ทั้งสามคน "พูดภาษาเดียวกัน" เมื่อถึงเวลาออกแบบ Go&lt;/p&gt;

&lt;p&gt;มีบันทึกว่า Thompson เคยพูดว่า APL เป็นหนึ่งในแรงบันดาลใจสำคัญในการออกแบบ Unix philosophy — "ทำสิ่งเดียวแต่ทำให้ดี"&lt;/p&gt;

&lt;p&gt;เขาไม่ได้เอา APL syntax มาใช้ (Unix กับ Go ต่างจาก APL คนละขั้ว) — แต่เขาเอา &lt;strong&gt;วิธีคิด&lt;/strong&gt; มา: abstraction ที่ทรงพลัง, ความกระชับ, และความงามทางคณิตศาสตร์&lt;/p&gt;

&lt;h3&gt;
  
  
  Bell Labs — แหล่งรวมคนที่รู้จัก iota
&lt;/h3&gt;

&lt;p&gt;Bell Labs ช่วงยุค 70-80 คือสถานที่ที่รวมตัวนักคณิตศาสตร์-โปรแกรมเมอร์ที่เข้าใจ APL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ken Thompson — ผู้สร้าง Unix, เขียน APL interpreter เอง&lt;/li&gt;
&lt;li&gt;Rob Pike — โตมาในยุคที่ APL เป็นตำนาน, เขียน APL interpreter เอง&lt;/li&gt;
&lt;li&gt;Robert Griesemer — ผู้ร่วมสร้าง Go, ก็เขียน APL interpreter ด้วยตัวเองเช่นกัน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;นี่คือเหตุผลที่แท้จริงที่ทำให้ชื่อ &lt;code&gt;iota&lt;/code&gt; รู้สึก "ใช่" สำหรับทั้งทีม — ไม่ใช่แค่คนใดคนหนึ่งเสนอแล้วคนอื่นพยักหน้าตาม แต่ทั้งสามคนเข้าใจว่ามันคืออะไรและมาจากไหน&lt;/p&gt;

&lt;p&gt;ทุกคนในทีมที่สร้าง Unix, Plan 9, และต่อมา Go — รู้ว่า &lt;code&gt;iota&lt;/code&gt; คืออะไรและมาจากไหน&lt;/p&gt;

&lt;h3&gt;
  
  
  แนวคิด "sequence generator" ในตระกูล Unix
&lt;/h3&gt;

&lt;p&gt;ระหว่างทางจาก APL สู่ Go — แนวคิด iota ไม่ได้หายไป:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C language&lt;/strong&gt; (1972): &lt;code&gt;enum&lt;/code&gt; — สร้าง constant sequence ได้ แต่ต้องประกาศชื่อ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan 9 C&lt;/strong&gt;: ปรับปรุง &lt;code&gt;enum&lt;/code&gt; ให้ cleaner ขึ้น&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limbo&lt;/strong&gt; (Inferno OS): constant declaration ที่ใกล้เคียง Go มากขึ้น&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pike และทีมเห็นว่า — การสร้าง enum/constant series ใน C น่าเบื่อและ verbose — ต้องเขียนเลขเองทุกบรรทัด&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="c1"&gt;// C — ต้องเขียนเลขเอง&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;READ&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WRITE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EXEC&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&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;เมื่อถึงเวลาออกแบบ Go — Pike รู้ดีว่าต้องมีอะไรดีกว่านี้&lt;/p&gt;




&lt;h2&gt;
  
  
  iota ใน Go — เกิดได้ยังไง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ปัญหาที่ต้องแก้
&lt;/h3&gt;

&lt;p&gt;ทีม Go (Pike, Thompson, Griesemer) เริ่มออกแบบ Go ในปี 2007 — หนึ่งในเป้าหมายคือ: &lt;strong&gt;"ทำทุกอย่างให้ง่ายกว่า C"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;constant declaration ใน C/C++ เป็นหนึ่งใน pain point:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ต้องเขียนค่าให้ทุก constant — แม้มันจะ predictable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enum&lt;/code&gt; กับ &lt;code&gt;#define&lt;/code&gt; อยู่คนละโลก&lt;/li&gt;
&lt;li&gt;bitmask/enum pattern ทำซ้ำซาก&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ทีมต้องการอะไรที่:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;กระชับ — ไม่ต้องเขียนเลขซ้ำ&lt;/li&gt;
&lt;li&gt;flexible — ใช้กับ expression ได้ ไม่ใช่แค่ +1&lt;/li&gt;
&lt;li&gt;ไม่ใช่ magic — อ่านแล้วเข้าใจทันทีว่าเกิดอะไรขึ้น&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ตัวเลือกที่ทีมไม่เอา
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-increment แบบ &lt;code&gt;enum&lt;/code&gt;&lt;/strong&gt; — ใช้ได้แค่ +1, ทำ bitmask ไม่ได้&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preprocessor macro&lt;/strong&gt; — Go ไม่มี preprocessor (ตั้งใจ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code generation&lt;/strong&gt; — ซับซ้อนเกินสำหรับของง่าย ๆ&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  iota — ทางสายกลาง
&lt;/h3&gt;

&lt;p&gt;Pike เสนอ &lt;code&gt;iota&lt;/code&gt; — constant generator ที่:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reset ทุก &lt;code&gt;const&lt;/code&gt; block&lt;/strong&gt; — scope ชัดเจน&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;เป็น expression&lt;/strong&gt; — เอาไปคำนวณต่อได้&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reset เมื่อมี &lt;code&gt;const&lt;/code&gt; keyword ใหม่&lt;/strong&gt; — predictable
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&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="no"&gt;iota&lt;/span&gt;             &lt;span class="c"&gt;// 0&lt;/span&gt;
    &lt;span class="n"&gt;KB&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 10 = 1024&lt;/span&gt;
    &lt;span class="n"&gt;MB&lt;/span&gt;                     &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 20&lt;/span&gt;
    &lt;span class="n"&gt;GB&lt;/span&gt;                     &lt;span class="c"&gt;// 1 &amp;lt;&amp;lt; 30&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ส่วนชื่อ — ไม่ต้องคิดนาน: มันคือการยกย่อง (homage) ให้ APL — ภาษาที่สร้างแรงบันดาลใจให้กับคนสร้าง Unix, Plan 9, และ Go&lt;/p&gt;




&lt;h2&gt;
  
  
  วิธีใช้ iota แบบต่าง ๆ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Enum พื้นฐาน
&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;Season&lt;/span&gt; &lt;span class="kt"&gt;int&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;Spring&lt;/span&gt; &lt;span class="n"&gt;Season&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;  &lt;span class="c"&gt;// 0&lt;/span&gt;
    &lt;span class="n"&gt;Summer&lt;/span&gt;                &lt;span class="c"&gt;// 1&lt;/span&gt;
    &lt;span class="n"&gt;Autumn&lt;/span&gt;                &lt;span class="c"&gt;// 2&lt;/span&gt;
    &lt;span class="n"&gt;Winter&lt;/span&gt;                &lt;span class="c"&gt;// 3&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Bitmask
&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;Permission&lt;/span&gt; &lt;span class="kt"&gt;uint8&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;Read&lt;/span&gt;    &lt;span class="n"&gt;Permission&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;  &lt;span class="c"&gt;// 1&lt;/span&gt;
    &lt;span class="n"&gt;Write&lt;/span&gt;                           &lt;span class="c"&gt;// 2&lt;/span&gt;
    &lt;span class="n"&gt;Execute&lt;/span&gt;                         &lt;span class="c"&gt;// 4&lt;/span&gt;
    &lt;span class="n"&gt;Admin&lt;/span&gt;                           &lt;span class="c"&gt;// 8&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// ใช้:&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;perm&lt;/span&gt; &lt;span class="n"&gt;Permission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Read&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Write&lt;/span&gt;  &lt;span class="c"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Skip values
&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;const&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="no"&gt;iota&lt;/span&gt;          &lt;span class="c"&gt;// 0 (skip)&lt;/span&gt;
    &lt;span class="n"&gt;Monday&lt;/span&gt;            &lt;span class="c"&gt;// 1&lt;/span&gt;
    &lt;span class="n"&gt;Tuesday&lt;/span&gt;           &lt;span class="c"&gt;// 2&lt;/span&gt;
    &lt;span class="n"&gt;Wednesday&lt;/span&gt;         &lt;span class="c"&gt;// 3&lt;/span&gt;
    &lt;span class="n"&gt;Thursday&lt;/span&gt;          &lt;span class="c"&gt;// 4&lt;/span&gt;
    &lt;span class="n"&gt;Friday&lt;/span&gt;            &lt;span class="c"&gt;// 5&lt;/span&gt;
    &lt;span class="n"&gt;Saturday&lt;/span&gt;          &lt;span class="c"&gt;// 6&lt;/span&gt;
    &lt;span class="n"&gt;Sunday&lt;/span&gt;            &lt;span class="c"&gt;// 7&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Multiple expressions ในบรรทัดเดียวกัน
&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bit0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask0&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;iota&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="no"&gt;iota&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;// 1, 0&lt;/span&gt;
    &lt;span class="n"&gt;bit1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask1&lt;/span&gt;                           &lt;span class="c"&gt;// 2, 1&lt;/span&gt;
    &lt;span class="n"&gt;bit2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask2&lt;/span&gt;                           &lt;span class="c"&gt;// 4, 3&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Jump pattern
&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;    &lt;span class="c"&gt;// 0&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;               &lt;span class="c"&gt;// 2&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;               &lt;span class="c"&gt;// 4&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;               &lt;span class="c"&gt;// 6&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ทำไม iota ถึง "ใช่" สำหรับ Go
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Philosophy match
&lt;/h3&gt;

&lt;p&gt;Go มีหลักการ "หนึ่งวิธีที่ดีที่สุด" (one way to do it) — ต่างจาก Python ("หลายวิธีก็ดี") และ C++ ("มีทุกวิธี")&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; คือ "หนึ่งวิธี" ในการสร้าง enum/constant series — ใน Go คุณไม่ต้องเลือกว่าจะใช้ &lt;code&gt;#define&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, หรือ &lt;code&gt;constexpr&lt;/code&gt; — มีแค่ &lt;code&gt;iota&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Zero cost abstraction
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; ไม่มี overhead — มันถูก resolve ตอน compile — runtime ไม่รู้ด้วยซ้ำว่ามี &lt;code&gt;iota&lt;/code&gt; อยู่&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Readability
&lt;/h3&gt;

&lt;p&gt;เมื่อคุณเห็น &lt;code&gt;iota&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;
    &lt;span class="n"&gt;Warning&lt;/span&gt;
    &lt;span class="n"&gt;Error&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;vs C:&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="k"&gt;enum&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WARNING&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;น้อยกว่า — แต่บอกอะไรได้มากกว่า&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Connection to history
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; เป็นหนึ่งใน easter egg ที่สวยงามของ Go — มันเชื่อมต่อกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APL&lt;/strong&gt; (1962) — ต้นกำเนิด&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bell Labs&lt;/strong&gt; — สถานที่ที่ Thompson, Ritchie, Pike ทำงาน&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unix philosophy&lt;/strong&gt; — ทำสิ่งเดียวให้ดี&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  นอกเรื่องนิดหน่อย — iota ในภาษาอื่น
&lt;/h2&gt;

&lt;p&gt;หลังจาก Go ใช้ &lt;code&gt;iota&lt;/code&gt; — หลายภาษาเริ่ม implement concept คล้าย ๆ กัน:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rust&lt;/strong&gt;: ไม่มี &lt;code&gt;iota&lt;/code&gt; โดยตรง แต่ &lt;code&gt;enum&lt;/code&gt; + &lt;code&gt;#[repr]&lt;/code&gt; + &lt;code&gt;strum&lt;/code&gt; crate ให้ความสามารถใกล้เคียง&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zig&lt;/strong&gt;: &lt;code&gt;enum&lt;/code&gt; มี auto-increment แต่ไม่ flexible เท่า &lt;code&gt;iota&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swift&lt;/strong&gt;: &lt;code&gt;enum&lt;/code&gt; รองรับ raw value แต่ต้องระบุ type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ไม่มีใครทำได้สะอาดเท่า Go — เพราะไม่มีใครกล้าใช้ชื่อ &lt;code&gt;iota&lt;/code&gt; (หรืออาจเพราะไม่มีใครรู้ว่ามันคืออะไร)&lt;/p&gt;




&lt;h2&gt;
  
  
  สิ่งที่ iota สอนเรา
&lt;/h2&gt;

&lt;p&gt;ชื่อดีเปลี่ยนทุกอย่าง&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; ใน Go ทำแค่ auto-increment — เหมือน &lt;code&gt;__COUNTER__&lt;/code&gt; ใน C, &lt;code&gt;enumerate&lt;/code&gt; ใน Python&lt;/p&gt;

&lt;p&gt;แต่เพราะชื่อมัน — &lt;code&gt;iota&lt;/code&gt; กลายเป็นมากกว่า feature: มันคือ &lt;strong&gt;เรื่องราว&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ทุกครั้งที่โปรแกรมเมอร์ Go สงสัยว่า "iota คืออะไร" — พวกเขาย้อนกลับไปถึง APL, ถึง Kenneth Iverson, ถึง Bell Labs, ถึงประวัติศาสตร์ของภาษาโปรแกรมมิ่ง&lt;/p&gt;

&lt;p&gt;และนั่นคือสิ่งที่ภาษาโปรแกรมมิ่งที่ดีทำ — ไม่ใช่แค่แก้ปัญหา แต่ &lt;strong&gt;เล่าเรื่อง&lt;/strong&gt;&lt;/p&gt;




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

&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; เริ่มจากอักษรกรีกตัวเล็กที่สุด → กลายเป็น operator ใน APL (1962) → ถูกจดจำโดยทีม Bell Labs → และมาอยู่ใน Go ในฐานะ homage ให้กับประวัติศาสตร์ของ programming&lt;/p&gt;

&lt;p&gt;มันใช้เวลา 50 ปี — จากแนวคิดทางคณิตศาสตร์ สู่ภาษาโปรแกรมมิ่งที่ใช้ใน production ทั่วโลก&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 go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 50 ปีของการเดินทาง — ใน 4 บรรทัด&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;APL&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;  &lt;span class="c"&gt;// 1962&lt;/span&gt;
    &lt;span class="n"&gt;Unix&lt;/span&gt;           &lt;span class="c"&gt;// 1969&lt;/span&gt;
    &lt;span class="n"&gt;Go&lt;/span&gt;             &lt;span class="c"&gt;// 2009&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;ที่มา: Go Language Specification, "A Programming Language" โดย Kenneth Iverson (1962), Rob Pike's talks on Go design, Bell Labs oral history, APL Wiki&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>history</category>
    </item>
    <item>
      <title>"Plan 9 — ระบบปฏิบัติการที่โลกลืม แต่เปลี่ยนวิธีคิดเรื่อง OS ไปตลอดกาล"</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Sun, 02 Aug 2026 03:38:26 +0000</pubDate>
      <link>https://dev.to/gophernment/plan-9-rabbptibatikaarthiiolkluuem-aetepliiynwithiikhideruueng-os-aiptldkaal-41o</link>
      <guid>https://dev.to/gophernment/plan-9-rabbptibatikaarthiiolkluuem-aetepliiynwithiikhideruueng-os-aiptldkaal-41o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📅 เขียนเมื่อ: กรกฎาคม 2026&lt;br&gt;
⚠️ อ้างอิงจากเอกสารต้นฉบับของ Bell Labs และบันทึกของผู้พัฒนา&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;ถ้าผมถามว่า "ระบบปฏิบัติการที่เจ๋งที่สุดในโลกคืออะไร" — คุณคงตอบ Linux, macOS, หรือ Windows&lt;/p&gt;

&lt;p&gt;แต่ถ้าถาม Ken Thompson และ Dennis Ritchie — สองคนที่สร้าง Unix ขึ้นมา — พวกเขาจะตอบว่า &lt;strong&gt;Plan 9&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Plan 9 คือระบบปฏิบัติการที่ Bell Labs สร้างขึ้นในช่วงปลายยุค 80 ถึงต้นยุค 90 โดยทีมเดียวกับที่สร้าง Unix แต่มันไม่ใช่แค่ "Unix เวอร์ชันใหม่" — มันคือการเริ่มต้นใหม่ทั้งหมด&lt;/p&gt;

&lt;p&gt;และถึงแม้วันนี้แทบไม่มีใครใช้ Plan 9 — แนวคิดของมันแทรกซึมอยู่ในทุกระบบปฏิบัติการที่คุณใช้อยู่&lt;/p&gt;




&lt;h2&gt;
  
  
  จุดเริ่มต้น — "Unix เริ่มแก่แล้ว"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ปัญหาที่ Unix สะสมมา
&lt;/h3&gt;

&lt;p&gt;Unix เกิดในปี 1969 — ตอนนั้นคอมพิวเตอร์คือเครื่องเดียวที่มี terminal ต่อพ่วง&lt;/p&gt;

&lt;p&gt;พอยุค 80 มาถึง โลกเปลี่ยน — network กลายเป็นเรื่องปกติ, graphics เริ่มสำคัญ, distributed computing เริ่มเกิด&lt;/p&gt;

&lt;p&gt;แต่ Unix ไม่ได้ถูกออกแบบมาเพื่อสิ่งเหล่านี้ มันถูก patch, extend, retrofit — จนกลายเป็นระบบที่ซับซ้อนเกินกว่าที่ผู้สร้างจะภูมิใจ&lt;/p&gt;

&lt;p&gt;Ken Thompson เคยพูดประมาณว่า: "Unix เริ่มต้นด้วยความเรียบง่าย แต่มันค่อย ๆ สะสมความซับซ้อนเข้าไปเรื่อย ๆ — ถึงเวลาที่ต้องเริ่มใหม่"&lt;/p&gt;

&lt;h3&gt;
  
  
  ทำไมต้องชื่อ Plan 9
&lt;/h3&gt;

&lt;p&gt;ชื่อ "Plan 9" มาจากหนัง B-movie สุดคลาสสิกเรื่อง &lt;em&gt;"Plan 9 from Outer Space"&lt;/em&gt; (1959) ของ Ed Wood — ที่ได้ชื่อว่าเป็น "หนังที่แย่ที่สุดตลอดกาล"&lt;/p&gt;

&lt;p&gt;แต่ชื่อนี้ไม่ได้หมายความว่า OS นี้แย่ — มันคือมุกวงในของทีม Bell Labs ที่ชอบตั้งชื่อแปลก ๆ (Unix เองก็เป็นมุก — มันล้อ Multics)&lt;/p&gt;




&lt;h2&gt;
  
  
  หัวใจของ Plan 9 — "ทุกอย่างคือไฟล์" จริง ๆ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  จาก Unix สู่ Plan 9 — ทำให้สุดทาง
&lt;/h3&gt;

&lt;p&gt;Unix มีแนวคิด famous: &lt;strong&gt;"everything is a file"&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;filesystem เป็นไฟล์ → &lt;code&gt;/home/user/document.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;devices เป็นไฟล์ → &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/tty&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;processes เป็นไฟล์ → &lt;code&gt;/proc/1234&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แต่มันมีข้อยกเว้น — network sockets, graphics, window system — สิ่งเหล่านี้ไม่ใช่ไฟล์ใน Unix&lt;/p&gt;

&lt;p&gt;Plan 9 เอาแนวคิดนี้ไป &lt;strong&gt;สุดทาง&lt;/strong&gt; — ใน Plan 9, &lt;strong&gt;ทุกอย่างคือไฟล์ ไม่มีข้อยกเว้น&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9P — โปรโตคอลเดียวที่เชื่อมทุกอย่าง
&lt;/h3&gt;

&lt;p&gt;หัวใจของ Plan 9 คือ &lt;strong&gt;9P&lt;/strong&gt; — โปรโตคอลที่ทำให้ทุกอย่างสื่อสารกันผ่าน filesystem&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;หน้าต่าง GUI → mount เป็นไฟล์
network connection → mount เป็นไฟล์  
เครื่องอื่นใน network → mount เป็นไฟล์
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ลองนึกภาพ: คุณ &lt;code&gt;ls&lt;/code&gt; ดูไฟล์ในเครื่องคนอื่นได้เหมือนกับเป็น local disk — โดยไม่ต้องใช้ NFS, Samba, หรืออะไรซับซ้อนเลย&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ตัวอย่างจริง:&lt;/strong&gt; ถ้าผมเปิดหน้าต่าง editor (acme) ใน Plan 9 — หน้าต่างนั้นคือไฟล์ใน &lt;code&gt;/mnt/acme/&lt;/code&gt; คุณสามารถ &lt;code&gt;echo "hello" &amp;gt; /mnt/acme/body&lt;/code&gt; แล้วข้อความก็จะไปโผล่ใน editor ทันที&lt;/p&gt;

&lt;p&gt;GUI ของ Plan 9 ไม่ใช่ "โปรแกรมที่วาดรูปบนจอ" — มันคือ "filesystem ที่บังเอิญแสดงผลเป็นกราฟิกได้"&lt;/p&gt;

&lt;h3&gt;
  
  
  Namespace — มุมมองส่วนตัวของทุก process
&lt;/h3&gt;

&lt;p&gt;ใน Linux, ทุก process มองเห็น filesystem เดียวกัน&lt;/p&gt;

&lt;p&gt;ใน Plan 9, &lt;strong&gt;แต่ละ process มี namespace ของตัวเอง&lt;/strong&gt; — คุณสามารถ mount, unmount, และจัดเรียง filesystem ใหม่ได้โดยไม่กระทบ process อื่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process A: /bin → /usr/local/bin
Process B: /bin → /remote/machine/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สอง process นี้เห็น filesystem คนละชุด — แต่รันอยู่บนเครื่องเดียวกัน&lt;/p&gt;

&lt;p&gt;แนวคิดนี้คือต้นกำเนิดของ Linux namespaces ที่ใช้ใน Docker และ container ทุกวันนี้&lt;/p&gt;




&lt;h2&gt;
  
  
  ชีวิตใน Plan 9 — หน้าตาเป็นยังไง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rio — หน้าต่างที่ไม่มี title bar
&lt;/h3&gt;

&lt;p&gt;Rio คือ window system ของ Plan 9 — และมัน minimal สุด ๆ&lt;/p&gt;

&lt;p&gt;ไม่มี taskbar, ไม่มี desktop icons, ไม่มี minimize button — มีแต่หน้าต่างที่คุณจัดการด้วยเมาส์ 3 ปุ่ม&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ปุ่มซ้าย&lt;/strong&gt; = เลือกข้อความ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ปุ่มกลาง&lt;/strong&gt; = execute (รันข้อความที่เลือก)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ปุ่มขวา&lt;/strong&gt; = เมนู (New, Resize, Delete ฯลฯ)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ลองนึกภาพการใช้งานจริง:&lt;/p&gt;

&lt;p&gt;คุณเปิดหน้าต่าง terminal ขึ้นมา — พิมพ์ &lt;code&gt;ls -la&lt;/code&gt; — ผลลัพธ์โผล่ในหน้าต่าง คุณใช้ปุ่มซ้ายลากเลือกชื่อไฟล์ที่สนใจ → กดปุ่มกลาง → Plan 9 เอาไฟล์นั้นมาเป็น argument ให้คำสั่งถัดไปโดยอัตโนมัติ&lt;/p&gt;

&lt;p&gt;หรือใน editor: คุณพิมพ์ &lt;code&gt;man 9p&lt;/code&gt; ใน buffer → ลากคลุม → ปุ่มกลาง → หน้าต่าง man page โผล่ขึ้นมาทันที&lt;/p&gt;

&lt;p&gt;ไอเดียคือ: &lt;strong&gt;คุณไม่ต้อง copy-paste — คุณแค่เลือกแล้ว execute&lt;/strong&gt; ทุกอย่างที่เลือกคือ command ได้หมด&lt;/p&gt;

&lt;p&gt;นี่คือ UX ที่ต่างจากสิ่งที่เราใช้ทุกวันนี้โดยสิ้นเชิง — ไม่มี clipboard, ไม่มี Ctrl-C/Ctrl-V — แต่เมื่อคุณชินแล้ว มันเร็วและ natural กว่ามาก&lt;/p&gt;

&lt;h3&gt;
  
  
  Acme — editor ที่เป็นมากกว่า editor
&lt;/h3&gt;

&lt;p&gt;Acme คือ text editor ในตำนานที่ Rob Pike สร้าง — และมันก็คือ "หน้าต่างที่ควบคุมด้วยเมาส์แบบ hypertext"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทุกคำใน Acme เป็น clickable link&lt;/li&gt;
&lt;li&gt;คุณพิมพ์คำสั่ง shell ลงไปใน buffer แล้วคลิก execute ได้เลย&lt;/li&gt;
&lt;li&gt;ไฟล์ทุกไฟล์ที่เปิดอยู่คือหน้าต่างที่จัดการได้ด้วย mouse gesture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acme ดู primitive สำหรับยุคนี้ — แต่มันคือต้นแบบของ IDE สมัยใหม่: code + terminal + file browser อยู่ในหน้าต่างเดียวกัน&lt;/p&gt;

&lt;h3&gt;
  
  
  Plumbing — messsage bus ก่อนจะมี D-Bus
&lt;/h3&gt;

&lt;p&gt;Plumbing คือระบบส่งข้อความระหว่างโปรแกรมใน Plan 9&lt;/p&gt;

&lt;p&gt;คุณ click ที่ URL → มันส่งไปให้ browser&lt;br&gt;
คุณ click ที่ error message → มันเปิดไฟล์ไปที่บรรทัดนั้น&lt;/p&gt;

&lt;p&gt;ทุกอย่างเชื่อมกันด้วยไฟล์ — และ plumbing ก็คือไฟล์พิเศษที่อ่านเขียนข้อความได้&lt;/p&gt;


&lt;h2&gt;
  
  
  มรดกของ Plan 9 — สิ่งที่อยู่รอดมาจนถึงวันนี้
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. UTF-8 — เกิดบนกระดาษรองจานในร้านอาหารแห่งหนึ่ง
&lt;/h3&gt;

&lt;p&gt;คืนหนึ่งในปี 1992 — Ken Thompson กับ Rob Pike นั่งกินข้าวที่ร้านอาหารสไตล์ diner แห่งหนึ่งใน New Jersey&lt;/p&gt;

&lt;p&gt;พวกเขาคุยกันเรื่องปัญหาที่โลกคอมพิวเตอร์มี encoding เป็นร้อย ๆ แบบ — ASCII, Latin-1, Shift-JIS — แต่ละตัวใช้กับภาษาเดียว&lt;/p&gt;

&lt;p&gt;ทั้งคู่หยิบกระดาษรองจานขึ้นมา แล้วออกแบบ &lt;strong&gt;UTF-8&lt;/strong&gt; — encoding ที่ backward compatible กับ ASCII และรองรับทุกภาษาในโลก&lt;/p&gt;

&lt;p&gt;วันนี้ UTF-8 ใช้ใน &lt;strong&gt;98% ของเว็บทั้งโลก&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;และมันถูกออกแบบระหว่างการพัฒนา Plan 9 — โดยคนของ Plan 9&lt;/p&gt;
&lt;h3&gt;
  
  
  2. &lt;code&gt;/proc&lt;/code&gt; filesystem — เกิดใน Unix แต่ Plan 9 ทำให้มันเป็นอย่างที่เราใช้
&lt;/h3&gt;

&lt;p&gt;Linux users รู้จัก &lt;code&gt;/proc&lt;/code&gt; — filesystem ที่เก็บข้อมูล process:&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="nb"&gt;cat&lt;/span&gt; /proc/cpuinfo    &lt;span class="c"&gt;# ดู CPU&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/meminfo    &lt;span class="c"&gt;# ดูหน่วยความจำ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/proc&lt;/code&gt; เกิดครั้งแรกใน &lt;strong&gt;Unix รุ่น 8 (1984)&lt;/strong&gt; โดย Tom Killian — ผู้เขียน paper "Processes as Files" ที่นำเสนอใน USENIX เดือนมิถุนายน 1984 ซึ่งเกิดก่อน Plan 9 จะเริ่มพัฒนาด้วยซ้ำ&lt;/p&gt;

&lt;p&gt;แต่ &lt;code&gt;/proc&lt;/code&gt; ใน Unix 8 เป็นแบบ flat — หนึ่ง process = หนึ่งไฟล์&lt;/p&gt;

&lt;p&gt;Plan 9 คือคนที่พัฒนาแนวคิดนี้ต่อไป — ทำให้แต่ละ process เป็น &lt;strong&gt;directory&lt;/strong&gt; ที่มีหลายไฟล์ย่อย (ใช้ &lt;code&gt;ls&lt;/code&gt; ดู, &lt;code&gt;cat&lt;/code&gt; อ่านค่าต่าง ๆ ได้) — กลายเป็น hierarchical &lt;code&gt;/proc&lt;/code&gt; แบบที่เราใช้กันทุกวันนี้&lt;/p&gt;

&lt;p&gt;Linux สืบทอด hierarchical model นี้มาจาก Plan 9 — และทุกวันนี้ &lt;code&gt;/proc&lt;/code&gt; กลายเป็นมาตรฐาน&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Linux Namespaces — Container ทั้งหมดมีรากจาก Plan 9
&lt;/h3&gt;

&lt;p&gt;Docker, Kubernetes, container ทั้งหมด — ใช้ Linux namespaces ในการแยก process&lt;/p&gt;

&lt;p&gt;แนวคิด "แต่ละ process มีมุมมองของระบบที่แตกต่างกัน" — เกิดใน Plan 9&lt;/p&gt;

&lt;p&gt;พูดให้เห็นภาพ: Plan 9 คิดเรื่อง per-process namespace ตั้งแต่ปลายยุค 80 — ส่วน Linux namespaces ที่เป็นรากฐานของ Docker และ container ทั้งหมด เพิ่งมาใน kernel ราวปี 2008 หรือเกือบ 20 ปีให้หลัง&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Go — ภาษาโปรแกรมที่สืบทอด DNA จาก Plan 9
&lt;/h3&gt;

&lt;p&gt;Go ถูกสร้างโดย Rob Pike, Ken Thompson, และ Robert Griesemer — สามคนที่เคยทำงานบน Plan 9&lt;/p&gt;

&lt;p&gt;และ Go ก็เต็มไปด้วยแนวคิดจาก Plan 9:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goroutines&lt;/strong&gt; — lightweight threads → ต้นแบบคือ lightweight processes ของ Plan 9&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channels&lt;/strong&gt; — สื่อสารด้วยการส่งข้อความ → ต้นแบบคือ 9P และ plumbing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gofmt&lt;/strong&gt; — ทุกคนเขียนโค้ดหน้าตาเหมือนกัน → ต้นแบบคือ philosophy "หนึ่งวิธีที่ดีที่สุด" ของ Plan 9&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static binary&lt;/strong&gt; — compile เป็นไฟล์เดียว → ต้นแบบคือ Plan 9 binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go คือ Plan 9 ในร่างของภาษาโปรแกรม&lt;/p&gt;




&lt;h2&gt;
  
  
  ทำไม Plan 9 ถึงไม่รุ่ง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ปัญหาที่แก้ไม่ได้
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Chicken-and-egg problem&lt;/strong&gt; — ไม่มี applications เพราะไม่มี users → ไม่มี users เพราะไม่มี applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licensing&lt;/strong&gt; — ยุค 90 Bell Labs ถูกขายต่อหลายมือ (AT&amp;amp;T → Lucent → ...) ทำให้ license ของ Plan 9 ซับซ้อน&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux ชนะไปแล้ว&lt;/strong&gt; — ตอน Plan 9 เริ่ม stable (กลางยุค 90), Linux กำลังโตเร็วและมี ecosystem ใหญ่กว่า&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware support&lt;/strong&gt; — Plan 9 รองรับ hardware น้อยมาก เทียบกับ Linux/Windows&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;แต่ปัญหาหลักคือ: &lt;strong&gt;Plan 9 ไม่ได้พยายามจะ "ดีกว่า Unix นิดหน่อย" — มันพยายามจะ "ใหม่หมด"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;และโลกไม่พร้อมจะเปลี่ยน OS ทั้งระบบเพื่อไอเดียใหม่ ๆ — ถึงแม้ไอเดียเหล่านั้นจะดีกว่า&lt;/p&gt;




&lt;h2&gt;
  
  
  Plan 9 ในปี 2026
&lt;/h2&gt;

&lt;p&gt;Plan 9 ยังมีชีวิตอยู่ — ในรูปแบบของ &lt;strong&gt;9front&lt;/strong&gt; (community fork) และ &lt;strong&gt;Plan 9 from User Space&lt;/strong&gt; (port ของ tools บน Linux/macOS)&lt;/p&gt;

&lt;p&gt;มีกลุ่ม enthusiasts ที่ใช้ Plan 9 เป็น daily driver — เล็ก แต่บ้าพลัง&lt;/p&gt;

&lt;p&gt;และทุก concept ที่ Plan 9 เคยคิด — ถูก implement ใหม่ใน Linux ในอีก 20 ปีต่อมา&lt;/p&gt;




&lt;h2&gt;
  
  
  สิ่งที่ Plan 9 สอนเรา
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"แนวคิดที่ดีไม่ได้แปลว่าจะชนะ — แต่มันจะถูกขโมย"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Plan 9 ล้มเหลวในฐานะผลิตภัณฑ์ — แต่ชนะในฐานะแนวคิด&lt;/p&gt;

&lt;p&gt;ทุก container ที่รันอยู่บน Kubernetes...&lt;br&gt;
ทุก UTF-8 byte ที่ส่งผ่าน network...&lt;br&gt;
ทุก Go routine ที่ทำงานอยู่บน cloud...&lt;/p&gt;

&lt;p&gt;ทั้งหมดมี DNA ของ Plan 9 อยู่ในนั้น&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ที่มา: Bell Labs Technical Journal, Plan 9 manual pages, Rob Pike's personal blog (herpolhode.com), 9fans.net, Wikipedia&lt;/em&gt;&lt;/p&gt;

</description>
      <category>plan9</category>
      <category>os</category>
      <category>history</category>
      <category>unix</category>
    </item>
    <item>
      <title>Russ Cox — คนที่ทำให้ Go เป็น ecosystem ไม่ใช่แค่ภาษา</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Fri, 31 Jul 2026 03:09:23 +0000</pubDate>
      <link>https://dev.to/gophernment/russ-cox-khnthiithamaih-go-epn-ecosystem-aimaichaekhphaasaa-295c</link>
      <guid>https://dev.to/gophernment/russ-cox-khnthiithamaih-go-epn-ecosystem-aimaichaekhphaasaa-295c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📅 กรกฎาคม 2026&lt;br&gt;
⚠️ อ้างอิงจากแหล่งปฐมภูมิ ตรวจสอบแล้ว — แต่เช็คข้อมูลล่าสุดก่อนแชร์&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;ถ้า Pike, Thompson, และ Griesemer คือคนสร้าง Go&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Russ Cox&lt;/strong&gt; คือคนที่ทำให้ Go โตจากภาษาใน Google เป็น ecosystem ที่คนทั้งโลกใช้ เขาเป็นคนเขียน &lt;code&gt;gofmt&lt;/code&gt; คนออกแบบ Go modules คนคิด Minimal Version Selection algorithm คนเขียน compatibility promise และปัจจุบันเป็น &lt;strong&gt;Go tech lead&lt;/strong&gt; — คนที่ตัดสินใจว่า Go จะไปทางไหนต่อ&lt;/p&gt;

&lt;p&gt;ทุกครั้งที่คุณพิมพ์ &lt;code&gt;go build&lt;/code&gt; แล้วมันทำงานในไม่กี่วินาที นั่นคือ Cox&lt;/p&gt;




&lt;h2&gt;
  
  
  เด็ก Plan 9 จาก MIT
&lt;/h2&gt;

&lt;p&gt;ตอนที่เพื่อนวัยเดียวกันใช้ Windows หรือ Mac Russ Cox ใช้ &lt;strong&gt;Plan 9&lt;/strong&gt; — ระบบปฏิบัติการที่ Rob Pike, Ken Thompson, และ Dennis Ritchie สร้างที่ Bell Labs&lt;/p&gt;

&lt;p&gt;เขาใช้มันเป็นระบบปฏิบัติการหลักทุกวัน&lt;/p&gt;

&lt;p&gt;ตอนเรียน MIT Cox ทำงานกับ Pike โดยตรงในโปรเจกต์ Plan 9 ร่วมเขียน paper "Security in Plan 9" (USENIX 2002) ทั้งที่ยังเป็นนักศึกษา&lt;/p&gt;

&lt;p&gt;แต่พอเว็บเริ่มสำคัญ Plan 9 ไม่มีเว็บเบราว์เซอร์ที่ใช้งานได้&lt;/p&gt;

&lt;p&gt;แทนที่จะย้ายไปใช้ OS อื่น Cox ทำสิ่งที่ programmer ดี ๆ จะทำ: &lt;strong&gt;port Plan 9 ทั้งระบบมาที่ Unix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;plan9port คือ Plan 9 userspace ที่รันบน Linux และ macOS คุณสามารถใช้ &lt;code&gt;acme&lt;/code&gt; editor, &lt;code&gt;sam&lt;/code&gt; editor, &lt;code&gt;rc&lt;/code&gt; shell, &lt;code&gt;plumber&lt;/code&gt; — เครื่องมือทั้งหมดของ Plan 9 — บนเครื่องธรรมดา&lt;/p&gt;

&lt;p&gt;Cox ใช้ Acme เป็น editor หลักทุกวัน มาจนถึงทุกวันนี้&lt;/p&gt;

&lt;p&gt;นี่คือจุดที่ Pike สังเกตเห็นเขา "เด็กคนนี้เข้าใจ Plan 9 ดีพอที่จะ port มันมาทั้งระบบ"&lt;/p&gt;




&lt;h2&gt;
  
  
  Google Code Search และ RE2
&lt;/h2&gt;

&lt;p&gt;Cox ฝึกงานที่ Google ระหว่างเรียนปริญญาเอกที่ MIT&lt;/p&gt;

&lt;p&gt;งานที่ทำให้เขาเป็นที่รู้จักคือ &lt;strong&gt;RE2&lt;/strong&gt; — regular expression engine ที่เขาเขียนให้ Google Code Search&lt;/p&gt;

&lt;p&gt;ปัญหาของ regex engine ทั่วไปคือ catastrophic backtracking: บาง pattern ใช้เวลาค้นหาเป็นนาที, ชั่วโมง, หรือพังเลย เพราะ algorithm แบบ backtracking มี worst-case ที่ exponential&lt;/p&gt;

&lt;p&gt;Cox ใช้ automata theory จากงานวิจัยของเขา ออกแบบ RE2 ที่การันตี &lt;strong&gt;linear time&lt;/strong&gt; เสมอ ไม่ว่า pattern จะซับซ้อนแค่ไหน&lt;/p&gt;

&lt;p&gt;RE2 ถูกใช้ใน production ที่ Google ตั้งแต่ปี 2006 — Google Code Search, Google Sheets, และต่อมาเป็น &lt;code&gt;regexp&lt;/code&gt; package ใน Go standard library&lt;/p&gt;

&lt;p&gt;เขาเขียนบทความสามตอนที่กลายเป็น reference ของวงการ regex: "Regular Expression Matching Can Be Simple And Fast", "Regular Expression Matching: the Virtual Machine Approach", "Regular Expression Matching in the Wild"&lt;/p&gt;




&lt;h2&gt;
  
  
  2008 — Pike ชวนเข้าร่วมทีม Go
&lt;/h2&gt;

&lt;p&gt;Cox กำลังจะจบปริญญาเอกที่ MIT (วิทยานิพนธ์ "An Extension-Oriented Compiler") ตอนที่ Pike มาชวน&lt;/p&gt;

&lt;p&gt;Pike พูดว่า: "เราพยายามเอาทุกอย่างที่เรารักเกี่ยวกับการพัฒนา software บน Plan 9 มาทำให้มันใช้ได้กับการเขียน software ที่เราอยากเขียนที่ Google นายอยากช่วยไหม"&lt;/p&gt;

&lt;p&gt;Cox ตอบ: "Yeah, of course"&lt;/p&gt;

&lt;p&gt;เขากลายเป็น member คนที่ 4 ของทีม Go — ต่อจาก Pike, Thompson, Griesemer&lt;/p&gt;




&lt;h2&gt;
  
  
  gofmt — โปรเจกต์แรก
&lt;/h2&gt;

&lt;p&gt;งานแรกของ Cox ในทีม Go คือ &lt;strong&gt;gofmt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ธันวาคม 2009 เขาเขียน blog post ประกาศ gofmt — เครื่องมือที่ format โค้ด Go ให้หน้าตาเหมือนกันหมดโดยอัตโนมัติ&lt;/p&gt;

&lt;p&gt;ฟังดูเป็นเรื่องเล็ก แต่ gofmt คือหนึ่งในการตัดสินใจที่ฉลาดที่สุดในประวัติศาสตร์ Go&lt;/p&gt;

&lt;p&gt;ก่อน gofmt ทีม Go เสียเวลาเถียงกันเรื่อง style ว่า bracket ควรอยู่บรรทัดเดียวกับ if หรือบรรทัดใหม่ indent ควรเป็น tab หรือ space ควรเว้นวรรคหลัง comma ไหม&lt;/p&gt;

&lt;p&gt;หลัง gofmt ปัญหานี้หายไปตลอดกาล&lt;/p&gt;

&lt;p&gt;ทุกวันนี้ทุกโปรเจกต์ Go ในโลกหน้าตาเหมือนกัน ไม่ว่าคุณจะอ่านโค้ดของ Google, ของ startup, หรือของ open source contributor&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;go&lt;/code&gt; command — คุณใช้มันทุกวันโดยไม่เคยคิดถึงมัน
&lt;/h2&gt;

&lt;p&gt;Cox เป็นคนออกแบบ &lt;code&gt;go&lt;/code&gt; command — &lt;code&gt;go build&lt;/code&gt;, &lt;code&gt;go test&lt;/code&gt;, &lt;code&gt;go get&lt;/code&gt; — ทั้งหมดที่คุณพิมพ์ทุกวัน&lt;/p&gt;

&lt;p&gt;แต่ผลงานที่เปลี่ยน Go ไปตลอดกาลคือ &lt;strong&gt;Go modules&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Modules — จุดเปลี่ยนของ Go ecosystem
&lt;/h2&gt;

&lt;p&gt;ก่อน Go 1.11 การจัดการ dependency ใน Go คือหายนะ&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go get&lt;/code&gt; ดึงโค้ดจาก version control โดยตรง ไม่มี versioning ไม่มี lock file Dependency ของโปรเจกต์หนึ่งอาจ silently upgrade แล้วพังโปรเจกต์อีกโปรเจกต์&lt;/p&gt;

&lt;p&gt;กุมภาพันธ์ 2018 Cox เปิดตัว &lt;strong&gt;vgo&lt;/strong&gt; — prototype ของ Go modules ที่เขาเขียนคนเดียว&lt;/p&gt;

&lt;p&gt;แล้วเขาก็ทำสิ่งที่แทบไม่มีใครทำ: เขียน blog post 10 ตอนอธิบายการตัดสินใจทุกอย่าง&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทำไมต้อง semantic import versioning&lt;/li&gt;
&lt;li&gt;Minimal Version Selection คืออะไร และทำไมมันดีกว่า SAT solving แบบ npm&lt;/li&gt;
&lt;li&gt; reproducible builds ทำงานยังไง&lt;/li&gt;
&lt;li&gt;หลักการของ versioning ใน Go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal Version Selection (MVS) เป็น algorithm ที่ Cox คิดขึ้นมาเอง แทนที่จะพยายามหา "best possible combination" ของ dependency versions แบบ npm (ซึ่งใช้ SAT solver และใช้เวลานานมาก) MVS เลือก version ต่ำสุดที่ satisfy requirement ของทุก module&lt;/p&gt;

&lt;p&gt;ผลลัพธ์: &lt;code&gt;go mod tidy&lt;/code&gt; ทำงานใน 2 วินาที แทนที่จะ resolve dependency tree เป็นนาที&lt;/p&gt;

&lt;p&gt;Go modules กลายเป็นมาตรฐานใน Go 1.13 และเป็น default ใน Go 1.16&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Compatibility Promise — โค้ด 10 ปี compile ได้
&lt;/h2&gt;

&lt;p&gt;สิงหาคม 2023 Cox เขียน blog post "Backward Compatibility, Go 1.21, and Go 2"&lt;/p&gt;

&lt;p&gt;ประโยคเปิดของ post นั้นกลายเป็นคำขวัญของ Go:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Boring is good. Boring is stable. Boring means being able to focus on your work, not on what's different about Go."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cox เป็นคน maintain compatibility promise ของ Go review ทุก change ที่อาจ break backward compatibility&lt;/p&gt;

&lt;p&gt;ลองนึกถึงภาษาอื่นที่โค้ดปี 2012 ไม่สามารถ compile ด้วย compiler ปี 2026 ได้โดยไม่ต้องแก้&lt;/p&gt;

&lt;p&gt;Go ทำได้ เพราะ Cox และทีมทุ่มเทให้ compatibility เป็นพระเจ้า&lt;/p&gt;




&lt;h2&gt;
  
  
  Transparent Decision-Making
&lt;/h2&gt;

&lt;p&gt;สิ่งที่ Cox เปลี่ยนวิธีทำงานของทีม Go ไม่ใช่แค่โค้ด แต่คือ &lt;strong&gt;กระบวนการ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เขาเป็นคนออกแบบ Go proposal process — ทุกการเปลี่ยนแปลงใหญ่ต้องผ่าน proposal สาธารณะบน GitHub พร้อม design doc ที่อธิบายปัญหา, ทางเลือก, และเหตุผล&lt;/p&gt;

&lt;p&gt;เขาเขียน blog post 6 ตอนอธิบายกระบวนการนี้: "Clarity &amp;amp; Transparency", "Large Changes", "Scaling Discussions" "Enabling Experiments", "Representation"&lt;/p&gt;

&lt;p&gt;ไม่มี "คนเก่งที่สุดในห้องตัดสินใจ" ทุกการตัดสินใจของ Go ตั้งแต่ Go 1.0 ถึงวันนี้ถูกบันทึกไว้เป็นลายลักษณ์อักษร&lt;/p&gt;

&lt;p&gt;นี่คือสิ่งที่ Cox เรียกว่า "language design in the service of software engineering" — การออกแบบภาษาไม่ใช่ศิลปะของอัจฉริยะ แต่เป็นวิศวกรรมที่มีกระบวนการ มีเหตุผล มีบันทึก&lt;/p&gt;




&lt;h2&gt;
  
  
  ปรัชญา
&lt;/h2&gt;

&lt;p&gt;GitHub bio ของ Cox เขียนว่า: &lt;strong&gt;"Go Hacker. Mistake maker."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เขาไม่เคยแกล้งทำเป็นรู้ทุกอย่าง และนั่นคือสิ่งที่น่านับถือที่สุด&lt;/p&gt;

&lt;p&gt;ตอนให้สัมภาษณ์ GoTime ปี 2018 เขาพูดถึงความคาดหวังตอนแรก:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"My hope was that maybe a few people would notice, and ideally whatever the next big language to come along would steal the concurrency and the interface ideas from Go, and that would be our tiny little contribution to making programming a little bit better. Obviously, that didn't happen, but I'm okay with how it turned out instead."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"หวังว่าไม่กี่คนจะสังเกตเห็น" — แล้ว Go ก็กลายเป็นภาษาที่ใช้ใน production ทั่วโลก&lt;/p&gt;




&lt;h2&gt;
  
  
  อีกด้านของ Cox — นักคณิตศาสตร์
&lt;/h2&gt;

&lt;p&gt;Cox ไม่ใช่แค่วิศวกร Go&lt;/p&gt;

&lt;p&gt;เขาเป็น &lt;strong&gt;President ของ OEIS Foundation&lt;/strong&gt; (Online Encyclopedia of Integer Sequences) — ฐานข้อมูลลำดับจำนวนเต็มที่ใหญ่ที่สุดในโลก นักคณิตศาสตร์และนักวิทยาศาสตร์คอมพิวเตอร์ใช้ OEIS เป็น reference มาตั้งแต่ปี 1964&lt;/p&gt;

&lt;p&gt;ผลงานล่าสุดบน blog ของเขา (2024-2026) ส่วนใหญ่เป็นเรื่อง floating-point formatting — ลงลึกถึงระดับ "Pull a New Proof from Knuth's Fixed-Point Printer" และ "Fast Unrounded Scaling: Proof by Ivy"&lt;/p&gt;

&lt;p&gt;เขาใช้ Ivy ภาษา calculator แนว APL ที่ Rob Pike เขียน พิสูจน์ความถูกต้องของ algorithm เชิงคณิตศาสตร์&lt;/p&gt;

&lt;p&gt;นี่คือคนที่วันหนึ่งออกแบบ Go modules, อีกวันใช้ formal proof พิสูจน์ว่า floating-point formatter ทำงานถูกต้อง&lt;/p&gt;




&lt;h2&gt;
  
  
  ผลงานอื่น ๆ
&lt;/h2&gt;

&lt;p&gt;ก่อน Go, Cox มีผลงานอีกมาก:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vx32&lt;/strong&gt; sandboxing system (USENIX 2008)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vivaldi&lt;/strong&gt; network coordinate system (SIGCOMM 2004) — ใช้หา latency ระหว่าง node ใน distributed system โดยไม่ต้องวัดทุกคู่&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xoc&lt;/strong&gt; extensible compiler (ASPLOS 2008)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หลัง Go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go Memory Model&lt;/strong&gt; — เขียนบทความอธิบาย memory model ของ Go และ hardware memory models (2021-2022)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent Telemetry&lt;/strong&gt; — ระบบเก็บข้อมูลการใช้งาน Go toolchain แบบโปร่งใส (2022)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coroutines for Go&lt;/strong&gt; — proposal สำหรับ coroutine ใน Go (กรกฎาคม 2023)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;xz backdoor analysis&lt;/strong&gt; — วิเคราะห์ timeline ของ backdoor ใน xz library (2024)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ทุกวันนี้
&lt;/h2&gt;

&lt;p&gt;Cox ยังเป็น Go tech lead ทำงานที่ Google Cambridge, MA ใช้ Acme editor ทุกวัน&lt;/p&gt;

&lt;p&gt;เขียน blog ที่ research.swtch.com อย่างสม่ำเสมอ 150+ บทความและยังเพิ่มขึ้นเรื่อย ๆ&lt;/p&gt;

&lt;p&gt;2024-2026: เขียนเรื่อง Go Changes, Go Testing By Example, floating-point formatting series, hash-based bisect debugging, differential coverage&lt;/p&gt;

&lt;p&gt;เขาไม่ได้เป็น keynote speaker ที่มีคนดูเป็นหมื่น แต่ทุกครั้งที่เขาเขียนอะไร คนในทีม Go และ community อ่าน&lt;/p&gt;




&lt;h2&gt;
  
  
  Cox กับโปรแกรมเมอร์ไทย
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;go mod tidy&lt;/code&gt; ที่ทำงานใน 2 วินาที คือ Cox&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gofmt&lt;/code&gt; ที่ทำให้โค้ดทุกโปรเจกต์หน้าตาเหมือนกัน คือ Cox&lt;/p&gt;

&lt;p&gt;Compatibility promise ที่ทำให้โค้ด Go ปี 2012 ยัง compile ได้ในปี 2026 คือ Cox&lt;/p&gt;

&lt;p&gt;และวัฒนธรรม "ตัดสินใจแบบโปร่งใส เขียนเหตุผลให้อ่านได้" ที่กลายเป็น DNA ของ Go community&lt;/p&gt;

&lt;p&gt;คือสิ่งที่ Cox สร้าง&lt;/p&gt;




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

&lt;p&gt;Rob Pike, Ken Thompson, Robert Griesemer คือคนสร้าง Go&lt;/p&gt;

&lt;p&gt;แต่ Russ Cox คือคนที่ทำให้มันอยู่รอด&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ที่มา: swtch.com/~rsc, research.swtch.com, go.dev/blog, GoTime #77, github.com/rsc, github.com/google/re2, pdos.csail.mit.edu/~rsc&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>history</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Robert Griesemer — ศิษย์ของ Wirth ผู้เขียน spec ให้ Go และทำให้ generics เป็นจริง</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Fri, 31 Jul 2026 03:06:38 +0000</pubDate>
      <link>https://dev.to/gophernment/robert-griesemer-sisykhng-wirth-phuuekhiiyn-spec-aih-go-aelathamaih-generics-epncchring-2kg1</link>
      <guid>https://dev.to/gophernment/robert-griesemer-sisykhng-wirth-phuuekhiiyn-spec-aih-go-aelathamaih-generics-epncchring-2kg1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📅 กรกฎาคม 2026&lt;br&gt;
⚠️ ตรวจสอบข้อมูล ณ วันที่เขียน — API และเวอร์ชันอาจเปลี่ยนแปลงภายหลัง&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;เวลาพูดถึงคนสร้าง Go ทุกคนรู้จัก Rob Pike กับ Ken Thompson&lt;/p&gt;

&lt;p&gt;แต่คนที่สามบน whiteboard วันนั้น คนที่เขียน Go specification คนที่ออกแบบ type inference algorithm ให้ Go คนที่ผลักดัน generics ตั้งแต่ proposal จนถึง implementation&lt;/p&gt;

&lt;p&gt;เขาไม่ใช่คนที่คุณเห็นบนเวทีบ่อย ๆ ไม่มี Twitter ไม่ใช่หน้าเป็นข่าว&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Robert Griesemer&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ศิษย์ของ Niklaus Wirth
&lt;/h2&gt;

&lt;p&gt;Griesemer เกิดปี 1964 ที่สวิตเซอร์แลนด์ จบปริญญาเอกจาก ETH Zurich หนึ่งในมหาวิทยาลัยวิศวกรรมที่ดีที่สุดในยุโรป&lt;/p&gt;

&lt;p&gt;อาจารย์ที่ปรึกษาของเขาคือ &lt;strong&gt;Hanspeter Mössenböck&lt;/strong&gt; ภายใต้การดูแลของ &lt;strong&gt;Niklaus Wirth&lt;/strong&gt; — ผู้สร้างภาษา Pascal, Modula, และ Oberon&lt;/p&gt;

&lt;p&gt;ถ้าคุณเคยเขียน Pascal ตอนเรียนมหาวิทยาลัย คุณใช้ภาษาที่ Wirth ออกแบบ Wirth ได้รับ Turing Award ปี 1984 จากผลงานการออกแบบภาษาโปรแกรมมิ่งทั้งตระกูล&lt;/p&gt;

&lt;p&gt;Griesemer เติบโตมาในโรงเรียนแห่งนี้ วิทยานิพนธ์ปริญญาเอกของเขาชื่อ "A programming language for vector computers" (1993) เน้นการออกแบบภาษาให้กับฮาร์ดแวร์เฉพาะทาง&lt;/p&gt;

&lt;p&gt;และร่องรอยของ Wirth อยู่ใน Go จนถึงทุกวันนี้ Go specification ใช้ &lt;strong&gt;Wirth Syntax Notation&lt;/strong&gt; (EBNF variant) ซึ่งเป็น notation ที่ Wirth คิดค้นขึ้นมา นี่ไม่ใช่เรื่องบังเอิญ&lt;/p&gt;




&lt;h2&gt;
  
  
  ก่อน Go: จาก HotSpot สู่ V8
&lt;/h2&gt;

&lt;p&gt;ก่อนจะมาเป็นหนึ่งในสามคนสร้าง Go Griesemer มีผลงานที่โลกใช้กันทุกวัน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Object Oberon&lt;/strong&gt; ขยายภาษา Oberon ของ Wirth ให้มี object-oriented features นี่คือจุดเริ่มต้นของความคิดเรื่อง type system ที่ต่อมาทำให้ Go แตกต่างจากภาษา C-family ทั่วไป&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java HotSpot VM&lt;/strong&gt; Griesemer ร่วมเขียน compiler ให้ HotSpot ซึ่งเป็น JVM implementation ที่ทำให้ Java เร็วพอสำหรับ enterprise และรันอยู่ในเซิร์ฟเวอร์เป็นล้านเครื่องทั่วโลก&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;V8 JavaScript engine&lt;/strong&gt; เขาเป็นหนึ่งในทีมแรก ๆ ของ V8 ที่ Google engine ตัวนี้ทำให้ Chrome เร็วขึ้นหลายเท่า ทำให้ Node.js เกิด และเปลี่ยน JavaScript จากภาษาที่คนดูถูกว่า "ช้า" เป็นภาษาที่เร็วพอจะรัน server-side ได้&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sawzall&lt;/strong&gt; ภาษา data processing ของ Google ที่ Griesemer ทำร่วมกับ Pike และทีม Google อีกหลายคน เป็นประสบการณ์ที่ทำให้เขาเห็นว่า "ภาษาสำหรับงานเฉพาะทาง" ควรหน้าตาเป็นยังไง&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paxos Made Live&lt;/strong&gt; (2006) paper ที่อธิบายความท้าทายในการ implement Paxos algorithm ในระบบ production แสดงให้เห็นว่า Griesemer ไม่ใช่แค่นักทฤษฎีภาษา แต่ยังเข้าใจ distributed systems engineering ด้วย&lt;/p&gt;




&lt;h2&gt;
  
  
  21 กันยายน 2007 — วันที่สามคนยืนหน้า whiteboard
&lt;/h2&gt;

&lt;p&gt;Go FAQ บันทึกวันที่นี้ไว้อย่างชัดเจน:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Robert Griesemer, Rob Pike and Ken Thompson started sketching the goals for a new language on the white board on September 21, 2007."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;สามคนนี้มาจากคนละโลก Pike คือ systems programmer จาก Bell Labs Thompson คือตำนาน Unix ทีสร้างภาษา B และร่วมสร้าง UTF-8 ส่วน Griesemer มาจากโลกของ type theory, formal semantics, และ compiler design&lt;/p&gt;

&lt;p&gt;นี่คือสิ่งที่ทำให้ทีมนี้สมบูรณ์ Pike กับ Thompson คิดจากมุม developer experience ส่วน Griesemer คิดจากมุม "ภาษาแบบนี้ type system ควรเป็นยังไง"&lt;/p&gt;

&lt;p&gt;และผลลัพธ์ที่จับต้องได้ที่สุดจากมุมของ Griesemer คือ &lt;strong&gt;Go specification&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Spec — มหากาพย์เงียบ
&lt;/h2&gt;

&lt;p&gt;Go language specification คือเอกสารที่ programmer Go ทุกคนเปิดอ่านตอนสงสัยว่า syntax แบบนี้ถูกต้องไหม ภาษาเวอร์ชันนี้เปลี่ยนอะไรไปจากเวอร์ชันก่อน&lt;/p&gt;

&lt;p&gt;Griesemer เป็น &lt;strong&gt;primary author&lt;/strong&gt; ของ spec นี้&lt;/p&gt;

&lt;p&gt;การเขียน spec ไม่ใช่งานที่มีคนปรบมือให้ แต่คือสิ่งที่ทำให้ Go เป็นภาษาที่ compiler ทุกตัว (gc, gccgo, TinyGo) ทำงานตรงกัน เพราะทุกอย่างถูกนิยามไว้ชัดเจน ไม่มี "มันควรจะทำงานแบบนี้แหละ" หรือ "แล้วแต่ implementation"&lt;/p&gt;

&lt;p&gt;spec ใช้ Wirth Syntax Notation ภาษาที่อาจารย์ของ Griesemer คิดค้นขึ้นเมื่อ 40 ปีก่อน นั่งอยู่ในเอกสารภาษาใหม่ที่รันอยู่ใน production เป็นล้านเครื่องทั่วโลก&lt;/p&gt;




&lt;h2&gt;
  
  
  Type Inference — สิ่งที่ทำให้ Go generics ใช้งานได้จริง
&lt;/h2&gt;

&lt;p&gt;นี่คือผลงานที่แสดง DNA ของ Griesemer ชัดที่สุด&lt;/p&gt;

&lt;p&gt;Go มี type inference ตั้งแต่แรก (&lt;code&gt;:=&lt;/code&gt;) แต่ของที่ยากคือ &lt;strong&gt;type inference สำหรับ generics&lt;/strong&gt; ที่เขาออกแบบให้กับ Go 1.18&lt;/p&gt;

&lt;p&gt;Griesemer เป็นคนออกแบบ type unification algorithm ที่ทำให้คุณเขียน &lt;code&gt;slices.Sort(list)&lt;/code&gt; แทนที่จะต้องเขียน &lt;code&gt;slices.Sort[MyType](list)&lt;/code&gt; ได้&lt;/p&gt;

&lt;p&gt;เขายังเป็นคนเขียน blog post บน go.dev (ตุลาคม 2023) อธิบายการทำงานของ type inference อย่างละเอียด ตั้งแต่หลักการจนถึง edge case&lt;/p&gt;

&lt;p&gt;ประโยคหนึ่งใน post นั้นสรุปปรัชญาของเขาได้ดี:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Type inference is an optional mechanism: if type arguments make code clearer, by all means, write them down."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ไม่ใช่ "type inference จะเดาทุกอย่างให้คุณ" แต่คือ "type inference จะช่วยคุณในที่ที่มันแน่ใจ และให้คุณควบคุมในที่ที่มันไม่แน่ใจ"&lt;/p&gt;

&lt;p&gt;นี่คือ pragmatic language design — ให้เครื่องมือกับ programmer แต่ไม่บังคับ&lt;/p&gt;




&lt;h2&gt;
  
  
  Generics — 10 ปีที่ทำให้มันถูกต้อง
&lt;/h2&gt;

&lt;p&gt;Generics ใน Go ใช้เวลา design นานกว่าทศวรรษ ไม่ใช่เพราะ Go team ขี้เกียจ แต่เพราะพวกเขาไม่ยอมปล่อยอะไรที่ "เกือบดี"&lt;/p&gt;

&lt;p&gt;Griesemer กับ Ian Lance Taylor เป็นสองคนหลักที่เขียน proposal "The Next Step for Generics" (มิถุนายน 2020)&lt;/p&gt;

&lt;p&gt;เขาเป็นคนออกแบบ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type inference algorithm&lt;/li&gt;
&lt;li&gt;Interface-based constraint system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~&lt;/code&gt; (approximation) syntax สำหรับ type constraints&lt;/li&gt;
&lt;li&gt;Square brackets &lt;code&gt;[]&lt;/code&gt; สำหรับ type parameters (การเลือกที่ถกเถียงกันมาก แต่เขายืนยันว่าดีกว่า &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; เพราะไม่ชนกับ comparison operators)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ทุกครั้งที่คุณเขียน &lt;code&gt;func Sort[T cmp.Ordered](list []T)&lt;/code&gt; คุณกำลังใช้ type system ที่ Griesemer ออกแบบ&lt;/p&gt;




&lt;h2&gt;
  
  
  ปรัชญาการทำงาน
&lt;/h2&gt;

&lt;p&gt;คนที่เคยร่วมงานกับ Griesemer บอกตรงกัน: เขาเป็นคนเงียบ ถามทีตอบที comment ใน code review เฉียบและสั้น&lt;/p&gt;

&lt;p&gt;Go spec ทุกเวอร์ชันคือลายเซ็นของเขา ไม่มีน้ำ ไม่มี buzzword แต่เป๊ะ&lt;/p&gt;

&lt;p&gt;มีคนเล่าว่าในทีม Go ถ้า spec บอกว่า "implementation-defined" Griesemer จะถามทันทีว่าทำไม แล้วช่วย redesign จนมันไม่ต้องเป็น undefined อีก&lt;/p&gt;

&lt;p&gt;นี่คือ DNA ที่มาจาก Wirth: ทำให้ทุกอย่าง explicit ใน spec ทำให้ compiler implement ได้ตรงกันทุกตัว&lt;/p&gt;




&lt;h2&gt;
  
  
  การปรากฏตัวต่อสาธารณะ
&lt;/h2&gt;

&lt;p&gt;Griesemer ไม่ใช่คนที่คุณเห็นบ่อยบนเวที conference แต่ก็ไม่ใช่ว่าไม่เคยขึ้นเลย&lt;/p&gt;

&lt;p&gt;เขาพูดที่ &lt;strong&gt;GopherCon SG 2017&lt;/strong&gt; (keynote: "Exporting Go"), GopherCon 2016, dotGo 2016, Gopherpalooza 2019, GopherCon 2020, GopherCon 2021 (ร่วมกับ Ian Lance Taylor), และ GopherCon 2023&lt;/p&gt;

&lt;p&gt;นอกจากนี้ยังเคยให้สัมภาษณ์ใน GoTime podcast&lt;/p&gt;

&lt;p&gt;แต่เมื่อเทียบกับ Pike ที่มีบล็อก 150+ บทความและเป็น keynote speaker ระดับโลก หรือ Thompson ที่ได้ Turing Award&lt;/p&gt;

&lt;p&gt;Griesemer เลือกที่จะให้งานพูดแทนตัวเขา&lt;/p&gt;

&lt;p&gt;Blog post บน go.dev ที่ Griesemer เขียนเองมีไม่กี่ครั้ง: "The Next Step for Generics" (2020) และ "Type Inference" (2023)&lt;/p&gt;

&lt;p&gt;แต่ทุกครั้งที่มีคนเปิด Go spec ทุกครั้งที่มีคนใช้ generics ทุกครั้งที่ type inference เดา type ได้ถูกต้องโดยที่คุณไม่ต้องประกาศ&lt;/p&gt;

&lt;p&gt;นั่นคือลายเซ็นของเขา&lt;/p&gt;




&lt;h2&gt;
  
  
  ทุกวันนี้
&lt;/h2&gt;

&lt;p&gt;Griesemer ยังทำงานที่ Google เป็นหนึ่งใน maintainer หลักของ Go&lt;/p&gt;

&lt;p&gt;เขาไม่ใช่ "อดีตผู้สร้าง Go" เขาคือ "คนที่ยังสร้าง Go อยู่ทุกวันนี้"&lt;/p&gt;




&lt;h2&gt;
  
  
  Griesemer กับโปรแกรมเมอร์ไทย
&lt;/h2&gt;

&lt;p&gt;Go spec ที่เขาเขียน คือเหตุผลที่คุณอ่าน type error แล้วไม่ต้องเดาว่ามันแปลว่าอะไร&lt;/p&gt;

&lt;p&gt;Go generics ที่เขาออกแบบ คือเหตุผลที่คุณไม่ต้องเขียน &lt;code&gt;interface{}&lt;/code&gt; แล้ว type-assert ไปมา&lt;/p&gt;

&lt;p&gt;และปรัชญาที่เขาสืบทอดมาจาก Wirth "ทำให้ของซับซ้อนง่ายด้วย specification ที่ดี" คือสิ่งที่โปรแกรมเมอร์ไทยทุกคนได้ประโยชน์ ไม่ว่าเราจะรู้ชื่อเขาหรือไม่&lt;/p&gt;




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

&lt;p&gt;Robert Griesemer คือ proof ว่าคุณไม่จำเป็นต้องดังเท่า Pike หรือ Thompson ถึงจะเปลี่ยนโลก&lt;/p&gt;

&lt;p&gt;คุณแค่ต้องเขียน spec ให้ดีพอ&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ที่มา: Go FAQ (go.dev), Wikipedia, Go blog (go.dev/blog), Go spec (go.dev/ref/spec), ETH Research Collection (research-collection.ethz.ch), Google Research (research.google/people/author96/), GoTime podcast, Griesemer's Type Inference post (Oct 2023)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>history</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Rob Pike — ผู้ชายที่เกลียดความซับซ้อน และสร้าง UTF-8 กับ Go</title>
      <dc:creator>Gophernment</dc:creator>
      <pubDate>Thu, 30 Jul 2026 02:35:15 +0000</pubDate>
      <link>https://dev.to/gophernment/rob-pike-phuuchaaythiiekliiydkhwaamchabchn-aelasraang-utf-8-kab-go-2f4k</link>
      <guid>https://dev.to/gophernment/rob-pike-phuuchaaythiiekliiydkhwaamchabchn-aelasraang-utf-8-kab-go-2f4k</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📅 กรกฎาคม 2026&lt;br&gt;
⚠️ อ้างอิงจากแหล่งปฐมภูมิ ตรวจสอบแล้ว — แต่เช็คข้อมูลล่าสุดก่อนแชร์&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;ถ้าคุณพิมพ์ภาษาไทยแล้วไม่พัง&lt;br&gt;
เขียน Go แล้วรู้สึกว่ามันง่ายเกินคาด&lt;br&gt;
หรือสงสัยว่าทำไมภาษาใหม่ ๆ ถึงไม่มี inheritance&lt;/p&gt;

&lt;p&gt;คุณกำลังใช้ของที่ผู้ชายคนหนึ่งใช้เวลา 40 ปีทำ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rob Pike&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  เด็กเนิร์ดแคนาดา
&lt;/h2&gt;

&lt;p&gt;Rob Pike เกิดปี 1956 โตมาในยุคที่คอมพิวเตอร์ยังเป็นเครื่องจักรขนาดห้อง&lt;/p&gt;

&lt;p&gt;จบ BS จาก University of Toronto แล้วไปต่อที่ Caltech เขาสนใจ operating system กับ programming language ตั้งแต่วันแรก&lt;/p&gt;

&lt;p&gt;ปี 1980 ได้งานที่ Bell Labs บ้านของ Unix, C และภาษาโปรแกรมมิ่งอีกนับไม่ถ้วน&lt;/p&gt;

&lt;p&gt;ที่นั่นเขาได้ร่วมงานกับ Ken Thompson (ผู้สร้าง Unix) และ Dennis Ritchie (ผู้สร้าง C) สองคนที่วงการนี้เรียกว่าระดับเทพ&lt;/p&gt;




&lt;h2&gt;
  
  
  ยุค Bell Labs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  สร้าง window system ตัวแรกของ Unix
&lt;/h3&gt;

&lt;p&gt;ปี 1981 Pike เขียน window system ตัวแรกให้ Unix ก่อนที่โลกจะมี Windows หรือ Mac GUI ด้วยซ้ำ ต่อมาพัฒนาเป็น Blit terminal (1984) ที่มี overlapping windows&lt;/p&gt;

&lt;h3&gt;
  
  
  Plan 9 — OS ที่ล้ำเกินยุค
&lt;/h3&gt;

&lt;p&gt;ทีม Pike, Thompson, Ritchie สร้าง Plan 9 ระบบปฏิบัติการที่เอาแนวคิด "ทุกอย่างคือไฟล์" ไปสุดทาง&lt;/p&gt;

&lt;p&gt;ใน Unix พวก filesystem, network, devices ต่างก็เป็นไฟล์ แต่ยังมีข้อยกเว้น Plan 9 ทำแม้กระทั่งหน้าต่าง GUI ก็เป็นไฟล์ คุณ mount window ของคนอื่นมาดูผ่าน network ได้เลย&lt;/p&gt;

&lt;p&gt;Plan 9 ล้ำมาก แต่ไม่เคยเป็น mainstream โลกไปทาง Windows กับ Linux แทน&lt;/p&gt;

&lt;p&gt;แต่แนวคิดของ Plan 9 กลายเป็น DNA ของทุกอย่างที่ Pike ทำต่อมา&lt;/p&gt;

&lt;h3&gt;
  
  
  Inferno — Plan 9 เวอร์ชัน network
&lt;/h3&gt;

&lt;p&gt;ต่อจาก Plan 9 พวกเขาสร้าง Inferno OS ที่ออกแบบให้รันบน network ตั้งแต่เกิด&lt;/p&gt;

&lt;p&gt;มาพร้อมภาษา Limbo ที่หน้าตาเหมือน Go เวอร์ชันดั้งเดิม แต่ก่อน Limbo ก็ยังมี &lt;strong&gt;Newsqueak&lt;/strong&gt; ภาษาที่ Pike เขียนตอนปี 1989 สำหรับ experiment เรื่อง concurrency โดยเฉพาะ&lt;/p&gt;

&lt;p&gt;นี่คือจุดที่สำคัญ goroutine และ channel ที่เราใช้ใน Go ทุกวันนี้ มีต้นแบบตรง ๆ จาก Newsqueak (1989) → Alef (1992) → Limbo (1995) → Go (2009) สาย DNA นี้ตรงมาก คนเขียน Go ส่วนใหญ่ไม่รู้ว่า concurrency model ที่ตัวเองใช้ทุกวัน อายุเกือบ 40 ปีแล้ว&lt;/p&gt;

&lt;p&gt;Inferno ก็เหมือน Plan 9 ล้ำเกินไปสำหรับยุคนั้น&lt;/p&gt;

&lt;h3&gt;
  
  
  UTF-8 — ของที่คุณใช้ทุกวันโดยไม่รู้ตัว
&lt;/h3&gt;

&lt;p&gt;นี่คือหนึ่งใน origin story ที่ดีที่สุดของวงการคอมพิวเตอร์&lt;/p&gt;

&lt;p&gt;กันยายน 1992 Pike ได้เมลจากคณะกรรมการ X/Open ให้ช่วยรีวิวข้อเสนอ encoding ใหม่ชื่อ FSS/UTF Pike ลิสต์สิ่งที่ encoding ที่ดีควรทำได้ แล้วพบว่า FSS/UTF ขาดไปอย่างน้อยหนึ่งข้อ&lt;/p&gt;

&lt;p&gt;เย็นวันนั้น Pike กับ Ken Thompson ไปกินข้าวที่ร้านอาหารใน New Jersey &lt;strong&gt;Ken Thompson เป็นคนออกแบบ bit-packing ของ UTF-8 บนกระดาษรองจาน (placemat)&lt;/strong&gt; Pike นั่งดูอยู่ตรงหน้า&lt;/p&gt;

&lt;p&gt;คืนนั้นพวกเขาโทรกลับไปหา X/Open บอกว่าพร้อมจะ implement ให้เสร็จภายในวันจันทร์&lt;/p&gt;

&lt;p&gt;วันศุกร์สัปดาห์เดียวกัน Plan 9 รันบน UTF-8 เต็มระบบ (บันทึกใน source code: 4 กันยายน 1992 เวลา 19:51:55)&lt;/p&gt;

&lt;p&gt;หัวใจของ UTF-8 คือ backward compatibility กับ ASCII ตัวอักษร A-Z ยังใช้ 1 byte เหมือนเดิม แต่ภาษาไทย จีน ญี่ปุ่น เติมอีกไม่กี่ byte&lt;/p&gt;

&lt;p&gt;วันนี้ UTF-8 ใช้กับเกือบ 98% ของเว็บทั้งโลก ทุกครั้งที่คุณส่งข้อความภาษาไทยแล้วไม่พัง คุณกำลังใช้ของที่ Thompson ออกแบบและ Pike นั่งดูอยู่ข้าง ๆ ในร้านอาหารเย็นนั้น&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pike เคยแก้ความเข้าใจผิดด้วยตัวเอง: "UTF-8 was designed, in front of my eyes, on a placemat in a New Jersey diner one night in September or so 1992" — ไม่ใช่ IBM เป็นคนออกแบบ&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  หนังสือ The Unix Programming Environment
&lt;/h3&gt;

&lt;p&gt;ปี 1984 Pike เขียนหนังสือ "The Unix Programming Environment" ร่วมกับ Brian Kernighan (อีกหนึ่งตำนาน Bell Labs)&lt;/p&gt;

&lt;p&gt;หนังสือเล่มนี้สอนปรัชญา Unix: เขียนโปรแกรมที่ทำอย่างเดียวแต่ทำให้ดี, ต่อท่อเชื่อมกัน, ใช้ text เป็น interface ปรัชญานี้กลายเป็นกระดูกสันหลังของ Go ในอีก 25 ปีต่อมา&lt;/p&gt;




&lt;h2&gt;
  
  
  Google และการเกิดของ Go
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ที่มา: ตำนาน 45 นาทีที่เปลี่ยนทุกอย่าง
&lt;/h3&gt;

&lt;p&gt;ปี 2002 Pike ย้ายไป Google&lt;/p&gt;

&lt;p&gt;ตามตำนานที่ Pike เองเรียกว่า "origin myth" วันหนึ่งปี 2007 มี build ของ Google binary ใหญ่ตัวหนึ่งที่ใช้เวลานานมาก (คนเล่ากันว่า 45 นาที) Pike หันไปคุยกับ Robert Griesemer ระหว่างรอ Ken Thompson มาร่วมวง&lt;/p&gt;

&lt;p&gt;21 กันยายน 2007 ทั้งสามคนเริ่มร่างภาษาใหม่บน whiteboard ในห้องประชุมของ Google&lt;/p&gt;

&lt;p&gt;เป้าหมายชัด: compile เร็วแม้ codebase จะใหญ่แค่ไหน, ไม่มี inheritance ไม่มี exception, concurrency ในตัว (goroutine + channel สืบทอดมาจาก Newsqueak และ Limbo), และ garbage collection ที่ไม่ทำให้ช้า&lt;/p&gt;

&lt;p&gt;Pike เสนอชื่อ "go" เพราะสั้น พิมพ์ง่าย&lt;/p&gt;

&lt;h3&gt;
  
  
  Go เปิดตัว
&lt;/h3&gt;

&lt;p&gt;พฤศจิกายน 2009 Google open source Go&lt;/p&gt;

&lt;p&gt;สิ่งที่ทำให้ Go ต่าง: มันไม่ใช่ academic language ไม่มี paper รองรับ ไม่มี type system ซับซ้อน มันคือภาษาของวิศวกรที่อยากทำงานให้เสร็จ&lt;/p&gt;

&lt;p&gt;Compile เป็น static binary ไฟล์เดียว ไม่มี dependency hell และมี gofmt โค้ดทุกคนหน้าตาเหมือนกัน เลิกเถียงเรื่อง style ได้เลย&lt;/p&gt;

&lt;p&gt;Go 1.0 ปล่อยมีนาคม 2012 และรักษา backward compatibility มาจนถึงทุกวันนี้&lt;/p&gt;

&lt;h3&gt;
  
  
  Go ชนะเพราะอะไร
&lt;/h3&gt;

&lt;p&gt;Pike ออกแบบ Go ด้วยหลักการเดียวที่สรุปเป็นชื่อ talk ปี 2012 ของเขา: &lt;strong&gt;"Less is exponentially more."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ไม่ใช่แค่ทำน้อย ๆ แต่มันคือการตัดทุกอย่างที่ไม่จำเป็นออก จนเหลือแต่ของที่ต้องมีจริง ๆ&lt;/p&gt;

&lt;p&gt;Go เลือกตัด inheritance (ใช้ composition + interface แทน), exception (ใช้ error return value), generic (ตอนแรก เพิ่มใน Go 1.18)&lt;/p&gt;

&lt;p&gt;ทุกอย่างที่ Go ไม่มี คือสิ่งที่ Pike มองว่าเป็นภาระ ไม่ใช่ฟีเจอร์&lt;/p&gt;

&lt;p&gt;ผลที่ได้: Go กลายเป็นภาษาเบอร์หนึ่งของ cloud infrastructure Docker, Kubernetes, Terraform, Prometheus ทั้งหมดเขียนด้วย Go&lt;/p&gt;




&lt;h2&gt;
  
  
  ปรัชญาของ Pike
&lt;/h2&gt;

&lt;p&gt;Pike ไม่ใช่คนที่พูดเยอะ แต่ของที่พูดมักคม&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Less is exponentially more."&lt;/strong&gt; หัวใจของ Go และทุกอย่างที่ Pike ทำ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Object-oriented design is the roman numerals of computing."&lt;/strong&gt; (2004) OOP คือเลขโรมันของวงการคอมพิวเตอร์ ใช้ได้แต่ไม่ควรเป็นค่า default&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Concurrency is not parallelism."&lt;/strong&gt; ชื่อ talk ปี 2012 ที่กลายเป็นประโยคคลาสสิก concurrent ไม่ใช่ parallel&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"A little copying is better than a little dependency."&lt;/strong&gt; ก็อปโค้ดนิดหน่อย ดีกว่าเพิ่ม dependency ใหม่ทั้งตัว (Go proverb)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Clear is better than clever."&lt;/strong&gt; โค้ดที่ชัดเจน ดีกว่าโค้ดที่ฉลาด (Go proverb)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Don't communicate by sharing memory; share memory by communicating."&lt;/strong&gt; อย่าสื่อสารด้วยการแชร์ memory ให้แชร์ memory ด้วยการสื่อสาร หัวใจของ goroutine + channel&lt;/p&gt;




&lt;h2&gt;
  
  
  ทุกวันนี้
&lt;/h2&gt;

&lt;p&gt;Pike ไม่ได้เกษียณ (ถึง Wikipedia จะพยายามเขียนแบบนั้นหลายรอบ แต่ไม่มีแหล่งอ้างอิง)&lt;/p&gt;

&lt;p&gt;เขาเรียกตัวเองว่า &lt;strong&gt;"Professional dilettante"&lt;/strong&gt; ยัง review Go code อยู่ ยังเขียนโค้ดใหม่ ๆ ลง GitHub เรื่อย ๆ&lt;/p&gt;

&lt;p&gt;ผลงานล่าสุด: Ivy (เครื่องคิดเลขแนว APL), ภาษา Lisp ของตัวเอง, เครื่องมือภาษาญี่ปุ่น&lt;/p&gt;

&lt;p&gt;2024 พูดที่ GopherConAU ในหัวข้อ "What We Got Right, What We Got Wrong" ยอมรับทั้งสิ่งที่ Go ทำถูก (spec, gofmt, compatibility, concurrency) และสิ่งที่พลาด (async/await ดีกว่า CSP? concurrency กับ parallelism อธิบายผิดมาตลอด?)&lt;/p&gt;

&lt;p&gt;2025 โพสต์ "On Bloat" ชวนคิดว่า package เล็ก ๆ กลายเป็นปัญหาได้ยังไงเมื่อมี dependents เยอะ&lt;/p&gt;

&lt;p&gt;มกราคม 2026 ลงลึกเรื่อง high-precision math ใน Ivy&lt;/p&gt;

&lt;p&gt;ยัง active บน Mastodon และ Bluesky ใช้ชีวิตระหว่างสหรัฐฯ กับออสเตรเลียกับ Renée French ภรรยาที่เป็น illustrator และเป็นคนวาด Go gopher ที่เราเห็นกันทุกวันนี้&lt;/p&gt;




&lt;h2&gt;
  
  
  Pike กับโปรแกรมเมอร์ไทย
&lt;/h2&gt;

&lt;p&gt;UTF-8 ทำให้ภาษาไทยอยู่บนเว็บได้โดยไม่พัง ก่อนหน้านั้น TIS-620 กับ Windows-874 ชนกันตลอด&lt;/p&gt;

&lt;p&gt;Go เป็นภาษาที่บริษัทเทคไทยใช้กันเยอะขึ้น Agoda, LINE MAN Wongnai, SCB TechX ทั้งหมดมี Go ใน production&lt;/p&gt;

&lt;p&gt;ปรัชญา "less is exponentially more" ของ Pike ยิ่งสำคัญในยุคที่ AI สร้างโค้ดได้เป็นล้านบรรทัด ทำน้อยแต่ดี ไม่ใช่ทำเยอะแล้วงง&lt;/p&gt;




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

&lt;p&gt;Rob Pike ใช้เวลา 40 ปีทำโจทย์เดียว: ทำยังไงให้คอมพิวเตอร์ง่ายสำหรับมนุษย์&lt;/p&gt;

&lt;p&gt;UTF-8 ที่เราใช้ทุกวัน Go ที่รันบนเซิร์ฟเวอร์เป็นล้านเครื่อง ปรัชญา "less is exponentially more" ที่กลายเป็น DNA ของ developer รุ่นใหม่&lt;/p&gt;

&lt;p&gt;ทั้งหมดเริ่มจากผู้ชายคนหนึ่งที่รอ C++ compile 45 นาที แล้วคิดว่า "เราทำได้ดีกว่านี้"&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ที่มา: Wikipedia, go.dev, Rob Pike's blog (commandcenter.blogspot.com), UTF-8 history (Pike's own account 2003), Go FAQ, GitHub (github.com/robpike), Wikiquote&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>history</category>
      <category>unicode</category>
    </item>
    <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>
  </channel>
</rss>
