1 คะแนน โดย GN⁺ 2024-02-08 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ตัวแปรของลูป for ถูกเปลี่ยนให้สร้างใหม่ในแต่ละรอบ เพื่อหลีกเลี่ยงบั๊กจากการแชร์โดยไม่ตั้งใจ และสามารถใช้ range กับจำนวนเต็มได้แล้ว
  • ความเข้ากันได้ ยังคงรักษาคำมั่นสัญญาเรื่องความเข้ากันได้ของ Go 1 และคาดว่าโปรแกรม Go แทบทั้งหมดจะยังคอมไพล์และรันได้เหมือนเดิม
  • ใน workspace สามารถใช้ ไดเรกทอรี vendor ที่สร้างด้วย go work vendor ได้ และถ้ามี vendor ใน workspace ค่าเริ่มต้นของ -mod จะเป็น vendor
  • ยุติการรองรับ go get นอกโมดูลในโหมด legacy GOPATH เมื่อ GO111MODULE=off ส่วนคำสั่ง build อื่น ๆ เช่น go build และ go test ยังคงทำงานกับโปรแกรม legacy GOPATH ต่อไป
  • runtime ถูกเปลี่ยนให้วาง metadata ของ GC ตามชนิดไว้ใกล้กับ heap object ทำให้ประสิทธิภาพ CPU ของโปรแกรม Go ดีขึ้น 1–3% และลด overhead ของหน่วยความจำในหลายโปรแกรมลงประมาณ 1%
  • การจัดแนวที่อยู่ของ object บางส่วนอาจเปลี่ยนจากเดิมที่ 16 ไบต์ขึ้นไปเป็นการจัดแนว 8 ไบต์ โค้ด assembly ที่สมมติว่ามีการจัดแนวสูงกว่านี้ต้องแก้ไข และสามารถใช้ GOEXPERIMENT=noallocheaders เป็นทางเลี่ยงชั่วคราวได้
  • การ build แบบ PGO สามารถ devirtualize การเรียกได้มากขึ้น และเมื่อเปิดใช้ PGO ในชุดโปรแกรม Go ตัวแทน ประสิทธิภาพ runtime ดีขึ้น 2–14%
  • เพิ่มแพ็กเกจ v2 แรกใน standard library คือ math/rand/v2 โดยไม่สืบทอด Read, มี global generator ที่ใช้ seed แบบสุ่มเสมอ, มี source ChaCha8 และ PCG, และมีฟังก์ชัน generic N
  • รูปแบบ routing ของ net/http.ServeMux รองรับ method และ wildcard แล้ว โดยมีการแตกความเข้ากันได้ย้อนหลังเล็กน้อยใน pattern ที่มี {} และการจัดการ escaped path และสามารถคืนพฤติกรรมเดิมได้ด้วย GODEBUG=httpmuxgo121=1
  • ค่า default minimum version ของเซิร์ฟเวอร์ crypto/tls เมื่อไม่ได้ระบุถูกเปลี่ยนเป็น TLS 1.2 และ cipher suite ที่ไม่มี ECDHE ใน handshake ก่อน TLS 1.3 จะไม่ถูก提供เป็นค่า default
  • execution tracer ถูกปรับปรุงใหม่ทั้งหมด โดยใช้ OS clock บนแพลตฟอร์มส่วนใหญ่ และให้ข้อมูล trace แบบแบ่งส่วนที่ประมวลผลแบบ streaming ได้, duration ของ syscall ทั้งหมด, และข้อมูล OS thread ที่ goroutine เคยรัน
  • Web UI ของเครื่องมือ trace ถูกอัปเดตเป็นส่วนหนึ่งของงานรองรับ tracer ใหม่ และรองรับการแสดง thread-oriented view กับ duration ของ syscall ทั้งหมดใน trace ของโปรแกรมที่ build ด้วย Go 1.22 ขึ้นไป
  • vet ปรับคำเตือน loop closure ให้สอดคล้องกับความหมายของตัวแปรลูปใน Go 1.22 และเพิ่มคำเตือนสำหรับ append ที่ไม่มีการใช้ค่า, time.Since ที่ไม่ถูก defer ภายใน defer, และ key/value ของ log/slog ที่ไม่ตรงกัน
  • การ bootstrap ต้องใช้ Go 1.20 รุ่น point release สุดท้ายขึ้นไป และคาดว่า Go 1.24 จะต้องใช้ Go 1.22 รุ่น point release สุดท้ายขึ้นไป
  • บน darwin/amd64 Go toolchain จะสร้าง PIE เป็นค่า default และ Go 1.22 เป็นรุ่นสุดท้ายที่รันบน macOS 10.15 Catalina ส่วน Go 1.23 จะต้องใช้ macOS 11 Big Sur ขึ้นไป
  • เพิ่มชนิด Alias และฟังก์ชัน Unalias ใน go/types และเมื่อใช้ gotypesalias=1 client ต้องจัดการชนิด Alias
  • slices.Delete, DeleteFunc, Compact, CompactFunc, Replace จะ zero องค์ประกอบในช่วงที่ถูกย่อ และ Insert จะ panic เสมอเมื่อ i อยู่นอกช่วง
  • ฟังก์ชันอ่านไดเรกทอรีบน Windows อ่านรายการเป็น batch เพื่อลดจำนวน system call ทำให้ประสิทธิภาพดีขึ้นสูงสุด 30%

1 ความคิดเห็น

 
GN⁺ 2024-02-08
ความคิดเห็นจาก Hacker News
  • ตลอด 3 ปีที่ผ่านมาใช้ TypeScript เป็นหลัก แล้วเพิ่งเริ่มใช้ Go ไม่นานนี้ ตอนแรกก็กังวลนิดหน่อยเรื่องการขาดฟังก์ชันสำหรับอาร์เรย์และระบบ type ที่ยืดหยุ่นน้อยกว่า
    แต่พอลองตั้งโปรเจ็กต์ TypeScript ใหม่สำหรับทีมขนาด 4~5 คน ก็พบว่าเสียเวลาไปมากกับการ linting, การเลือกไลบรารี server routing, การเลือกเซิร์ฟเวอร์, มาตรฐานการเขียนโค้ด, การจัดการข้อผิดพลาดพื้นฐาน, การบังคับใช้ custom error หรือชนิด Result เพื่อหลีกเลี่ยงนรกของ try/catch ที่ซ้อนกัน, การทดสอบและ mocking, การตั้งค่า Prisma ฯลฯ
    ถึงจะชอบ TypeScript แต่การได้ใช้ภาษาที่มี standard library ที่ยอดเยี่ยม, type safety ที่เหมาะสม, และมาตรฐานการเขียนโค้ดที่มีมาให้ในตัว มันสะดวกมากจริงๆ Go ก็มี quirks อยู่บ้าง แต่ถือว่าดีมาก และการที่ routing เข้ามาอยู่ใน standard library ด้วยก็ยิ่งลดเรื่องที่ต้องคอยคิดลงไปอีกอย่าง ตอนนี้ก็รอแค่ฟังก์ชัน map/filter/find สำหรับ slice

    • ในจดหมายข่าว CTO มีการเปรียบเทียบ รีลีสของ TypeScript และ Go เมื่อไม่นานมานี้ และยก Go 1.22 เป็นตัวอย่าง
      TypeScript ยิ่งออกรีลีสก็ยิ่งซับซ้อนขึ้นเรื่อยๆ เพื่อผู้ใช้ระดับสูง ขณะที่ Go ใส่สิ่งที่ขาดไปอย่าง range สำหรับจำนวนเต็มเข้ามาเพื่อให้ใช้งานได้ง่ายขึ้น
      มันคล้ายกับภาคต่อของเกมที่ค่อยๆ เพิ่มการตั้งค่าและกลไกจนสุดท้ายต้องรีเซ็ตใหม่เพื่อผู้เล่นหน้าใหม่ แต่กับภาษาโปรแกรมมันทำแบบนั้นยากกว่า ดังนั้นก็ดีที่ Go ตระหนักถึงเรื่องนี้
    • รู้สึกคล้ายกัน สิ่งที่ทำให้อยู่กับ Go ต่อคือ build toolchain ที่เป็นหนึ่งเดียวกันอย่างน่าทึ่ง และสิ่งที่ทำได้ด้วย go:embed กับ go:generate ก็น่าประทับใจทุกครั้งที่เห็นในโปรเจ็กต์อื่น
      แพ็กเกจ golang.org/x ก็ดีมากเพราะมี implementation ที่เกี่ยวกับ RFC อินเทอร์เน็ตแทบทั้งหมดเตรียมไว้ให้ใช้ได้ทันที
    • ระหว่างที่ยังไม่มี map/filter/find สำหรับ slice ก็ใช้ ไลบรารี lo ที่ได้แรงบันดาลใจจาก lodash ได้
      เป็นเครื่องมือสารพัดประโยชน์ที่ใช้บ่อยในโปรเจ็กต์ Go: https://github.com/samber/lo
    • เพราะไม่ได้มีฟีเจอร์เยอะเกินไป เลยช่วยลด การตัดสินใจเล็กๆ น้อยๆ ลง และสวนทางกับสัญชาตญาณแต่มันกลับเพิ่มประสิทธิภาพการทำงานได้มาก
    • เคยทำงานให้ลูกค้าที่ใช้ TypeScript อยู่พักหนึ่ง สุดท้ายเลยเรียกมันว่า “Tricks Driven Development”
      คือพออ่านเอกสารเพื่อจะทำอะไรสักอย่าง ไม่นานก็จะมีใครสักคนมาบอก ทริกที่ใช้ได้จริง ซึ่งไม่ได้อยู่ในเอกสาร วนแบบนี้ซ้ำไปเรื่อยๆ
  • ถ้ารู้สึกว่า release notes ทางการค่อนข้างแห้งไปหน่อย มีเวอร์ชันแบบ interactive ทำไว้แล้ว: https://antonz.org/go-1-22

    • ตอนแรกยังไม่เข้าใจปัญหาเรื่องการแชร์ตัวแปรในลูป for ทันที แต่พอลองรันเองแล้วเห็นผลลัพธ์ที่ไม่คาดคิดก็ช่วยได้มาก
    • ตัวอย่าง Compact กับ Replace ทำให้งงนิดหน่อย ไม่แน่ใจว่านี่ถูกต้องหรือเปล่า
  • ใช้ Go มามากกว่า 9 ปี และ 4 ปีหลังมานี้ใช้ Dart เยอะเพราะ Flutter ซึ่งมองว่าสองภาษานี้มี ท่าทีต่อความซับซ้อน ตรงกันข้ามสุดๆ Dart พยายามเพิ่มทุกฟีเจอร์ที่เป็นไปได้ ส่วน Go ตรงข้าม
    อย่างแรก ใน Dart มีวิธี initialize ค่าในคลาสมากเกินไปจนเสียเวลาเยอะ ตำแหน่งที่ต้อง initialize ก็เปลี่ยนไปตามคำนำหน้า final/const/static/late ไม่ว่าจะเป็น constructor, factory, หรือ initState() ของ Flutter StatefulWidget และถ้ารีแฟกเตอร์ก็ต้องแก้โครงสร้างการ initialize ครั้งใหญ่ getter ที่ดูเหมือนตัวแปรแต่จริงๆ เป็นฟังก์ชันก็ยิ่งเพิ่มความสับสนใน codebase ใหม่
    อย่างที่สอง Dart มีฟีเจอร์อย่าง map, stream ฯลฯ ที่เหมาะกับการยัดทุกอย่างลงในบรรทัดเดียว เลยชวนให้เขียน โค้ดบรรทัดเดียว ที่เข้าใจยากได้อย่างรวดเร็ว แต่เวลา debug หรือเวลานักพัฒนารุ่นจูเนียร์เจอทั้งแฮ็กอย่าง firstWhereOrNull และพารามิเตอร์ทางเลือกอย่าง orElse: () => null ก็สับสนมาก แล้วยังมี null safety หรือ type error ที่เข้าใจยากจนทำงานต่อไม่ได้บ่อยๆ แต่พอเปลี่ยนมันเป็นลูป for แบบ Go ที่เรียบง่ายก็รู้สึกโล่งใจจริงๆ

    • ขอแชร์ทิปเกี่ยวกับ Dart ว่าสำหรับงานที่ไม่เกี่ยวกับ UI โดยตรง ไม่ควรใช้ initState เช่น ถ้าไม่ใช่ระดับตั้งค่า hint ของ text field ก็ควรหลีกเลี่ยง
      หน้า Flutter ส่วนใหญ่มีสิ่งที่ต้องทำไม่มาก หรือไม่ก็ให้ parent สร้างอ็อบเจ็กต์ MyPageController แล้ว initialize ตามต้องการ จากนั้น child page ก็ทำงานตาม controller นั้น ตัวอย่างเช่น ตอนผู้ใช้จะแก้ไขรายการในหน้ารายการ ก็สร้าง controller ใส่รายการลงไปแล้วส่งให้หน้าลูก พอกลับมาแล้ว parent ก็สามารถดูรายการใน controller หรือตัวแปรและ callback ต่างๆ เพื่อตัดสินใจว่าจะเปลี่ยน UI อย่างไร
      วิธีนี้ช่วยควบคุมการโต้ตอบระหว่างวิดเจ็ตได้ละเอียด โดยไม่ต้องไปแตะพวก InheritedWidget แน่นอนว่าควรใช้ร่วมกับไลบรารีจัดการสถานะ แต่ในทางปฏิบัติก็มีหลายกรณีที่ไม่ได้ใช้
    • เห็นด้วยว่า ความเรียบง่าย ของ Go คือข้อดีที่ใหญ่ที่สุด range ก็เช่นกัน สุดท้ายจำนวนตัวอักษรที่ต้องพิมพ์ก็ใกล้เคียงกับ comprehension แบบ forEach(f) เพียงแต่กระจายเป็นสามบรรทัดเลยดูยาวกว่า และถ้าเขียนออกมาตรงๆ ก็ไม่ได้มีอะไรใหญ่โต
      แต่ในเรื่องการ initialize ก็อยากให้ Go มีวิธีที่เป็นสำนวนมาตรฐานสำหรับ initialize ฟิลด์ของ struct ให้เป็นค่าบางอย่าง ถึงจะไม่มี constructor ก็ไม่เป็นไร แต่บางครั้งก็อยากให้ bool ถูก initialize เป็น true โดยปริยาย
    • initState เป็นการ override ของ StatefulWidget และไม่ได้ต่างจาก lifecycle event ในเฟรมเวิร์กอื่นๆ ทั้งยังเป็นแนวคิดฝั่ง Flutter จึงไม่ใช่อะไรที่น่าจะสับสนกับ constructor
      อาจจะสับสนได้ว่าควรใช้ constructor ปกติหรือ factory แต่จะบอกว่า Go เหนือกว่าด้วยเหตุผลเรื่อง initState ก็คงยาก
    • เราควร optimize ความสามารถในการ สื่อสารกันอย่างกระชับ ของผู้เชี่ยวชาญค่าตัวแพง การดึงเครื่องมือทางไวยากรณ์ที่ทรงพลังออกจากมือผู้เริ่มต้น มีแต่จะขัดขวางการพัฒนาความเชี่ยวชาญของพวกเขา
  • ตอน io.Copy คัดลอกจาก TCPConn ไปยัง UnixConn ถ้าเป็นไปได้จะใช้ system call splice(2) ของ Linux และจะใช้เมธอดใหม่ TCPConn.WriteTo
    เท่ากับว่าเกิด zero-copy I/O เพิ่มขึ้นอีกครั้งแบบโปร่งใสจากการอัปเกรดอินเทอร์เฟซ ผลที่ได้จากแพตเทิร์นนี้ในแพ็กเกจ io มีขนาดใหญ่มากจริงๆ

    • ใน standard library ของ Go มีการปรับจูนแบบนี้เยอะมาก ไม่ได้แย่เสมอไป แต่การจัดการเฉพาะทางแบบนี้ขยายต่อได้ยาก จึงทำให้ การรวมเข้ากับไลบรารีนอกมาตรฐาน น่าพอใจน้อยลง
      ถึงอย่างนั้นก็ยังถือว่าอยู่ในขอบเขตที่คาดหมายได้
  • แพตเทิร์นการทำ routing ที่ดีขึ้น เป็นการปรับปรุงที่ดีในช่วงที่อนาคตของ Gorilla Mux ดูไม่แน่นอน
    แต่การเปลี่ยนแปลงนี้ก็ทำให้ความเข้ากันได้ย้อนหลังพังเล็กน้อย โดยมีทั้งส่วนที่ชัดเจน เช่น พฤติกรรมของแพตเทิร์นที่มี { และ } เปลี่ยนไป และส่วนที่ชัดเจนน้อยกว่า เช่น การจัดการเส้นทางที่ถูก escape ไว้ โดยควบคุมได้ผ่านฟิลด์ httpmuxgo121 ของ GODEBUG และถ้าตั้ง httpmuxgo121=1 ก็จะย้อนกลับไปใช้พฤติกรรมเดิมได้
    แต่ก็ยังสงสัยว่าทำไมสิ่งนี้ถึงไม่ขัดกับ คำมั่นเรื่องความเข้ากันได้ของ Go 1 ที่บอกว่า “โปรแกรมที่เขียนตามข้อกำหนดของ Go 1 จะยังคงคอมไพล์และทำงานได้อย่างถูกต้องต่อไปตลอดอายุของข้อกำหนดนั้นโดยไม่ต้องเปลี่ยนแปลง”

    • วิธีเลี่ยงคำมั่นเรื่องความเข้ากันได้ของ Go 1 คือเอา การเปลี่ยนแปลงที่ไม่เข้ากันย้อนหลัง แบบนี้ไปผูกไว้กับค่าของ go directive ใน go.mod
      ถ้าเป็น 1.22 ขึ้นไปก็จะได้พฤติกรรมใหม่ของไลบรารี ถ้าต่ำกว่านั้นก็ได้พฤติกรรมเดิม จะได้ผลดีแค่ไหนคงต้องดูกันต่อไป
    • ปกติทีม Go มักจะไล่ดู GitHub และโปรแกรมโอเพนซอร์สก่อนจะทำอะไรที่อาจทำให้ของเดิมพัง เพื่อดูว่าคนใช้งานกันอย่างไร คิดว่าคราวนี้คงเห็นว่าการใช้ { และ } ในเส้นทางของ HTTP handler แทบไม่มี
      และยังมีวิธีปิดพฤติกรรมใหม่ได้ จึงไม่ได้บังคับให้ต้องแก้โค้ด เพียงแต่ต้องตั้งตัวแปรแวดล้อมตัวใหม่
      การเปลี่ยนความหมายของลูป for ในรีลีสนี้ก็เป็นตัวอย่างคล้ายกัน คือในทางปฏิบัติมันเป็นการเปลี่ยนที่ทำของเดิมพัง แต่โปรแกรม Go ทั้งหมดยังคอมไพล์และรันได้ต่อ เพียงแค่บางพฤติกรรมเปลี่ยนไปนิดหน่อย มองว่า Go เลือกแนวทางที่เน้นการใช้งานจริง และนั่นก็เป็นหนึ่งในเหตุผลที่มันประสบความสำเร็จ
    • มองว่านี่เป็นความพลาดที่พบได้ไม่บ่อยใน Go ถ้าจะใช้ go.mod เพื่อทำให้เวอร์ชันต่างกันพังต่างกัน อย่างน้อยก็ควรทำให้พังในแบบที่ดีกว่านี้
      เวลาปรับ go.mod เป็น 1.22 ถ้าให้ คอมไพล์ไม่ผ่าน ไปเลยยังดีกว่าต้องไปไล่หาปัญหา runtime ที่ละเอียดอ่อน
    • ไม่อย่างนั้นก็จะเสียสโลแกนสั้น ๆ ที่ทรงพลังไป ไม่ว่าจะเล็กหรือใหญ่ มันก็คือ การเปลี่ยนแปลงที่ไม่เข้ากัน
  • ใน Go 1.22 แต่ละรอบของลูปจะสร้างตัวแปรใหม่
    เรื่องนี้เคยคุยกันที่นี่มาก่อนแล้ว: https://news.ycombinator.com/item?id=33160236 - Go: Redefining For Loop Variable Semantics (2022)

  • อาจจะหัวโบราณไปหน่อย แต่ไม่ชอบการเพิ่ม range สำหรับฟังก์ชัน มองว่าความสะดวกที่ได้ยังไม่มากพอจะคุ้มกับความซับซ้อนที่เพิ่มเข้ามาในภาษา และสไตล์แบบ functional ก็ดูไม่เข้ากับสไตล์ของ Go ที่ชัดเจน เป็นเชิง imperative และค่อนข้าง verbose แต่มีฟีเจอร์ไม่มาก ซึ่งนั่นเคยเป็นจุดแข็งสำคัญอย่างหนึ่งของมัน
    ด้วยเหตุผลเดียวกัน จึงคิดว่า range สำหรับจำนวนเต็มก็เป็นทิศทางที่ผิดเช่นกัน ชุดฟีเจอร์ที่บางและภาระทางความคิดที่ต่ำของ Go เป็นทั้งจุดแข็งหลักและจุดที่ทำให้มันแตกต่างมาโดยตลอด

    • ตอนนี้ฟีเจอร์นี้ยังเป็นแบบเลือกใช้และเป็นเชิงทดลอง ต้องเปิดใช้งานอย่างชัดเจน ถ้าจะเปิดเป็นค่าเริ่มต้นเมื่อไร ก็อยากให้มีกลไก ปิดการใช้งาน เพื่อช่วยกันไม่ให้นักพัฒนาใช้ในกรณีที่มันไม่ได้ถูกใช้กันทั่วไป
      ปัญหาใหญ่ข้อหนึ่งของการพัฒนาซอฟต์แวร์คือการห้ามไม่ให้นักพัฒนาเพิ่มความซับซ้อนโดยไม่จำเป็นหรือโชว์ฝีมือเกินเหตุ โดยเฉพาะคนที่ยังอยู่ช่วงต้นอาชีพ มักมีแนวโน้มจะทำวิธีแก้ให้ซับซ้อนเกินไป แทนที่จะเขียนแบบตรงไปตรงมาซึ่งอาจดูไม่หรูและยาวกว่า
    • รู้สึกว่าฟีเจอร์นี้ยังมาเร็วเกินไป ตามอุดมคติแล้วควรเข้ามาหลังจากมี lambda และ generic parameter packs แล้ว
      ตอนนี้การรองรับ generics ของ Go ยังไม่ละเอียดพอจะรองรับสิ่งนี้ได้ดี จึงเกิดข้อจำกัดแปลก ๆ ที่ดูเหมือนถูกยัดเข้ามา
    • ผมก็รู้สึกก้ำกึ่งเหมือนกัน เมื่อเทียบกับการส่ง inline function เข้าไปแล้ว มันก็ไม่ได้ช่วยลด จำนวนบรรทัดของโค้ด เลย
  • การเพิ่ม sql.Null[T] ถือว่าดี น่าจะเริ่มใช้ในโปรเจ็กต์ใหม่
    ในงานปัจจุบันยังพึ่งพา null [0] ของ sqlboiler อยู่ ซึ่ง API คล้ายกันมาก ทำงานแบบ sql.Null แต่มีเมธอด IsSet() bool เพิ่มมาเพื่อบอกว่าค่านั้นเคยถูกตั้งไว้อย่างชัดเจนหรือไม่ ช่วยแยก “null โดยตั้งใจ” ออกจาก “null เพราะยังไม่ถูกกำหนดค่า” ฟังดูดี แต่ในทางปฏิบัติก็ไม่เคยได้ใช้เลย

    • สงสัยเหมือนกันว่ามีกรณีไหนที่ต้องรู้ว่า null นั้นตั้งใจให้เป็นแบบนั้น เพราะในฐานข้อมูลเองก็ไม่มี type ที่ตรงกับแนวคิดนี้ไม่ใช่หรือ
  • ตั้งใจว่าจะกลับไปดู mux ใน standard library อีกรอบ และหวังว่าอาจจะ ถอด chi ออก ได้ด้วย ชอบมากที่ฟีเจอร์แบบนี้เข้ามาอยู่ใน standard library

    • เข้ามาจะพูดแบบเดียวกันเลย chi ยอดเยี่ยมมากในบางโปรเจ็กต์ ถึงขั้นแทบลืมไปว่ามันมีอยู่ เพราะมันกลืนไปกับงานได้เป็นธรรมชาติมาก
      พอฟีเจอร์แบบนี้เข้า standard library ก็มั่นใจได้ว่าจะมีการดูแลรักษาตลอด และโปรแกรม Go จำนวนมากจะใช้แนวทางเดียวกัน
  • ถ้าใช้ Go ใน production สงสัยว่าปกติย้ายไปเวอร์ชันใหม่กันได้เร็วแค่ไหน หรือมักจะติดอยู่กับรีลีสเก่า
    เวลาดูโปรเจ็กต์ที่เปิดเผยต่อสาธารณะ รู้สึกว่าหลายแห่งค่อนข้างเลี่ยงการใช้ฟีเจอร์ใหม่ ๆ ถึงจะยังไม่ใช้ generics แต่อยากใช้ any ที่มาจาก Go 1.18 แทน interface{} แบบเดิม เพราะก็เคยได้ยินว่าตัวหลังดู “เป็นสำนวนของ Go” มากกว่า

    • ที่ Square ปกติจะรอ point release แรกก่อนค่อยยกเวอร์ชันพื้นฐานของ monorepo Go ขึ้น เจ้าของแอปแต่ละตัวจะอัปก่อนหน้านั้นก็ได้
      รอบนี้จังหวะมันแปลกนิดหน่อย เพราะทีม Go ออก 1.22 มาก่อนที่เราจะยกค่าเริ่มต้นเป็น 1.21 ตอนนี้การรอกลายเป็นแทบจะเป็นธรรมเนียมไปแล้ว และก็ไม่ค่อยเกิดกรณีที่รีลีสของ Go ทำของเดิมพัง
    • สำหรับฟีเจอร์ใหม่มักจะรอดูไปก่อนจนกว่าจะเห็นปัญหาที่เหมาะกับมัน แต่ เวอร์ชันของคอมไพเลอร์ จะอัปแทบจะทันทีหลังรีลีสออก
      อย่างเช่น generics เรารออยู่ราว 6 เดือนกว่าจะเริ่มใช้จริง เพื่อดูทั้งประโยชน์ ประสบการณ์ของนักพัฒนา และผลกระทบต่อความเร็วในการ build/test ว่าไม่ได้มากเกินไป
    • ที่ทำงาน คอนเทนเนอร์ที่รันอยู่จริง เช่น บริการ SaaS/API นักพัฒนาสามารถอัปไปใช้ build ล่าสุดได้ค่อนข้างอิสระ
      แต่เราก็ส่งมอบแอปสำหรับผู้ใช้ปลายทางที่เขียนด้วย Go ด้วย ซึ่งฝั่งนั้นต้องค้างอยู่ที่ 1.20 เพราะต้องรองรับ client OS รุ่นเก่า และใน 1.21 มีการตัดการรองรับออกไปพอสมควร จึงมีโอกาสสูงว่าจะอยู่ที่ 1.20 ต่อไปอีกหลายปี
    • CI ใช้ build และ test ด้วยคอนเทนเนอร์ latest ของ Go และชุดทดสอบก็ค่อนข้างแข็งแรง เลยแทบจะเป็นการอัปเดตอัตโนมัติอยู่แล้ว ถ้ามีอะไรไม่เข้ากัน pipeline ก็จะพัง แล้วค่อยตัดสินใจว่าจะตรึงไว้ที่เวอร์ชันล่าสุดที่ยังใช้ได้ หรือจะเลื่อนการอัปเดตออกไปจนกว่าจะแก้การเปลี่ยนแปลงที่ทำให้พังได้
      ประมาณไม่กี่ปีครั้งก็จะมีบั๊ก edge case ละเอียดอ่อนระหว่างเวอร์ชันใหม่กับแพ็กเกจ third-party โผล่มา แต่บั๊กแบบนั้นตามนิยามก็คือหลุดรอดชุดทดสอบอยู่แล้ว ดังนั้นถึงจะอัปเกรดแบบ manual ก็น่าจะจับไม่เจอเหมือนกัน
    • อัปทันทีเลย
      ถ้ากังวลเรื่องการรองรับคอมไพเลอร์ Go รุ่นเก่า ก็ใส่ build condition สำหรับเวอร์ชันเก่าไว้เพื่อกำหนดสิ่งที่ขาดได้ เช่น ใต้ // +build !go1.7 ก็สามารถใส่ type any = interface{} ได้