<?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 Co</title>
    <description>The latest articles on DEV Community by Gophernment Co (@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 Co</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>เขียน Go แบบ Java/Ruby/PHP — ทางลัดสู่ Technical Debt โดยไม่รู้ตัว</title>
      <dc:creator>Gophernment Co</dc:creator>
      <pubDate>Mon, 29 Jun 2026 11:09:46 +0000</pubDate>
      <link>https://dev.to/gophernment/ekhiiyn-go-aebb-javarubyphp-thaangladsuu-technical-debt-odyaimruutaw-pi4</link>
      <guid>https://dev.to/gophernment/ekhiiyn-go-aebb-javarubyphp-thaangladsuu-technical-debt-odyaimruutaw-pi4</guid>
      <description>&lt;h2&gt;
  
  
  เขียน Go แบบ Java/Ruby/PHP — ทางลัดสู่ Technical Debt โดยไม่รู้ตัว
&lt;/h2&gt;

&lt;p&gt;มีเรื่องเล่าใน community Go ว่าเคยมีคนเขียนโค้ดแบบนี้ส่งมาที่ code review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserGetterServiceInterface&lt;/span&gt; &lt;span class="k"&gt;interface&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;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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserGetterService&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;repo&lt;/span&gt; &lt;span class="n"&gt;UserRepositoryInterface&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;NewUserGetterService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="n"&gt;UserRepositoryInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserGetterService&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;UserGetterService&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;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แล้วคนรีวิวถามว่า "พี่ครับ... อะไรคือ &lt;code&gt;UserGetterServiceInterface&lt;/code&gt;? แล้วทำไมต้องมี &lt;code&gt;NewUserGetterService&lt;/code&gt;?"&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;เจ้าตัวตอบแบบซื่อ ๆ ว่า "ก็ผมมาจาก Java นี่นา"&lt;/p&gt;




&lt;h2&gt;
  
  
  ปัญหาไม่ได้อยู่ที่ภาษา — อยู่ที่ "สำเนียง"
&lt;/h2&gt;

&lt;p&gt;เวลาเราเรียนรู้ภาษาใหม่ — โดยเฉพาะภาษาแรกหรือภาษาที่สอง — เรามักจะเขียนมันด้วย "สำเนียง" ของภาษาเดิม ฝรั่งเรียกปรากฏการณ์นี้ว่า &lt;strong&gt;accent-driven development&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ถ้าคุณมาจาก...&lt;/th&gt;
&lt;th&gt;คุณมักจะ...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;☕ &lt;strong&gt;Java&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;สร้าง interface ทุกอย่าง, factory pattern, getter/setter, inheritance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💎 &lt;strong&gt;Ruby&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;metaprogramming, monkey-patching, magic method, &lt;code&gt;method_missing&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐘 &lt;strong&gt;PHP&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;global state, framework-first mindset, procedural ใน struct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐍 &lt;strong&gt;Python&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;exception for control flow, decorator ซ้อน decorator, &lt;code&gt;__init__&lt;/code&gt; magic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ทั้งหมดนี้ไม่ใช่ของไม่ดี — แต่มันคือ "เครื่องมือของอีกภาษา" พอเอามาใช้ใน Go โดยไม่ปรับ mindset — มันกลายเป็น code ที่อ่านยาก, ช้า, และที่สำคัญคือ &lt;strong&gt;ไม่ใช่ Go&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ☕ Java → Go: OOP Overdose
&lt;/h2&gt;

&lt;p&gt;นี่คือกลุ่มที่เจอบ่อยที่สุด เพราะคนเขียน Java เยอะ และ Go ถูกใช้แทนที่ Java ใน microservices พอดี&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface ทุกอย่าง — "เผื่อไว้"
&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;// ❌ สำเนียง Java — interface ที่มี implementation เดียว&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&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="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="n"&gt;Save&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;user&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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PostgresUserRepo&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;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// ... implement ทั้งสอง method&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Java คุณต้องมี interface เพราะ DI framework (Spring) ต้องการมัน และเพราะการ mock ใน Java ทำผ่าน interface&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ใน Go — interface ถูกใช้ต่างกันโดยสิ้นเชิง:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The bigger the interface, the weaker the abstraction."&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go interfaces should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เล็ก (1-3 methods)&lt;/li&gt;
&lt;li&gt;ประกาศฝั่ง &lt;strong&gt;consumer&lt;/strong&gt; (คนใช้), ไม่ใช่ฝั่ง producer (คนสร้าง)&lt;/li&gt;
&lt;li&gt;ใช้เมื่อมีอย่างน้อย 2 implementations — ไม่ใช่ "เผื่อไว้"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ วิธี Go — ประกาศ interface ที่ consumer&lt;/span&gt;
&lt;span class="c"&gt;// user_service.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userFinder&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&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="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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserService&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;users&lt;/span&gt; &lt;span class="n"&gt;userFinder&lt;/span&gt; &lt;span class="c"&gt;// ← interface เกิดตรงนี้ — เล็ก, ใช้จริง&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// user_repo.go — ฝั่งนี้ไม่มี interface เลย&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserRepo&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;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&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;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepo&lt;/span&gt;&lt;span class="p"&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="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="o"&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;/p&gt;

&lt;ul&gt;
&lt;li&gt;interface เกิดที่คนใช้ → คนใช้บอกเองว่าอยากได้แค่ &lt;code&gt;FindByID&lt;/code&gt; ไม่เอาทั้ง &lt;code&gt;Save&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt;, &lt;code&gt;Update&lt;/code&gt;&lt;sup id="fnref3"&gt;3&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;ไม่ต้องมี mock framework — แค่สร้าง &lt;code&gt;struct{ FindByID func(...) }&lt;/code&gt; ก็ test ได้&lt;/li&gt;
&lt;li&gt;อ่านโค้ดรู้ทันทีว่า service ใช้แค่ find — ไม่ได้ใช้ save&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getter/Setter ทุก field — "เผื่อมี logic ภายหลัง"
&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;// ❌ สำเนียง Java — getter/setter ทั้งที่ field เป็น public อยู่แล้ว&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;email&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetName&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;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&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;u&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="n"&gt;GetEmail&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;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&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;u&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="n"&gt;SetEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Java คุณทำแบบนี้เพราะ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bean convention — framework จะเรียก &lt;code&gt;getXxx()&lt;/code&gt; / &lt;code&gt;setXxx()&lt;/code&gt; อัตโนมัติ&lt;/li&gt;
&lt;li&gt;Encapsulation — เผื่อมี logic ใน getter/setter วันข้างหน้า&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;ใน Go — YAGNI&lt;sup id="fnref4"&gt;4&lt;/sup&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;// ✅ Go — field คือ field, อย่า wrap โดยไม่จำเป็น&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าวันข้างหน้ามี logic จริง ๆ — ค่อยเปลี่ยนเป็น method ตอนนั้น compiler จะฟ้องทุกที่ที่ใช้ &lt;code&gt;user.Name&lt;/code&gt; ทำให้ refactor ปลอดภัย&lt;/p&gt;

&lt;h3&gt;
  
  
  "Architecture" ก่อนเขียนโค้ด
&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;// ❌ สำเนียง Java — โครงสร้างโฟลเดอร์ที่มาก่อนปัญหา&lt;/span&gt;
&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;user_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;user_service_interface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;user_repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;user_repository_interface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;user_dto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
    &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;string_util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับ Go — เริ่มจาก flat structure ก่อน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ Go — เริ่มเรียบ ๆ, แยกเมื่อจำเป็น&lt;/span&gt;
&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;          &lt;span class="c"&gt;// User struct + UserRepo + UserService&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;user_test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;       &lt;span class="c"&gt;// HTTP handlers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;พอโปรเจกต์ใหญ่ขึ้นก็ค่อยแยก package — แต่ไม่ใช่เพราะ "architecture บอกว่าต้องแยก" — แต่เพราะโค้ดเรียกร้องให้แยก&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💎 Ruby → Go: Magic มากเกิน
&lt;/h2&gt;

&lt;p&gt;คน Ruby โดยเฉพาะ Rails dev — คุ้นเคยกับ "convention over configuration" และ metaprogramming&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;method_missing&lt;/code&gt; ใน Go — ไม่มีวันได้
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ruby — dynamic method&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"find_by_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"find_by_"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# ... query database&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a@b.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Magic!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Go — ไม่มีทาง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go — explicit methods เท่านั้น&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;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FindByEmail&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;email&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="c"&gt;// เขียนเองทีละ method — ชัดเจน, grep ได้, IDE autocomplete ได้&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หลายคนที่ย้ายมาจาก Ruby จะรู้สึกว่า Go "verbose" — แต่นั่นคือ &lt;strong&gt;feature&lt;/strong&gt; ไม่ใช่ bug: โค้ด Go อ่านแล้วรู้ทันทีว่าทำอะไร โดยไม่ต้องรันก่อน&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;nil&lt;/code&gt; ≠ &lt;code&gt;nil&lt;/code&gt; — หลุมพรางที่ Ruby dev มองข้าม
&lt;/h3&gt;

&lt;p&gt;ใน Ruby — &lt;code&gt;nil&lt;/code&gt; คือ &lt;code&gt;nil&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="c1"&gt;# ไม่พัง&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Go — &lt;code&gt;nil&lt;/code&gt; มี type:&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;// ❌ bug คลาสสิก&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ← nil pointer to User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetError&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ← ไม่ใช่ nil error! เป็น non-nil interface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// เทียบกัน&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// true&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;GetError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// false! — เพราะ interface มี type แต่ว่า nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นี่คือหลุมพรางที่ Ruby dev เจอเกือบทุกคน — interface ใน Go ประกอบด้วย (type, value) ไม่ใช่แค่ value&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🐘 PHP → Go: Frameworks First
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ทุกอย่างต้องมี framework
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PHP — Laravel&lt;/span&gt;
&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/users/{id}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;UserController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'show'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ สำเนียง PHP — ตามหา "Go framework" ที่เหมือน Laravel&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/someone/goravel"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ใน Go — stdlib &lt;code&gt;net/http&lt;/code&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;// ✅ Go — router ใน 3 บรรทัด&lt;/span&gt;
&lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /users/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handleGetUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go 1.22+ รองรับ method-based routing ใน stdlib แล้ว ไม่ต้องหา framework เล็ก ๆ มาทำเรื่องนี้&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Global state
&lt;/h3&gt;

&lt;p&gt;PHP dev คุ้นเคยกับการใช้ global:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PHP&lt;/span&gt;
&lt;span class="nv"&gt;$db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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;ใน Go — injection คือคำตอบ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ Go — dependency injection ผ่าน constructor ง่าย ๆ&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Handler&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;svc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserService&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;NewHandler&lt;/span&gt;&lt;span class="p"&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;UserService&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;&amp;amp;&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;svc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svc&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;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="s"&gt;"42"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ไม่ต้องมี DI framework — แค่ส่ง &lt;code&gt;*UserService&lt;/code&gt; เข้า constructor ก็ได้ละ&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 Python → Go: Exception Mindset
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error คือ Exception
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python — try/except
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;DatabaseError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ สำเนียง Python — panic แทน return error&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&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;// ❌ อย่าทำ! Go ไม่ได้ใช้ panic เป็น control flow&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Go — error คือ value:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ Go — return error, ให้ caller จัดการ&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SELECT * FROM users WHERE id = $1"&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="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;h3&gt;
  
  
  Decorator ซ้อน Decorator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python
&lt;/span&gt;&lt;span class="nd"&gt;@login_required&lt;/span&gt;
&lt;span class="nd"&gt;@validate_params&lt;/span&gt;
&lt;span class="nd"&gt;@cache_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ Go — middleware chain — ชัดเจนว่าอะไรเกิดก่อน-หลัง&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleGetUser&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="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ใน main:&lt;/span&gt;
&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;handleGetUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidateParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CacheResult&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ต่างกันที่ — ใน Go คุณเห็นลำดับชัดเจน (ข้างใน → ข้างนอก) — ใน Python มันซ้อนกันจากบนลงล่าง ทำให้ debug ลำบากเวลามี middleware หลายตัว&lt;/p&gt;




&lt;h2&gt;
  
  
  สรุป — เรียนรู้ Grammar ไม่พ้อง ต้องเรียนรู้ "สำเนียง"
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;สำเนียงเดิม&lt;/th&gt;
&lt;th&gt;อาการที่พบบ่อย&lt;/th&gt;
&lt;th&gt;ทางแก้&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;☕ Java&lt;/td&gt;
&lt;td&gt;interface ทั้งที่ใช้ implementation เดียว&lt;/td&gt;
&lt;td&gt;interface เล็ก ประกาศที่ consumer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;☕ Java&lt;/td&gt;
&lt;td&gt;getter/setter ครอบ field เปล่า ๆ&lt;/td&gt;
&lt;td&gt;field คือ field, refactor ทีหลัง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;☕ Java&lt;/td&gt;
&lt;td&gt;โฟลเดอร์แยกตาม layer ก่อนเขียนโค้ด&lt;/td&gt;
&lt;td&gt;เริ่ม flat, แยกเมื่อจำเป็น&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💎 Ruby&lt;/td&gt;
&lt;td&gt;พยายาม metaprogramming&lt;/td&gt;
&lt;td&gt;Go = explicit, embrace it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💎 Ruby&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nil&lt;/code&gt; ไม่ระวัง type&lt;/td&gt;
&lt;td&gt;เข้าใจว่า interface nil ≠ pointer nil&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐘 PHP&lt;/td&gt;
&lt;td&gt;ตามหา framework&lt;/td&gt;
&lt;td&gt;net/http พอ, Go 1.22+ มี routing ในตัว&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐘 PHP&lt;/td&gt;
&lt;td&gt;global state&lt;/td&gt;
&lt;td&gt;dependency injection ผ่าน constructor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐍 Python&lt;/td&gt;
&lt;td&gt;panic = exception&lt;/td&gt;
&lt;td&gt;error คือ value, return มัน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐍 Python&lt;/td&gt;
&lt;td&gt;decorator ซ้อน decorator&lt;/td&gt;
&lt;td&gt;middleware chain — ชัดเจนกว่า&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  วิธีแก้ — อ่าน Effective Go
&lt;/h2&gt;

&lt;p&gt;หลังจากเขียน Go มาได้สักเดือน — ผมแนะนำให้อ่าน &lt;a href="https://go.dev/doc/effective_go" rel="noopener noreferrer"&gt;Effective Go&lt;/a&gt; หนึ่งรอบ&lt;/p&gt;

&lt;p&gt;มันคือ "สำเนียง" ทางการของ Go — ไม่ใช่แค่ syntax — แต่มันสอนวิธีคิด: ทำไมใช้ &lt;code&gt;:=&lt;/code&gt; ตรงนี้, ทำไม interface ต้องเล็ก, ทำไม &lt;code&gt;init()&lt;/code&gt; ใช้แค่เมื่อจำเป็น&lt;/p&gt;

&lt;p&gt;และอีกเรื่องที่ช่วยได้มาก: &lt;strong&gt;อ่านโค้ด standard library&lt;/strong&gt;&lt;sup id="fnref8"&gt;8&lt;/sup&gt; — &lt;code&gt;net/http&lt;/code&gt;, &lt;code&gt;encoding/json&lt;/code&gt;, &lt;code&gt;database/sql&lt;/code&gt; — โค้ดพวกนี้คือตัวอย่างของ "สำเนียง Go" ที่ดีที่สุด&lt;/p&gt;




&lt;h2&gt;
  
  
  เชิงอรรถ
&lt;/h2&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;strong&gt;"NewXxx" vs "xxxBar":&lt;/strong&gt; ใน Go — constructor มักชื่อ &lt;code&gt;NewXxx&lt;/code&gt; — แต่ใช้เฉพาะเมื่อมันมี logic เช่น validate input หรือตั้งค่า default — ไม่ใช่แค่ &lt;code&gt;return &amp;amp;Xxx{}&lt;/code&gt; เปล่า ๆ&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;strong&gt;"The bigger the interface, the weaker the abstraction"&lt;/strong&gt; — Rob Pike, Go Proverbs — &lt;a href="https://go-proverbs.github.io" rel="noopener noreferrer"&gt;https://go-proverbs.github.io&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;strong&gt;Interface ที่ consumer:&lt;/strong&gt; นี่คือหนึ่งในแนวคิดที่แตกต่างจาก Java มากที่สุด — ใน Java, interface ถูกประกาศโดย library owner ใน Go, interface ถูกประกาศโดย library user — ทำให้ dependency กลับทิศ, สะอาดกว่า&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;strong&gt;YAGNI:&lt;/strong&gt; "You Aren't Gonna Need It" — หลักการออกแบบที่บอกว่าอย่าเขียนโค้ดสำหรับ requirement ที่ยังไม่มี — เพราะ requirement มักเปลี่ยน หรือไม่เกิดเลย&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;strong&gt;Package structure:&lt;/strong&gt; มี talk ชื่อดังโดย Ben Johnson ชื่อ "Standard Package Layout" — &lt;a href="https://www.gobeyond.dev/standard-package-layout/" rel="noopener noreferrer"&gt;https://www.gobeyond.dev/standard-package-layout/&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;strong&gt;Interface nil ≠ pointer nil:&lt;/strong&gt; interface ใน Go เก็บ (type, value) เป็น tuple — &lt;code&gt;(*MyError)(nil)&lt;/code&gt; มี type = &lt;code&gt;*MyError&lt;/code&gt; แต่ value = nil — interface โดยรวมไม่ใช่ nil อ่านเพิ่ม: &lt;a href="https://go.dev/doc/faq#nil_error" rel="noopener noreferrer"&gt;https://go.dev/doc/faq#nil_error&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;strong&gt;Go 1.22 routing:&lt;/strong&gt; &lt;code&gt;http.ServeMux&lt;/code&gt; รองรับ &lt;code&gt;GET /users/{id}&lt;/code&gt; ตั้งแต่ Go 1.22 — &lt;a href="https://go.dev/blog/routing-enhancements" rel="noopener noreferrer"&gt;https://go.dev/blog/routing-enhancements&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;strong&gt;อ่าน standard library:&lt;/strong&gt; Go standard library คือ textbook ที่ดีที่สุด — เขียนโดยคนเดียวกันกับที่ออกแบบภาษา มันคือ "สำเนียง Go" ที่ถูกต้องและสะอาดที่สุดที่มีอยู่&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>go</category>
      <category>bestpractices</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Double Report — เมื่อ Error ถูก Log ซ้ำซากใน Go</title>
      <dc:creator>Gophernment Co</dc:creator>
      <pubDate>Mon, 29 Jun 2026 10:31:35 +0000</pubDate>
      <link>https://dev.to/gophernment/double-report-emuue-error-thuuk-log-chamchaakain-go-1607</link>
      <guid>https://dev.to/gophernment/double-report-emuue-error-thuuk-log-chamchaakain-go-1607</guid>
      <description>&lt;h2&gt;
  
  
  Double Report — เมื่อ Error ถูก Log ซ้ำซากใน Go
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;หรือที่ฝรั่งเรียกกันว่า "log and return" anti-pattern&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;เคยเจอไหม? เปิด log มาดู error เดียวกันซ้ำ 3-4 บรรทัด ทั้งที่มันคือ error จากเหตุการณ์เดียวกัน เขียนโค้ดดี ๆ แต่ log ดูกระจายเหมือนเกิดแผ่นดินไหว&lt;/p&gt;

&lt;p&gt;นี่คือ &lt;strong&gt;Double Report&lt;/strong&gt; — anti-pattern ยอดฮิตที่เกิดขึ้นใน Go (และภาษาอื่น) เวลาที่เราไม่แน่ใจว่า "ควร log ตรงไหน" แล้วก็เลย... log ทุกที่&lt;/p&gt;




&lt;h2&gt;
  
  
  ปัญหาหน้าตาเป็นยังไง
&lt;/h2&gt;

&lt;p&gt;ลองดูตัวอย่าง — REST API ที่ query user จาก database:&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;// repository/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;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepo&lt;/span&gt;&lt;span class="p"&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="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;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SELECT * FROM users WHERE id = $1"&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;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to query user: %v"&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;// ❌ log ครั้งที่ 1&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;"FindByID: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;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;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;UserService&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;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;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error getting user %s: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ❌ log ครั้งที่ 2&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;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;UserHandler&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;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLParam&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="s"&gt;"id"&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;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"handler GetUser failed: %v"&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;// ❌ log ครั้งที่ 3&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"not found"&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;StatusNotFound&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;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;Log ที่ออกมา:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/06/29 17:00:01 failed to query user: connection refused
2026/06/29 17:00:01 error getting user 42: FindByID: connection refused
2026/06/29 17:00:01 handler GetUser failed: GetUser: FindByID: connection refused
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;error จริงมีแค่ &lt;code&gt;connection refused&lt;/code&gt; — แต่ log เป็นสามบรรทัด วนซ้ำ context เดิม สิ่งที่เกิดขึ้นคือเราโปะ context เพิ่ม (&lt;code&gt;FindByID:&lt;/code&gt;, &lt;code&gt;GetUser:&lt;/code&gt;) แล้ว log ทุกระดับ ผลลัพธ์ = noise&lt;/p&gt;

&lt;p&gt;ลองนึกภาพ production ที่มี request พร้อมกันเป็นร้อย — log จะอ่านยากขนาดไหน&lt;/p&gt;




&lt;h2&gt;
  
  
  ทำไมเราถึงเผลอทำ
&lt;/h2&gt;

&lt;p&gt;มันมาจาก mindset นี้:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"ไม่รู้ว่า caller จะ handle error ยังไง — log ไว้ก่อนดีกว่า เผื่อหาย"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;แต่ใน Go &lt;strong&gt;error คือ value&lt;/strong&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; — มันถูกส่งกลับขึ้นไปตาม call chain อยู่แล้ว คนเรียกมีสิทธิ์เต็มที่ที่จะตัดสินใจว่าจะ log, wrap, ignore, หรือเปลี่ยนเป็น error type อื่น&lt;/p&gt;

&lt;p&gt;พอเรา log ทุกชั้น — เรากำลังแย่งหน้าที่ของ caller โดยไม่จำเป็น&lt;/p&gt;




&lt;h2&gt;
  
  
  วิธีที่ถูกต้อง — Log หรือ Return อย่างใดอย่างหนึ่ง
&lt;/h2&gt;

&lt;p&gt;กฎง่าย ๆ ที่ community Go ยึดกัน:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Either log the error, or return it — never both&lt;/strong&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;แปลคือ: ในหนึ่งชั้นของ call chain — คุณเลือกทำอย่างใดอย่างหนึ่ง:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log แล้ว swallow&lt;/strong&gt; — เมื่อคุณจัดการ error ได้เรียบร้อยแล้ว และไม่อยากให้มันไปต่อ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return error&lt;/strong&gt; — เมื่อคุณยังจัดการไม่ได้ อยากให้คนเรียกตัดสินใจ&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  แก้ที่ Repository — return เฉย ๆ, ไม่ต้อง log
&lt;/h3&gt;

&lt;p&gt;Repository เป็น layer ต่ำสุด — มันไม่รู้ว่า error เกิดจากอะไร, มีผลกระทบยังไง, ควร log ที่นี่ไหม มันก็แค่ return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepo&lt;/span&gt;&lt;span class="p"&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="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;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SELECT * FROM users WHERE id = $1"&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="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;"FindByID: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fmt.Errorf("FindByID: %w", err)&lt;/code&gt; — อันนี้คือ &lt;strong&gt;wrapping&lt;/strong&gt; ไม่ใช่ logging นะ — มันเติมชื่อ function เข้าไปใน error chain เพื่อให้ debug ได้&lt;sup id="fnref3"&gt;3&lt;/sup&gt; โดยไม่ต้อง log&lt;/p&gt;

&lt;h3&gt;
  
  
  แก้ที่ Service — business logic ตรงนี้
&lt;/h3&gt;

&lt;p&gt;Service รู้บริบท: "หา user id 42 ไม่เจอมันแปลว่า not found หรือ database down?" — นี่คือที่ที่ควร log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;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;UserService&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;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;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="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;"user %s not found"&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="p"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;// ⚠️ database connection error — service เห็นว่ามันคือ infra issue&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"database error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="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;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sql.ErrNoRows&lt;/code&gt; → ถือเป็นเรื่องปกติ แค่ return not found ไม่ต้อง log&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;connection refused&lt;/code&gt; → infrastructure issue → &lt;strong&gt;log ที่นี่ครั้งเดียว&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  แก้ที่ Handler — log เฉพาะที่ไม่คาดคิด
&lt;/h3&gt;

&lt;p&gt;Handler ควรตอบกลับ client — ไม่ต้อง log ทุก error&lt;sup id="fnref4"&gt;4&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="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;UserHandler&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;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLParam&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="s"&gt;"id"&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="c"&gt;// Service จัดการไปแล้ว — handler ก็แค่ map error → HTTP status&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;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="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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"not found"&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;StatusNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unhandled error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"internal error"&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;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;Log หลังแก้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/06/29 17:05:00 ERROR database error user_id=42 error="FindByID: connection refused"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;หนึ่งบรรทัด — หนึ่ง error — ครบ context&lt;/strong&gt; 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  สรุปหลักการ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ระดับ&lt;/th&gt;
&lt;th&gt;บทบาท&lt;/th&gt;
&lt;th&gt;Log?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data access — return error&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Business logic — ตัดสินใจว่า error คืออะไร&lt;/td&gt;
&lt;td&gt;✅ ครั้งเดียว&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Handler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP — map error เป็น status code&lt;/td&gt;
&lt;td&gt;❌ (ยกเว้น unexpected)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Golden Rule
&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;// ✅ ถูก&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;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;"doThing: %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;// return — ให้ caller จัดการ&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;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;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background task failed: %v"&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;// log — ไม่ return&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="c"&gt;// จบที่นี่&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;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;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error: %v"&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;// log แล้ว...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;                     &lt;span class="c"&gt;// return อีก = double report&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  เพิ่มเติม — Structured Logging แบบ Go 1.21+
&lt;/h2&gt;

&lt;p&gt;Go 1.21 เพิ่ม &lt;code&gt;log/slog&lt;/code&gt; เข้ามาใน stdlib&lt;sup id="fnref5"&gt;5&lt;/sup&gt; — ใช้ structured logging แทน &lt;code&gt;log.Printf&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;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"database error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อดี: log เป็น JSON, filter ด้วย field ได้, ต่อกับ observability tools (Loki, Datadog, Grafana) ง่าย&lt;/p&gt;




&lt;h2&gt;
  
  
  เชิงอรรถ — Go Naming Philosophy
&lt;/h2&gt;

&lt;p&gt;บทความนี้ตั้งใจใช้ชื่อ function แบบเดียวกับที่ standard library และ community Go ใช้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FindByID&lt;/code&gt; — prefix verb (&lt;code&gt;Find&lt;/code&gt;) + preposition (&lt;code&gt;By&lt;/code&gt;) + noun (&lt;code&gt;ID&lt;/code&gt;) — บอกว่า &lt;strong&gt;หา&lt;/strong&gt; ด้วย &lt;strong&gt;อะไร&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GetUser&lt;/code&gt; — prefix verb (&lt;code&gt;Get&lt;/code&gt;) + noun (&lt;code&gt;User&lt;/code&gt;) — บอกว่า &lt;strong&gt;เอาอะไร&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ชื่อแบบนี้มีที่มาจาก &lt;a href="https://go.dev/doc/effective_go#names" rel="noopener noreferrer"&gt;Effective Go&lt;/a&gt; — &lt;em&gt;"the name of a method should say what it does"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Function สั้น ตรงประเด็น อ่านแล้วรู้ว่าทำอะไร — โดยไม่ต้องมีคำว่า &lt;code&gt;Do&lt;/code&gt;, &lt;code&gt;Handle&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt; มาต่อข้างหน้าโดยไม่จำเป็น&lt;/p&gt;




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

&lt;p&gt;Double Report เกิดจากความไม่แน่ใจมากกว่าความผิดพลาด — พอเข้าใจ Go error handling philosophy แล้วจะเห็นว่ามันสวย:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt; — just return&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service&lt;/strong&gt; — ตัดสินใจว่า error คืออะไร &lt;strong&gt;แล้ว log ครั้งเดียว&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handler&lt;/strong&gt; — map error → HTTP status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;แค่นี้ log ก็สะอาด อ่านรู้เรื่อง ดีบั๊กง่าย 🧹&lt;/p&gt;







&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;strong&gt;"Error คือ value"&lt;/strong&gt; — หนึ่งใน Go proverbs โดย Rob Pike: error ไม่ใช่ exception ที่ต้อง throw/catch — มันคือค่า return ธรรมดาที่ caller ตรวจสอบด้วย &lt;code&gt;if err != nil { ... }&lt;/code&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;strong&gt;"Either log or return"&lt;/strong&gt; — Dave Cheney, ผู้เขียนบทความคลาสสิคเรื่อง Go error handling: "Don't just check errors, handle them gracefully" — &lt;a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully" rel="noopener noreferrer"&gt;https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;strong&gt;Error wrapping:&lt;/strong&gt; &lt;code&gt;fmt.Errorf("context: %w", err)&lt;/code&gt; ใช้ &lt;code&gt;%w&lt;/code&gt; verb เพื่อ wrap error โดยไม่เสีย error chain — &lt;code&gt;errors.Is()&lt;/code&gt; และ &lt;code&gt;errors.As()&lt;/code&gt; จะ traverse ผ่าน wrapped errors ได้&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;strong&gt;Handler logging:&lt;/strong&gt; ถ้าใช้ HTTP middleware (เช่น &lt;code&gt;chi&lt;/code&gt;, &lt;code&gt;echo&lt;/code&gt;, &lt;code&gt;gin&lt;/code&gt;) — หลายตัวมี request logger ในตัวอยู่แล้ว ไม่ต้อง log ซ้ำใน handler&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;log/slog&lt;/code&gt;:&lt;/strong&gt; structured logging ใน Go 1.21+ — ใช้ key=value pairs แทน format string — ทำให้ log parse ด้วยเครื่องมือ automation ได้ &lt;a href="https://go.dev/blog/slog" rel="noopener noreferrer"&gt;https://go.dev/blog/slog&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>go</category>
      <category>bestpractices</category>
      <category>errors</category>
    </item>
    <item>
      <title>Go 1.27 — Generic Methods, JSON v2, UUID ใน stdlib ฯลฯ</title>
      <dc:creator>Gophernment Co</dc:creator>
      <pubDate>Mon, 29 Jun 2026 10:23:12 +0000</pubDate>
      <link>https://dev.to/gophernment/go-127-generic-methods-json-v2-uuid-ain-stdlib-l-2ono</link>
      <guid>https://dev.to/gophernment/go-127-generic-methods-json-v2-uuid-ain-stdlib-l-2ono</guid>
      <description>&lt;h2&gt;
  
  
  Go 1.27 — Generic Methods, JSON v2, UUID ใน stdlib ฯลฯ
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Go 1.27 ยังไม่ release นะทุกคน release notes ยังเป็น &lt;strong&gt;draft&lt;/strong&gt; อยู่ คาดว่าจะออกจริงประมาณ &lt;strong&gt;สิงหาคม 2026&lt;/strong&gt;&lt;br&gt;
เราไปดูกันดีกว่าว่ารอบนี้มีอะไรน่าตื่นเต้นบ้าง&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ส่วนตัวผมตื่นเต้นกับเวอร์ชันนี้มาก เพราะมีของที่รอมานานหลายอย่างโผล่มาใน draft นี้พร้อมกัน ทั้ง &lt;strong&gt;Generic Methods&lt;/strong&gt; ที่คนเรียกร้องกันมาตั้งแต่ Go 1.18, &lt;strong&gt;JSON v2&lt;/strong&gt; ที่คุยกันมาเป็นปี ๆ, และ &lt;strong&gt;UUID ใน stdlib&lt;/strong&gt; ที่หลายคน (รวมผม) รอมานานจนเกือบลืม&lt;/p&gt;

&lt;p&gt;งั้นมาเริ่มกันเลย&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Generic Methods — ของที่รอมา 4 ปี
&lt;/h2&gt;

&lt;h3&gt;
  
  
  เล่าเรื่องก่อน
&lt;/h3&gt;

&lt;p&gt;ย้อนกลับไปตอน Go 1.18 (ปี 2022) — Go เพิ่งใส่ generics เข้ามาครั้งแรก&lt;sup id="fnref1"&gt;1&lt;/sup&gt; ตอนนั้น community ดีใจกันใหญ่ แต่ก็มีเสียงบ่นปนมาด้วยว่า "เฮ้ย แล้ว generic methods ล่ะ?" &lt;/p&gt;

&lt;p&gt;ปัญหามันเป็นแบบนี้...&lt;/p&gt;

&lt;p&gt;สมมติเราเขียน &lt;code&gt;Set[T]&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;type&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;T&lt;/span&gt; &lt;span class="n"&gt;comparable&lt;/span&gt;&lt;span class="p"&gt;]&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;m&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;struct&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;แล้วเราอยากมี method &lt;code&gt;Map&lt;/code&gt; — แปลงจาก &lt;code&gt;Set[int]&lt;/code&gt; เป็น &lt;code&gt;Set[string]&lt;/code&gt; — ใน Go 1.18-1.26 เราทำแบบนี้ไม่ได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ❌ ทำไม่ได้ใน Go ≤ 1.26 — method ห้ามมี type parameter ตัวเอง&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;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;U&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;f&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;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;U&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="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// compiler จะด่า "method must have no type parameters"&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; เพราะตอน Go 1.18 ทีมออกแบบเลือกที่จะ implement แค่ "generic functions" ในระดับ package ก่อน — method ยังต้องเหมือนเดิม&lt;sup id="fnref2"&gt;2&lt;/sup&gt; การจะทำให้ method มี type parameter ของตัวเองได้ มันต้องแก้ compiler, runtime, และ reflect ใหม่ยกใหญ่ &lt;/p&gt;

&lt;h3&gt;
  
  
  จนมาถึง Go 1.27
&lt;/h3&gt;

&lt;p&gt;ในที่สุด — &lt;strong&gt;method มี type parameter ของตัวเองได้แล้ว!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ✅ Go 1.27 — ได้แล้วครับ!&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;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;U&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;f&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;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;U&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="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;make&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="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&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;m&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&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;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะเกิดอะไรขึ้น? เรา chain method สวย ๆ ได้เลย:&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;scores&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// แปลงเป็น string → filter → นับ — ในบรรทัดเดียว!&lt;/span&gt;
&lt;span class="n"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Map&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;n&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;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;"score_%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&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;Filter&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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;7&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ก่อน Go 1.27:&lt;/strong&gt; ต้องเขียนแบบนี้ — function กระจายอยู่ใน package เปล่า ๆ อ่านไม่รู้เรื่อง:&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;labels&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FilterSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MapSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&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;n&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อจำกัดที่ยังมีอยู่: &lt;strong&gt;interface method ยังมี type parameter ไม่ได้&lt;/strong&gt; และ generic method ก็ implement interface ไม่ได้&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ทำไมถึงสำคัญ?
&lt;/h3&gt;

&lt;p&gt;ลองนึกภาพเวลาคุณเขียน library ที่ return type เป็น generic container — &lt;code&gt;Set[T]&lt;/code&gt;, &lt;code&gt;Result[T]&lt;/code&gt;, &lt;code&gt;Option[T]&lt;/code&gt; — คุณอยากให้คนใช้ chain operation ต่อได้ใช่ไหม? Generic methods ทำให้สิ่งนี้เป็นจริง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Footnote:&lt;/strong&gt; ทีม Go ใช้เวลานานกับฟีเจอร์นี้เพราะ philosophy ของ Go คือ "เรียบง่ายก่อน ถูกต้องเสมอ" การเพิ่ม generic methods ต้องทำให้แน่ใจว่ามันไม่ทำให้ type inference ช้าลง ไม่มี edge case แปลก ๆ และการคอมไพล์ยังเร็วเหมือนเดิม&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📦 JSON v2 — encoding/json ภาคใหม่
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ทำไมต้อง v2?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;encoding/json&lt;/code&gt; ปัจจุบันอยู่กับเรามาตั้งแต่ Go 1.0 (ปี 2012) มันทำงานได้ดี แต่ปัญหาสะสมมาตลอด 13 ปี:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; — ใช้ reflection เยอะ&lt;sup id="fnref4"&gt;4&lt;/sup&gt; marshal/unmarshal struct ที่มี nested field เยอะ ๆ แล้วช้า&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API ไม่ยืดหยุ่น&lt;/strong&gt; — อยาก customise marshaling? ต้อง implement &lt;code&gt;json.Marshaler&lt;/code&gt; เองทั้งก้อน ไม่ใช่แค่บาง field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming ไม่สะดวก&lt;/strong&gt; — ถ้าข้อมูล JSON มาเป็น streaming (เช่น WebSocket, Server-Sent Events) การ decode ด้วย &lt;code&gt;json.Decoder&lt;/code&gt; ตอนนี้ยังมีข้อจำกัดหลายอย่าง&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error messages&lt;/strong&gt; — ถ้า json ไม่ตรง schema error message อ่านยาก — "unexpected token" แล้วจบ ไม่บอกว่าบรรทัดไหน field ไหน&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  มีอะไรใน v2
&lt;/h3&gt;

&lt;p&gt;Go 1.27 เอา &lt;code&gt;encoding/json/v2&lt;/code&gt; เข้ามาใน stdlib — ยังเป็น experimental แต่มันคือรากฐานของ JSON handling ยุคใหม่&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;encoding/json/v2&lt;/code&gt;&lt;/strong&gt; — API ใหม่ที่ยืดหยุ่นกว่า, เร็วกว่า&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;encoding/json/jsontext&lt;/code&gt;&lt;/strong&gt; — low-level tokenizer/encoder สำหรับคนที่อยากทำอะไร custom กับ JSON stream โดยตรง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;รายละเอียดยังไม่เปิดเผยหมด เพราะ v2 ยังอยู่ระหว่างพัฒนา แต่ที่แน่ ๆ คือมันจะแก้ pain point ทั้ง 4 ข้อข้างบน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;สำหรับ junior dev:&lt;/strong&gt; ถ้าตอนนี้คุณใช้ &lt;code&gt;encoding/json&lt;/code&gt; อยู่ — ใช้ต่อไปได้เลย ไม่ต้องรีบเปลี่ยน &lt;code&gt;v2&lt;/code&gt; ยังเป็น experimental และ API อาจเปลี่ยนได้ระหว่างทาง&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🆔 UUID ใน stdlib — ในที่สุด!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  มันคืออะไร?
&lt;/h3&gt;

&lt;p&gt;UUID (Universally Unique Identifier) คือ ID ที่สร้างแล้ว "ไม่ซ้ำ" กับใครในโลก — ใช้แทน auto-increment ID เวลาอยากให้ ID เดาจากภายนอกไม่ได้&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;ก่อน Go 1.27 — คุณต้อง &lt;code&gt;go get github.com/google/uuid&lt;/code&gt; (ดาวน์โหลด ~1.3M) หรือ &lt;code&gt;gofrs/uuid&lt;/code&gt; — ซึ่งก็ดีนะ แต่คำถามคือ "ทำไมของพื้นฐานขนาดนี้ถึงไม่อยู่ใน stdlib?"&lt;/p&gt;

&lt;h3&gt;
  
  
  ใน Go 1.27
&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;import&lt;/span&gt; &lt;span class="s"&gt;"uuid"&lt;/span&gt;

&lt;span class="c"&gt;// UUID v7 — timestamp-based, sort ได้ตามเวลา&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;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewV7&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="c"&gt;// Output: 019309e2-29a3-7b2f-a4d5-80d44c4b3f1d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ทำไมต้อง UUID v7?&lt;/strong&gt; UUID ธรรมดา (v4) ใช้ random ล้วน ๆ — ทำให้ index ใน database B-tree&lt;sup id="fnref6"&gt;6&lt;/sup&gt; กระจัดกระจาย UUID v7 เอา timestamp ใส่ไว้ข้างหน้า ทำให้ "sort ได้ตามเวลา" — ข้อนี้สำคัญมากสำหรับ performance database&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;เปรียบเทียบง่าย ๆ: UUID v4 = จับสลากแบบสุ่ม / UUID v7 = จับสลากแบบเรียงตามคิว&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧪 SIMD ใน Go — CPU มัลติทาสกิ้ง
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SIMD คืออะไร?
&lt;/h3&gt;

&lt;p&gt;สมมติคุณมีตัวเลข 8 ตัว แล้วอยากบวก 1 ให้ทุกตัว — ปกติ CPU ทำทีละตัว 8 รอบ&lt;/p&gt;

&lt;p&gt;SIMD (Single Instruction, Multiple Data) คือการทำ "คำสั่งเดียว" กับ "ข้อมูลหลายตัวพร้อมกัน" — เหมือนเชฟที่หั่นผักทีเดียว 8 หัวแทนที่จะหั่นทีละหัว&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;// Concept (ยังไม่ใช่ syntax จริง)&lt;/span&gt;
&lt;span class="c"&gt;// ปกติ: for i := range data { data[i] += 1 }  ← 8 รอบ&lt;/span&gt;
&lt;span class="c"&gt;// SIMD:  data += [1,1,1,1,1,1,1,1]            ← รอบเดียว!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go 1.27 มี &lt;code&gt;simd&lt;/code&gt; + &lt;code&gt;simd/archsimd&lt;/code&gt; เป็น &lt;strong&gt;experimental package&lt;/strong&gt; — ยังต้อง build ด้วย &lt;code&gt;GOEXPERIMENT=simd&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ใช้กับอะไรได้บ้าง:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image processing (ปรับแสง, resize พร้อมกันหลาย pixel)&lt;/li&gt;
&lt;li&gt;Machine learning inference&lt;/li&gt;
&lt;li&gt;Cryptography (เข้ารหัสหลาย block พร้อมกัน)&lt;/li&gt;
&lt;li&gt;Audio/Video encoding&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 ต้องบอกว่า SIMD ใน Go ยังเด็กมาก — อย่าเพิ่งเอาไปใช้ production แต่มันเป็นการวางรากฐานที่สำคัญมาก&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ⚡ Runtime Improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Goroutine Leak Profile
&lt;/h3&gt;

&lt;p&gt;goroutine&lt;sup id="fnref7"&gt;7&lt;/sup&gt; รั่วคือฝันร้ายของ Go dev — มันใช้ memory ไปเรื่อย ๆ โดยไม่รู้ตัว&lt;/p&gt;

&lt;p&gt;Go 1.27 เพิ่ม &lt;strong&gt;goroutine leak profile&lt;/strong&gt; — ใช้กับ &lt;code&gt;go tool pprof&lt;/code&gt; เพื่อดูว่า goroutine ตัวไหนที่เกิดแล้วไม่ยอมตาย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ดู goroutine leak profile (คาดว่าใช้ประมาณนี้)&lt;/span&gt;
go tool pprof http://localhost:6060/debug/pprof/goroutine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Faster Memory Allocation
&lt;/h3&gt;

&lt;p&gt;รายละเอียดยังไม่เปิดเผยมาก แต่ทีม runtime บอกว่าปรับปรุง allocator ให้เร็วขึ้น — โดยเฉพาะเคส allocate ของเล็ก ๆ จำนวนมาก&lt;/p&gt;




&lt;h2&gt;
  
  
  🖥️ Ports
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS (Darwin)&lt;/strong&gt; — รองรับเวอร์ชันล่าสุด&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PowerPC 64-bit (ppc64)&lt;/strong&gt; — กลับมาอยู่ใน first-class port&lt;sup id="fnref8"&gt;8&lt;/sup&gt; ใครใช้ IBM Power server ยิ้มได้&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📊 สรุปให้
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;หมวด&lt;/th&gt;
&lt;th&gt;ฟีเจอร์&lt;/th&gt;
&lt;th&gt;น่าตื่นเต้น?&lt;/th&gt;
&lt;th&gt;ใช้ production ได้เลย?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Generic Methods&lt;/td&gt;
&lt;td&gt;🔥🔥🔥&lt;/td&gt;
&lt;td&gt;✅ (หลังออกจริง)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Struct Literal Keys&lt;/td&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Function Type Inference&lt;/td&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stdlib&lt;/td&gt;
&lt;td&gt;&lt;code&gt;encoding/json/v2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔥🔥&lt;/td&gt;
&lt;td&gt;🧪 experimental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stdlib&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uuid&lt;/code&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;stdlib&lt;/td&gt;
&lt;td&gt;&lt;code&gt;crypto/mldsa&lt;/code&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;stdlib&lt;/td&gt;
&lt;td&gt;&lt;code&gt;simd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔥🔥&lt;/td&gt;
&lt;td&gt;🧪 experimental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Faster Allocation&lt;/td&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Goroutine Leak Profile&lt;/td&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Go 1.27 ยังอีกประมาณ 2 เดือนกว่าจะออกจริง — release notes อาจเปลี่ยนได้ ติดตามที่ &lt;a href="https://go.dev/doc/go1.27" rel="noopener noreferrer"&gt;go.dev/doc/go1.27&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📚 Footnotes — อ่านเพิ่มเติมสำหรับคนอยากรู้ลึก
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://go.dev/doc/go1.27" rel="noopener noreferrer"&gt;Go 1.27 Release Notes (Draft)&lt;/a&gt; — The Go Programming Language&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;strong&gt;Go Generics (1.18):&lt;/strong&gt; Go ใส่ type parameter เข้ามาในปี 2022 — ใช้ syntax &lt;code&gt;func F[T any](t T)&lt;/code&gt; และ constraint แบบ &lt;code&gt;[T interface{ ~int | ~float64 }]&lt;/code&gt; — ทำให้เขียน function ที่ทำงานกับหลาย type ได้โดยไม่ต้องใช้ &lt;code&gt;interface{}&lt;/code&gt; + type assertion&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;strong&gt;ทำไม generic methods ถึงยาก:&lt;/strong&gt; เพราะ Go method ทำงานผ่าน "method set" ซึ่งถูกกำหนดที่ compile time การเพิ่ม type parameter ให้ method ทำให้ method set กลายเป็น "generic" — ต้องแก้ทั้ง compiler (SSA generation), runtime (interface dispatch), และ reflect&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;strong&gt;Interface กับ generic method:&lt;/strong&gt; ใจความคือ interface เป็น "สัญญา" ที่ concrete type ต้อง implement — ถ้า method มี type parameter มันก็เหมือน "สัญญาที่ยังไม่ได้ระบุประเภท" เลย implement interface ไม่ได้&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;strong&gt;Reflection:&lt;/strong&gt; &lt;code&gt;encoding/json&lt;/code&gt; ใช้ &lt;code&gt;reflect&lt;/code&gt; เพื่ออ่าน struct tags (&lt;code&gt;json:"name"&lt;/code&gt;) และ type ของ field — การใช้ reflect มี overhead เพราะต้อง resolve type ที่ runtime แทนที่จะทำตอน compile time&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;strong&gt;UUID vs Auto-Increment:&lt;/strong&gt; Auto-increment ID (1, 2, 3, ...) ทำให้คนนอกเดาได้ว่ามี record กี่อัน และเดา ID ของ record ถัดไปได้ UUID แก้ปัญหานี้&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;strong&gt;B-tree Index:&lt;/strong&gt; index มาตรฐานใน database (PostgreSQL, MySQL) — ทำงานเร็วเมื่อข้อมูล "เรียงตามลำดับ" UUID v4 สุ่ม ทำให้ B-tree ต้องจัดเรียงบ่อย UUID v7 เรียงตามเวลา ทำให้ insert แล้ว index เป็นระเบียบ — performance ดีกว่า&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;strong&gt;Goroutine:&lt;/strong&gt; lightweight thread ของ Go — สร้างได้เป็นแสน ๆ ตัวโดยใช้ memory น้อย แต่ถ้า goroutine เกิดแล้วไม่มีทางจบ (leak) มันจะกิน memory สะสมไปเรื่อย ๆ&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;strong&gt;First-class port:&lt;/strong&gt; หมายถึง architecture ที่ได้รับการทดสอบแบบเต็มรูปแบบ CI/CD — มี binary prebuilt ให้บน go.dev/dl — ไม่ใช่แค่ "cross-compile ได้"&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>release</category>
    </item>
  </channel>
</rss>
