ตอนนี้เขียนโปรแกรมอยู่หลายภาษา ก็คิดว่าอยากลองเขียนดูบ้างว่าเราคิดว่าจุดแข็งของแต่ละภาษาคืออะไร แล้วเราเลือกใช้มันยังไง
กฎส่วนตัวเราเวลาบอกว่าเขียนภาษานี้ “เป็น” พอจะใส่ใน 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 ที่เราเลือกใช้ แต่หลายๆ ครั้งก็จะเลือกตามนั้นไม่ได้ถ้าทำหลายคน แล้วคนอื่นไม่ได้เขียนด้วย อันนั้นก็ต้องคุยกันอีกที