ใช้ OpenJDK ตัวไหนดี

ช่วงนี้ทำ build infra ให้ Java project แล้วก็รำคาญ base image เลยไปทำ image Java 16/17 ให้ทีม

ทีนี้ก็สงสัย ว่าเราใช้ OpenJDK ตัวไหนดี…

ถ้าตอบสั้นๆ คือถ้าไม่เลือก Oracle แล้วแทบจะเหมือนถามว่าอยากได้ Blockchain สีอะไร

ถ้าจะเอาคำตอบดีๆ เลื่อนไปล่างสุดอ่านได้เลยครับ

The contenders

ปัจจุบันเราสามารถโหลด OpenJDK 11 ได้จากที่ต่างๆ ดังนี้ (ข้อมูลจาก javaalmanac.io) ซึ่งต้องทำความเข้าใจว่าที่มาที่ไป ทำไมคนอยาก build JDK กัน

  • Eclipse Temurin โดยกลุ่ม Adoptium หรือชื่อเดิมก่อนมาอยู่ Eclipse คือ AdoptOpenJDK เป็นโครงการ build OpenJDK จากชุมชน มี IBM ดันหลังเล็กน้อย ถ้าอยากได้แบบไม่ติดแบรนด์คิดว่าเจ้านี้ดีที่สุด
  • AdoptOpenJDK Upstream build (ยังไม่มีย้ายไป Temurin) มาจากคำขอของทีม OpenJDK update project ที่อยากได้ build เพียวๆ ไม่แต่งเติมใดๆ ทั้งสิ้น แต่ Oracle ไม่ให้วางไว้ในเว็บ java.net
  • Amazon Corretto เนื่องจาก AWS ใช้ Java ด้านในค่อนข้างเยอะ ก็เลยแจกตัวที่ใช้งานมาเพื่อให้นำไปใช้งานได้ รวมถึงถ้ามี AWS support อยู่แล้วก็ใช้ได้เลย
  • Azul Zulu เป็นบริษัทจากอเมริกาที่ support Java มากว่า 20 ปี
  • Azul Prime เวอร์ชั่นเสียเงินที่มี optimization เพิ่มเติมเช่น garbage collector ตัวใหม่ไม่มีที่อื่น
  • BellSoft Liberica เป็นบริษัท support Java จากรัสเซีย
  • Microsoft Build of OpenJDK ทีแรกผมก็สงสัยว่า Microsoft ใช้ Java ทำอะไร ตัวเก่าก็ไม่เห็นจะดี แต่อันนี้เป็นซอร์สโค้ดเหมือนชาวบ้าน และ Microsoft เองก็ใช้ Java ใน Azure, LinkedIn และ Minecraft อยู่แล้ว ถ้ามันไม่ดีผมว่ากองทัพ Minecraft น่าจะพร้อมถล่ม
  • Oracle JDK รุ่นนี้ไม่ฟรีสำหรับการใช้งานใน production ต้องใช้รุ่น Java 17 เป็นต้นไปถึงจะฟรี
  • Red Hat ผู้ผลิตลินุกซ์อันดับต้นๆ แต่ก็มีรุ่น Windows พร้อมใช้พร้อมซื้อ commercial support ได้ด้วย แต่ยังไม่มีเวอร์ชั่นแมค
  • ojdkbuild เป็น Red Hat source code unofficial build ผมเข้าใจว่าน่าจะมีมาก่อนที่ Red Hat จะออกรุ่น Windows เอง
  • SapMachine เนื่องจาก SAP ก็ใช้ Java ใน product ก็เลยมี OpenJDK build ของตัวเองด้วย สามารถใช้ SAP support ได้ถ้าเป็นประเด็นที่เกี่ยวกับการใช้ใน SAP
  • Debian/Ubuntu จะมีใน apt-get ซึ่งโครงการ build เอง ไม่ได้โหลดจากเจ้าไหนข้างบนนี้มา

Source code

โครงการ OpenJDK เป็นโครงการ open source แปลว่า คุณจะได้แต่ source code กลับบ้านไป เวลา Java มี improvement อะไรมันก็จะไปโผล่ใน source code นี้ แต่มันจะไม่มี exe/app/deb/rpm ให้ดาวน์โหลด

ดังนั้นจะใช้ Java ยี่ห้อไหน มันก็ออกมาจาก source code เดียวกัน ก็ควรจะเหมือนกันเลือกอะไรก็ได้หรือเปล่า? ผมคิดว่า OpenJDK Release Map อธิบายไว้ค่อนข้างดีแต่มองภาพยากนิดหน่อย ก็สรุปได้ว่าแต่ละ vendor มี source code “on top” ของตัวเอง ซึ่ง vendor ส่วนมากที่เป็น open source จ๋าๆ จะพูดเหมือนกันหมดว่าเรา upstream first แปลว่าส่วนที่ทำเพิ่มแก้ไม่เยอะ ไม่ค่อยสำคัญ บางทีอาจจะเป็นโค้ดเดียวกันแต่ feature flag ต่างกันเฉยๆ ส่วน vendor ที่ไม่ใช่ open source ก็มีอยู่ 2 เจ้า ซึ่งชัดเจนมากว่ามี on top

  • Oracle บอกว่า Java version เก่า ไม่เล่นด้วยปล่อย community ทำกันเองซึ่งก็คือแทบทุก vendor ในนี้ยกเว้น Oracle แต่จะสังเกตว่า Oracle ก็ออก JDK เวอร์ชั่นเก่าเหมือนกันซึ่งโค้ดในนั้นไม่อยู่ใน open source อยากได้ต้องซื้อ และฝั่ง open source เองก็ไม่แน่ใจว่า Oracle ใช้โค้ดจากที่ community แก้กันด้วยหรือเปล่า
  • Azul Prime ซึ่งมีลูกเล่นเฉพาะตัว เช่น ReadyNow, LLVM-based JIT, Pauseless C4 GC

เจ้าอื่นๆ ที่พอจะทราบว่าแก้อะไรบ้างเช่น

  • Amazon Corretto 8 มี patch พอสมควร ส่วนใน 11 ก็มีแก้เล็กน้อยให้รองรับ Amazon Linux 2 และ 17 ยังไม่มีแก้
  • Red Hat ระบุความแตกต่างไว้พอสมควร หลักๆ คือเปิด Shenandoah GC (เป็น compile time feature flag ที่ vendor อื่นๆ จะเปิดหรือปิดก็ได้ เนื่องจากเป็นของ Red Hat), ใช้ library จาก Red Hat เองไม่ใช่ตัวที่ vendor มาใน source tree
  • Microsoft มีระบุ change ไว้เล็กน้อย หลักๆ คือเน้นรองรับ Windows/Mac on ARM

ถ้าต้องการการันตีว่า ไม่เอา on top ขอ OpenJDK “แท้ๆ” สามารถดาวน์โหลดได้จาก AdoptOpenJDK Upstream build (ขณะที่เขียนนี้ยังไม่ได้ย้ายไป Eclipse) หรือ jdk.java.net ซึ่งมีเฉพาะรุ่นล่าสุดและรุ่นที่ยังไม่ออกเท่านั้น

ปัญหาหนึ่งที่จะเจอกันทั้งหมดทุกเจ้าคือ security update ต่างๆ ซึ่งจำเป็นต้องให้ออกพร้อมๆ กัน โดยใน OpenJDK จะมีกลุ่ม Vulnerability Group ที่ vendor เจ้าต่างๆ สามารถเข้าถึง patch ลับสำหรับช่องโหว่ที่ยังไม่เปิดเผยสำหรับ test release ก่อนที่จะประกาศพร้อมกันตอนที่ patch เข้าไปใน OpenJDK หลัก (ส่วน security patch ที่ Oracle ทำเองจะ merge เข้าเลยไม่มีขั้นตอนนี้) ดังนั้นควรจะเลือก vendor ที่มีสิทธิ์ในกลุ่ม OpenJDK Vulnerability Group ด้วย

เท่าที่ผมลอง cross reference จาก census ว่าทีมนี้ปัจจุบันทำงานที่ไหนบ้าง ก็จะมีคนจาก Amazon, SAP, Azul, Red Hat, BellSoft, SUSE, IBM, Canonical (Ubuntu) อยู่ในกลุ่มนี้ จากในแผนภาพระบุว่า AdoptOpenJDK และ Debian ไม่มีข้อมูล security update ล่วงหน้าจากกลุ่มนี้ (ข้อมูลจาก 2019)

TCK

ประเด็นถัดมาคือ Java Technology Compatibility Kit ซึ่งการันตีว่า JVM ที่ใช้ผ่านมาตรฐานรับรองว่าเป็น Java ได้ โดย TCK นี้ต้อง license มาจาก Oracle ไม่มีแจกฟรี

เท่ามีข้อมูลตอนนี้ OpenJDK ทุกเจ้าที่มีใช้งานผ่านการตรวจสอบ TCK กันหมดแล้ว ที่จะไม่มีข้อมูลว่าผ่านหรือไม่ก็คือตัวที่อยู่ใน repo ของ Debian/Ubuntu และตัวที่ยังไม่ผ่านคือ ojdkbuild และ Microsoft

Packaging & Build Matrix

ส่วนที่ผมคิดว่าแต่ละเจ้าจะแตกต่างกันเยอะคือ packaging เพราะถ้าโหลดจาก jdk.java.net จะได้ zip file เอาไปใช้งานลำบาก และมีแค่ Windows/Mac/Linux 64 bit, Mac/Linux ARM 64 bit เท่านั้น ดังนั้นควรจะพิจารณาด้วยว่าเจ้านั้นๆ รองรับ OS/Architecture ที่ใช้งานหรือไม่ และรองรับ package management ที่ใช้งาน (Docker/Chocolatey/Homebrew/Yum/APT/SDKMAN)

ส่วนมากแล้วเท่าที่เห็นแทบทุกเจ้ามี combination พื้นฐานกันเกือบครบ คือ Windows/Mac/Linux 64 bit, Docker ทั้ง Red Hat/Debian และ Chocolatey/Homebrew/Yum/APT/SDKMAN ก็จะมีเฉพาะ combination ยากๆ คือ

  • Alpine Linux รองรับเฉพาะ Azul, BellSoft, Amazon
  • Mac ARM รองรับเฉพาะ Azul, BellSoft, Microsoft
  • 32 bit OS ตอนนี้มีเฉพาะ Temurin (Windows only), Azul, BellSoft

Support

ถ้าจะซื้อ support แล้ว ผมยังคิดว่าซื้อกับ Oracle น่าจะดีที่สุดและช่วยสนับสนุนการพัฒนา Java ด้วย แต่ถ้าไม่ชอบ Oracle แล้ว vendor แทบทุกเจ้ามี commercial support ทั้งหมด

  • Eclipse Temurin ซื้อได้จาก IBM หรือ Azul
  • SapMachine ใช้ SAP support ได้เฉพาะเรื่องที่เกี่ยวกับการใช้งานใน SAP
  • Microsoft support ใช้ได้เฉพาะการใช้งานใน Azure
  • Amazon ใช้ AWS support เค้าไม่ได้จำกัดว่าต้องใช้ใน AWS เท่านั้นแต่ก็ไม่แนะนำให้ซื้อ AWS support ถ้าไม่ได้ใช้งาน AWS
  • Azul กับ BellSoft เป็นบริษัท support Java โดยเฉพาะอยู่แล้ว

นอกจากเรื่องคุณภาพของ support ที่ได้รับแล้ว ผมยังคิดว่า contribution ที่บริษัทที่เราซื้อส่งกลับให้ OpenJDK ควรจะเป็นปัจจัยที่นำมาคิดด้วย รูปหนึ่งที่พอจะตอบได้คือจำนวน issue ที่ปิดโดยแต่ละองค์กรใน JDK 16 release

ก็จะเห็นว่า top 10 ไม่รวม Oracle ได้แก่

  1. Red Hat
  2. SAP
  3. Tencent
  4. นักพัฒนาอิสระ
  5. ARM
  6. Amazon
  7. Bellsoft
  8. NTT Data
  9. Microsoft
  10. Azul

ทั้งนี้ Azul ก็มีคอมเมนต์ว่าตรงนี้ก็เป็นแค่จำนวนแต่ไม่เน้นถึง impact หรืองาน maintenance เวอร์ชั่นเก่า

JRE

สมัยก่อน Java จะมี installer 2 ตัวคือ Java Development Kit (JDK) กับ Java Runtime Environment (JRE) ซึ่งตัวแรกจะมี compiler ด้วย แต่ตั้งแต่ Java 11 แล้ว Java แนะนำให้ใช้ jlink สร้าง custom runtime ที่มีเฉพาะส่วนที่แอพใช้งานเท่านั้นแล้วผูกไปพร้อมกับแอพเลย จึงไม่มี JRE ให้ดาวน์โหลดอีก

แต่ปัจจุบันก็ยังมีบาง vendor ที่ยังมี JRE อยู่ ผมก็ไม่แน่ใจว่าเค้าเอามาจากไหน ก็ได้แก่ Temurin, Azul, BellSoft, Red Hat, SAP ที่จะไม่มีก็คือ Oracle, Amazon, Microsoft

JDK นอกกระแส

จริงๆ แล้ว list JDK ยังไม่หมดแค่นั้นแต่มีตัวอื่นๆ อีก ถ้าอยากลองของแปลกก็ใช้งานได้

  • Jetbrains Runtime คือตัวที่ใช้รันโปรแกรมของ Jetbrains (เช่น IntelliJ) สามารถโหลดมาใช้เดี่ยวๆ ได้ มีจุดเด่นคือปรับแต่งให้ใช้งาน desktop application ได้ดี แต่ไม่ได้รับรอง TCK
  • Alibaba Dragonwell ที่ Alibaba ใช้รันเว็บภายใน มีฟีเจอร์เฉพาะตัวคือ JWarmup ที่ใช้บันทึกข้อมูล JIT แล้วโหลดภายหลังเพื่อให้ได้ประสิทธิ์ภาพทันทีหลังจาก cold boot, Wisp2 เป็น coroutine สำหรับ Java thread ฟีเจอร์เหล่านี้เพิ่งตั้งไข่ใน OpenJDK คงอีกหลายปีอาจจะได้ใช้งาน แต่สามารถใช้ได้วันนี้เลยใน Dragonwell
  • Tencent Kona เป็นตัวที่ Tencent ใช้ ซึ่งมีปรับจูน GC บ้าง รองรับเฉพาะลินุกซ์เท่านั้น
  • Huawei Bisheng JDK ทาง Huawei บอกว่าเค้าใช้ตัวนี้ รองรับเฉพาะ Linux ARM เท่านั้น และมีการปรับแต่งหลายๆ จุดซึ่งมีเอกสารเฉพาะภาษาจีน ผมคิดว่าน่าจะเน้นไปทาง ARM เนื่องจาก Huawei มี ARM CPU ของตัวเอง

คนหนึ่งที่หายไปไม่ว่าจาก list นี้หรือด้านบนคือ Google ซึ่งผมคิดว่าเค้าไม่ว่างทำ โดน Oracle ฟ้องอยู่ เท่าที่หาข้อมูลมา App Engine v2 ใช้ Ubuntu openjdk-11-jdk ส่วน Bazel rules_java ใช้ Azul

ใช้อะไรดี

ถ้าอ่านทั้งหมดนี้แล้วตัดสินใจไม่ได้สักที ในความเห็นของผมคิดว่า

Server

พิจารณาตามลำดับนี้ครับ (นึกว่าเป็น flow chart ก็ได้)

  1. ถ้ามี license Red Hat อยู่แล้ว (ที่ไม่ใช่ CentOS หรือ rebuild อื่นๆ) ก็ yum มาลงได้เลย
  2. ถ้ามี support จาก AWS, SAP, Azure อยู่แล้วและ workload รันบนนั้น ก็ใช้ของยี่ห้อนั้นได้
  3. ถ้าใช้ Alpine Linux มีตัวเลือกคือ AWS กับ Azul
  4. ถ้าจำเป็นต้องซื้อ support คิดว่าซื้อจาก Oracle ได้ก็ดี (ผมไม่เคยซื้อกับ Oracle แนะนำไม่ได้)
  5. ถ้าไม่ชอบ Oracle ใช้ Azul
  6. ถ้าไม่ซื้อ support อยู่แล้ว ผมคิดว่า Azul ยัง release เร็วกว่า Temurin (ขณะที่เขียนนี้ Azul ออก Java 17 หลายวันแล้ว ในขณะที่ Temurin ยังไม่เสร็จ)
  7. ผมยังไม่แน่ใจ JDK สายโม คือ Azul Prime (เสียเงิน) และ Alibaba Dragonwell ว่าควรเอามาใช้ดีไหม

ดังนั้นสำหรับงานบริษัทผมมี AWS support ก็ใช้ของ AWS ข้อจำกัดคือไม่มี Debian Docker ต้อง build เอง แต่ก็มี Dockerfile ตัวอย่างให้ (เค้าอยากเชียร์ให้ใช้ Amazon Linux)

Desktop

สำหรับบนคอมพิวเตอร์ส่วนตัว ผมคิดว่าสำหรับ developer บน server ใช้อะไรก็ใช้ให้ตรงกันน่าจะดีที่สุด สำหรับผมแล้วเป็น user มากกว่า ก็เลยเลือกใช้ดังนี้

  • Windows ใช้ Temurin จาก Chocolatey เพราะอยากได้ JRE อย่างเดียว (บางเจ้ามีให้โหลด แต่ Chocolatey ไม่มีแพคเกจ) และคิดว่ามันเป็นตัวที่แพร่หลายสุดน่าจะอัพเดตบ่อย
  • Linux ผมใช้ตัวที่อยู่ใน repository เลย คิดว่าถ้ามัน build มาโดยตรงน่าจะมีปัญหากับ OS น้อยที่สุด (ซึ่งบางทีก็อาจจะเป็นตัวเลือกที่ไม่ดี เช่น Debian เคย build จาก master ไปแล้วเจอบั๊ก)
  • Mac เดี๋ยวนี้ไม่ได้ใช้แล้วเลยไม่มีคำแนะนำ

Matrix

ลงเป็นตารางให้สุดท้าย ถ้าสงสัยแนะนำให้อ่านข้างบนก่อนครับ

หมายเหตุ:

  • ข้อมูลแพคเกจจาก Linux distro ต่างๆ ไม่ค่อยมี อาจจะลงไม่ถูกต้อง
  • ข้อมูลนี้ผมสำรวจก่อน OpenJDK 17 ออกเล็กน้อย
  • ข้อมูลนี้รวมเฉพาะ HotSpot runtime

Programming Languages I Write

ตอนนี้เขียนโปรแกรมอยู่หลายภาษา ก็คิดว่าอยากลองเขียนดูบ้างว่าเราคิดว่าจุดแข็งของแต่ละภาษาคืออะไร แล้วเราเลือกใช้มันยังไง

กฎส่วนตัวเราเวลาบอกว่าเขียนภาษานี้ “เป็น” พอจะใส่ใน Resume คือถ้า Ecosystem ในเรื่องนั้นมันพร้อมเราสามารถทำ Task อะไรก็ได้ในภาษานั้นๆ และเขียนออกมา Idiomatic เฉพาะตัวแบบที่ภาษานั้นๆ เขียนอยู่ด้วย

หลักๆ ที่เขียนเลยก็จะมี

  • Python
  • Go
  • JavaScript & TypeScript

ภาษาอื่นๆ ที่เคยเขียนก็จะมี Java, C, C++, Rust, Kotlin, PHP แต่ในบทความนี้คงจะไม่พูดให้ครบทุกอัน

Python

Rule of thumb ตอนนี้คือถ้ามันจะต้องต่อ database เราจะเลือกใช้ Python และ Django เท่านั้น ไม่มีอะไรที่ทำให้เราทำงานเสร็จเร็วเท่า Django อีกแล้ว

นอกจาก Django แล้ว Python ฝั่ง Data Science ก็ค่อนข้างแข็ง แต่เราไม่ค่อยได้ใช้เท่าไร แล้วก็ยังทำ scripting ต่างๆ ได้เร็ว

ข้อเสียของ Python คือมันเป็นภาษาที่ปวกเปียกมาก

Package manager มันอยู่ระหว่าง requirements.txt ที่ใช้ได้แต่ค่อนข้าง manual และไม่มี lock กับ Poetry ที่ยังไม่ดัง (และคนไปเลือกใช้ Pipenv ที่เลิกทำแล้วมากกว่า) และ Virtualenv มันค่อนข้าง Hack หน่อยๆ เมื่อเทียบกับภาษาใหม่ๆ ที่เลิกมี global package repository แล้ว

Import syntax ที่ถ้ามีไฟล์อยู่ folder ข้างๆ จะนั่งงงมากว่าต้อง import ยังไง (มันไม่มี “absolute” import มีแต่ relative โดยใช้ syntax แปลกๆ)

Type checking ที่ยังตั้งไข่มากๆ ถึงจะทำมาหลายปีแล้ว และ syntax ที่มันแปลกๆ หน่อย

Speed ของมันที่ค่อนข้างช้าในการ execute ซึ่งปกติก็จะไม่ค่อยใส่ใจ แต่ก็มีครั้งนึงที่ทำให้พลาดข้อใน Codejam มาแล้วเพราะโค้ดเหมือนกันรันใน Python เกิน 5 นาที ไม่ผ่าน ไปเขียนใหม่ใช้วิธีเดิมใน C รันจบใน 30 วินาที

Web server deployment มันที่ยังต้อง deploy WSGI container เหมือนภาษาโบราณๆ ไม่ใช่ one click และก็จะติดเรื่อง thread pool ที่ต้องมาจูน

JavaScript

ผมไม่ชอบ JavaScript เท่าไรนัก แต่จำเป็นต้องเขียนเพราะมันใช้ได้หลายที่มากๆ มันเป็นภาษาเดียวที่รันได้ใน Browser เวลาจะทำอะไรที่เขียนครั้งเดียวใช้ได้ทุกที่ต้องใช้ JavaScript เลย

ข้อดีของ JavaScript คือ syntax มันค่อนข้างมาตรฐานไม่หวือหวา แล้วพอบวก TypeScript มามันทำให้การ code project ใหญ่ๆ ทำงานงายขึ้นเพราะ editor จะฉลาดขึ้นเยอะ

ข้อเสียของ JavaScript คือภาษามันค่อนข้างฟรีสไตล์และมันไม่เก่ง Class กลายเป็นว่าคนชอบเขียนเป็น functional แต่มันก็ไม่ใช่ภาษาที่ออกแบบเป็น functional มาตั้งแต่แรก โค้ดมันก็เลยเละมาก

แล้วที่ผมไม่ชอบ functional ใน JavaScript มากคือการที่เราเอา higher order function ที่ add functionality ทีละนิด แต่ใช้ซ้อนกันเยอะๆ เวลาไล่โค้ดมันจะงง flow มากว่ามันเข้าแล้วอะไรโผล่เข้ามาตอนไหน รวมถึง syntax มันที่จะเป็น a(b(c(d))) ซึ่งไม่ค่อยเป็นธรรมชาติในการอ่านเพราะ functionality หลักมักจะอยู่ที่ d

นึกภาพว่าถ้า Unix pipe มันเขียนว่า wc(tar('xf', gzcat(file)))ก็คงใช้ลำบากกว่า gzcat file | tar xf - | wc

แล้วการเขียน helper มาทำให้มันกลายเป็นซ้ายไปขวา ก็ทำได้ยุ่งยากใน Javascript เพราะถ้าเราเขียน chain(a, instance.methodB, c, d) this ใน methodB ก็จะผิดอีก ต้องมา bind ก่อน ซึ่งไม่มีภาษาอื่นทำกัน (แล้วผมก็ยังไม่เคยเห็นว่ามี use case ไหนเลยที่จะใช้มันโดยไม่ bind)

สรุปคือมันทำได้ทุกอย่างแต่ไม่เก่งสักอย่าง แต่ก็ยังต้องใช้มันอยู่เพราะใน Browser มันผูกขาด

Node.js

ผมจำไม่ได้ว่าสมัยที่ Node.js เพิ่งมาผมคิดกับมันไว้ยังไง (สมัยนั้นเขียน PHP กับ Web.py) น่าจะไม่ชอบที่เอา JavaScript ไปรันบน server เพราะมันช้า

แต่ในปัจจุบันปรากฏว่ามันเร็วมาก เร็วกว่า Python เสียอีกเพราะ JavaScript ใน browser มีเงินลงทุนมหาศาลจากบริษัทเว็บต่างๆ

ข้อดีของ Node คือ async io ซึ่งมาแรกๆ หลายคนไม่เชื่อว่าไม่ต้องมี application server คั่นหน้าแล้ว เอา Node รับ load ได้ตรงๆ เลย

แต่ข้อเสียของ async io ใน Node คือ syntax มันไม่ค่อยสวยเท่าไรนัก ไม่ว่าจะเป็น Callback หรือ Promise + Await ก็ตามมันก็ไม่ดูดีเท่า Sequential (a().b().c() vs. await (await (await a()).b()).c())

นอกจากนี้เนื่องจาก standard library ที่มีให้ค่อนข้างจำกัด ทำให้เกิด npm ecosystem ที่บูมมากๆ อะไรก็อยู่บน npm ได้หมดแม้แต่ application อย่าง Chromium ก็โหลดผ่าน npm ได้ ข้อเสียก็คือแต่ละคนก็เลือกใช้ lib คนละตัวแตกต่างกันไป รวมถึงพอแยกกัน maintenance คุณภาพก็แตกต่างกันออกไปตั้งแต่ระดับดีมากๆ อย่าง React ไปจนถึง left-pad ที่คนทำอยู่ดีๆ ก็เอาลง

พอ community มันเปิดกว้าง มันก็กลายเป็นว่า project structure เปิดมาแต่ละอันไม่ซ้ำกันเลย เวลาอ่านก็ต้องไปนั่งไล่ก่อนว่าทางเข้าทางออกอยู่ตรงไหนบ้าง แล้วแต่ละคนก็จะทำท่าไม่เหมือนกันเลยเพราะคนไม่ค่อยใช้ Framework (อาจจะมีใช้ Next/Nuxt.js บ้างแต่ก็ไม่ใช่ทุกคนจะชอบ)

ข้อจำกัดอีกเรื่องของ Node คือมันทำอะไรที่ CPU bound ไม่ได้เลยเพราะมันเป็น single thread แถมยังไม่มีวิธีแตก thread เลยแม้แต่วิธีเดียวเนื่องจากมันมี Global lock เหมือน Python (แต่ Python ยังมี multiprocess ให้ใน standard library) แต่คิดว่าเร็วๆ นี้น่าจะมา

ตอนนี้จุดเดียวที่เรายังเลือก Node.js อยู่คือ Socket.io เนื่องจากภาษาอื่นๆ ในยุคก่อนมันทำ Streaming ไม่ได้ (thread pool จะเต็มเอา) พอมาถึงยุคนี้ที่ใครๆ ก็ทำ async แล้วก็เลยโดดข้ามไป WebSocket เลย จะมีแต่ Socket.io เท่านั้นที่ทำได้ทั้ง Long polling และ WebSocket ได้ในเวลาเดียวกัน และใช้โค้ดเดียวกัน

Go

ช่วงที่เขียน Rust ผมบอกว่าจะไม่เขียน Go เดี๋ยวใจแตก แล้วก็เป็นอย่างนั้นจริงๆ

Go แก้ทุกปัญหาของ Node ที่ผมบ่นไว้ด้านบนได้เกือบหมด

ภาษามันเป็น Static typed ในขณะเดียวกัน map เป็น first class data structure ของภาษา ไม่เหมือนภาษายุคก่อนหน้า และมันยังไม่กลัวที่จะใช้ interface{} type

Go มี Standard library + golang.org/x ที่ค่อนข้างดีทำให้ third party ไม่ต้องนั่งตบตีกันว่า HTTP client ต้องใช้ตัวไหน (Go ก็มีให้เลือก แต่ส่วนมากคนก็ยังเชียร์ http.Client ใน standard library อยู่)

Syntax ของภาษาที่เรียบง่ายมากๆ ทีม Go เคยบอกว่าเขียน Go คือเขียนโค้ด ไม่ใช่ไปนั่งเขียน Type

Go มี gofmt และ compiler เองก็เป็น linter ในตัว (no unused variable, enforce ชื่อ function ตัวใหญ่เป็น public เป็นต้น) ทำให้โค้ด Go ไม่ว่า project ไหนส่วนมากก็แทบจะเป็นแบบเดียว

ตั้งแต่เขียนโค้ดมาคิดว่าเรา contribute Go pull request มากที่สุดแล้วถึงจะมาเขียนหลังสุด เพราะมันไม่ต้องกลัวว่า maintainer จะด่าอะไรเราในโค้ดมั้ย

(แอบชอบสไตล์ modulename.FunctionName() ของ Go ด้วย อ่านแล้วมันเคลียร์เลยทันทีว่า function มาจากไหน และตัวใหญ่ทำให้สะดุดตาโดดข้ามไปอ่านง่าย ต่างกับใน JS ที่คนชอบ destructure เอาแต่ functionName มา มันเพิ่ม cognitive load ต้องจำว่าตัวแปรนี้มาจากไหน)

วิธีแก้ปัญหา async แล้ว syntax ไม่สวยของ Go คือเลิกทำ async ทุกอย่างกลายเป็น sync อะไรที่อยากจะรันข้างหลังค่อยสั่งเปิด goroutine ซึ่งเพิ่มแค่คำว่า go ข้างหน้า (เทียบกับ Python thread นี่เขียนกันหลายบรรทัดเลย)

ที่คนเขียน Go อาจจะไม่รู้คือที่เห็น Go เป็น sync จริงๆ ด้านล่างมันก็เป็น async คล้ายๆ กับที่ Node ทำ แล้วมันใช้ scheduler เป็น abstraction ทำให้มันกลายเป็น sync (เหยด!)

แล้ว Go ไม่มี Global lock เหมือน Python หรือ JavaScript มันทำให้ performance ยิ่งดีขึ้นไปอีก แต่ก็อาจจะต้องระมัดระวัง race condition เองมากขึ้น สำหรับเราเราคิดว่ามันตื่นเต้นและเป็นประสบการณ์ที่อยากเรียนรู้ เพราะภาษาอื่นที่เขียนมี global lock หมดเลย

เวลา Deploy Go ก็ทำได้ง่ายกว่าภาษาอื่นๆ มาก เพราะมัน compile มาได้ binary ตัวเดียวใหญ่ๆ (20-200MB) แล้วคลิกเดียวรันได้เลย

ทุกวันนี้เราเลยพบว่ามาเขียน Go บ่อยขึ้นมาก แทบทุกอย่างที่ไม่ต้องแตะ Database ก็จะไปเขียน Go แม้แต่ scripting บางทียังเลือกใช้ Go เพราะมัน Deploy ได้ง่ายกว่าเยอะ

ข้อเสียของ Go คือ module ซึ่งมันมาช้าไปหน่อย และก็ยังออกแบบมาไม่ค่อยดีอยู่ดี ปัญหาที่เห็นคือระบบ versioning ที่ไม่เคลียร์เท่า Node.js และการใช้ URL import ทำให้เวลา fork ต้องแก้ทั้ง project (แล้วเกิดอยากจะ merge กลับจะทำได้ยาก)

Go ไม่มี generic ทำให้บางอย่างทำได้ยาก โดยเฉพาะการ search in array ที่เป็น operator ใน Python แต่ใน Go ต้องนั่งเขียน loop ทุกครั้ง

Go ไม่มี implements ที่เอาไว้เช็คว่า function/struct นี้ satisfy interface แล้วหรือยัง ถ้าไม่ตรง มันจะไป error ตรง call site แทนที่จะ error ที่ struct (ซึ่งลำดับการเขียนโค้ด เรามักจะเขียน struct ให้เสร็จก่อนแล้วค่อยไป create + pass ไปยังคนที่ใช้)

แล้วด้วยความที่มันเป็น static typed ก็คิดว่ายังไม่อยากเอามันไปทำงานที่ใช้ JSON มากๆ เท่าไร

Java

ยังไม่แน่ใจว่าไม่ชอบ Java หรือไม่ชอบ Spring แต่ที่แน่ๆ คือไม่ชอบที่ Java เพราะพยายาม violate YAGNI ทุกวันนี้ยังตั้งคำถามอยู่ว่าถ้าให้เรา rebuild ecosystem Java ให้ใช้ standard เหมือน Python (แต่ไม่แก้ compiler) จะอยากเขียน Java มั้ย

ปัญหาของ Java คือ type system มัน strict เกินไป เวลาจะเพิ่มนิดๆ หน่อยๆ ทำไม่ได้ เช่น ถ้ามี request object แล้วอยากจะฝากข้อมูลไปบน request object ก็ต้องทำ object อีกตัวมาครอบแล้ว delegate เข้าไปใน request (เทียบกับ JavaScript/Python ที่ใช้ req.data ได้เลย หรือ Go ก็มี embed struct ไปบน struct อีกตัว)

คนเขียน Java ก็เลยจะนิยมว่าทำ interface ไว้ให้ทุกอย่าง “เผื่อแก้” data ต่างๆ บน class อื่น ต้องวิ่งผ่าน getter เท่านั้น (ใน Go เรา mutate field บน http.Request กันสนุกเลย)

สองคือภาษามันเก่าแล้วมันไม่มี first class map เหมือนภาษาใหม่ๆ ทำให้ท่าที่จะเขียนโค้ดง่ายขึ้นกลับกลายเป็นโค้ดยุบยับที่อ่านลำบาก

สามคือภาษามันไม่มี first class function & class เวลาส่ง function และ class เข้าไปมันจะฟีลเหมือนเขียน reflection หน่อยๆ ซึ่งทำให้หลายๆ ท่าลำบากในการเขียน

หลายๆ ฟีเจอร์พวกนี้ถูกแก้ด้วย code generation บนภาษาใหม่ๆ เช่น Kotlin, Groovy หรือ Scala ที่ข้างหลังก็ไป generate class ออกมาอยู่ดี

PHP

ตอนเรียนจบ PHP เคยอยู่ใน resume ทุกวันนี้หลายๆ ครั้งก็ยังวิเคราะห์ PHP design อยู่ว่ามันพลาดไปตรงไหน

เราคิดว่า Design ของ PHP ออกแบบมาน่าสนใจ มันเป็นภาษากระแสหลักภาษาเดียวที่ออกแบบมาให้เขียนเว็บเพียงอย่างเดียว

ไม่มีภาษาอื่นไหนที่เขียน hello world เป็นหน้าเว็บได้ใน 1 บรรทัด อย่างน้อยๆ ก็ต้องเขียนส่วนที่รับ request object เข้ามาก่อน แต่ใน PHP มันอยู่ใน global มาตั้งแต่แรก

แล้วถ้าอยากจะ set header สักตัว ก็แค่เพิ่ม header("Content-Type: application/json"); จากหนึ่งบรรทัดกลายเป็นสองบรรทัด

ตัวภาษาเองยัง compact พอที่จะเป็น template engine ในตัวด้วย อย่างที่ WordPress ทำอยู่

แต่ community PHP อยู่ดีๆ ก็อยากเป็น Java แล้วทิ้งพวกนี้ไปโดยไปทำ Symfony และ Laravel มา

จากภาษาที่ echo ก็ออกหน้าจอได้แล้ว กลายเป็นว่าต้อง return Response object กลับไปเหมือนภาษาอื่นๆ แถมยังพยายามทำเป็น OOP จัดๆ ทั้งๆ ที่ตอน PHP4 ไม่มี class เลย พวก function ต่างๆ เช่น echo หรือ header กลายเป็นสิ่งที่ไม่ควรใช้

ก็ไม่รู้ว่า PHP พลาดไปตรงไหน เพราะ Zend หรือเปล่านะที่พยายามทำให้ PHP กลายเป็นการค้าแล้วก็ทำให้มันขายได้ หรือเพราะช่วงเปลี่ยนผ่านที่คนยังไม่ซื้อ VPS กันแล้วจำเป็นต้องใช้ PHP (แล้วที่ JavaScript ทุกวันนี้มันเป็นแบบนี้ ก็เพราะเหตุผลนี้หรือเปล่านะ?)

ทุกวันนี้ก็ยังฝันอยู่ว่าถ้าสร้าง PHP ขึ้นมาใหม่ในยุคนี้จะออกแบบมันยังไงดี แล้วมีใครทำแล้วหรือยังนะ?

ที่เอา PHP ออกจาก resume ตอนนี้เพราะตั้งแต่มันมี Symfony และ Laravel มา ก็ไม่ได้ตามแล้ว เคยเขียนอยู่ 2 project ใน stack นั้นได้ ก็ยังคิดว่า Django ดีกว่า

สองคือ คนเขียน PHP แย่ๆ มีเยอะในประเทศนี้เลยไม่อยากเสี่ยงไปทำงานกับคนพวกนี้ (เหมือนที่ 37signals ใช้ Ruby เพราะใครเขียนเป็นในยุคนั้นคือเซียน จะได้คัดกรองคนง่าย) สมัยที่เคยทำ freelance มา ทุก project ที่มี PHP code เรา penetration test แล้วเจอช่องโหว่ทุกอัน พอแจ้งไปก็มักจะมีข้ออ้างที่ไม่สนใจจะแก้ (เคยเจอขนาดว่าแก้ให้แล้วมา overwrite ทับ)

สามคือคนที่ยังใช้ PHP อยู่ ส่วนนึงคือติดอยู่ใน environment ที่ต้องใช้ PHP (และมักจะเป็นรุ่นเก่า) เราคิดว่าพอหลุดพ้นจาก environment นั้นไปได้แล้ว มันเปิดกว้างให้ใช้ภาษาอะไรก็ได้

Rust

เคยจับ Rust มาผ่านๆ ด้วยเหตุผลว่าเราไม่มีภาษาไหนเขียน low level เลย (ตอนนั้นยังไม่ได้เขียน Go)

ปัจจุบันก็คงคิดว่าไม่กลับไปเขียนแล้ว เพราะ ecosystem มันพร้อมเลยสำหรับงาน high level และ borrow checker ที่ทำให้เรา unproductive (ถ้ายอมมี garbage collection แล้วเขียนโปรแกรมได้เร็วขึ้น 10 เท่า สำหรับงานที่ผมทำมันไม่เลือกก็พลาดแล้ว)

It Depends

สรุปว่าเราเลือกใช้ตามนี้

  • ถ้าทำเว็บ ต่อ Database ใช้ Python และ Django
  • ถ้าทำอะไรที่ไม่ต้องต่อ Database (เช่น stateless API) อาจจะใช้ Go
  • บน browser ยังคงต้องใช้ JavaScript อยู่
  • Scripting ต่างๆ ถ้าเอาเร็วใช้ Python
  • แต่ถ้าจะเอาความ Deploy ง่าย ใช้ Go

ตอนนี้ยังไม่มีแผนจะเรียนภาษาเพิ่ม เพราะดูจะยังไม่มี use case ไหนขาดตกจนใช้ภาษาที่เขียนแก้ไม่ได้เลยเลยสักภาษา แล้วพอเรียนภาษาใหม่แต่ไม่ได้ใช้มันก็จะลืม (เคยจะลองเขียน Ruby แต่พอพบว่ามันทับซ้อนกับ Python ก็ไม่ได้ใช้ ลืมหมดแล้ว)

สุดท้ายแล้วถึงแต่ละภาษาจะมีจุดแข็ง – จุดอ่อนต่างๆ และมี clear use case ที่เราเลือกใช้ แต่หลายๆ ครั้งก็จะเลือกตามนั้นไม่ได้ถ้าทำหลายคน แล้วคนอื่นไม่ได้เขียนด้วย อันนั้นก็ต้องคุยกันอีกที