Clojure กับการ model โปรแกรมด้วย data structures

ตอบคำถามในกรุ๊ป Clojure in Thai

การ model โปรแกรมด้วย data structures เนี้ยก็คือ model โดยใช้ map ซ้อน map ซ้อน map นี่หละครับ ตอนที่เห็นครั้งแรกผมคิดว่านี่เราถอยหลังลงคลองกันหรือเปล่า นี่มัน Primitive Obsession ซึ่งเป็น code smell แบบหนึ่งหนิ ผมถูกสอนมาในยุค OO ที่เค้าให้ผมสร้าง class ขึ้นมาเพื่อให้เกิด encapsulation และ data hiding เพื่อที่จะได้ระบบที่ high cohesion, low coupling

  • แต่พอได้ทำลองทำจริงๆ กับ Clojure ก็พบว่า เออมันก็ยืดหยุ่นกว่านะ
    มันทำให้ reuse โค้ดได้ง่ายขึ้น เพราะฟังก์ชันไม่ผูกกับ class เราจะเอาฟังก์ชันนี้ไปใช้กับ data structure ไหนก็ได้ ส่งผลให้โค้ดเบสโดยรวมมีขนาดเล็กลงด้วย
  • สร้าง data ขึ้นมาสำหรับเทสก็ง่าย
  • debug ก็ง่าย print ออกมาดูได้เลย ไม่ต้องไป override toString ของ class นั้นๆ
  • จะเทียบ equality ก็ทำได้ทันที ไม่ต้องไป override hashCode
  • แปลงเป็น json ก็ง่าย serialize, deserialize ได้ไม่ยาก

หลายๆ คนที่เค้าเขียน JavaScript ในสไตล์ functional programming เค้าก็อาจจะใช้วิธีนี้กันอยู่บ้างแล้ว แต่พอเป็น Clojure ก็จะได้ข้อดีครงที่ immutable data ก็จะเพิ่มปลอดภัยให้อีกชั้นนึง และมี standard library พร้อม ทำให้ทุกๆ คนใช้ไลบราลีเดียวกัน

Advertisements

ตัวอย่างผลจากแนวคิด isolation ของ Clojure

คัดลอกมาเก็บไว้จากคอมเมนต์ตอบพี่ป้อ อันนี้ (มีเพิ่มเติมอีกนิดหน่อย)

เริ่ม —-

isolation มันฝังอยู่ในทุกๆส่วนของ Clojure ทั้งตัวภาษาและ ecosystem เช่น
– การแยกภาษาจาก runtime ทำให้สร้าง clojurescript ได้ง่าย
– การแยก ผู้ส่งกับผู้รับออกจากกันด้วย channel ของ core.async (ไม่เลือกทางที่ต้องรู้ผู้รับแบบ actor)
– การแยก check/validation ออกมาเรียกใช้เมื่อไหร่ก็ได้ ที่ไหนก็ได้ของ clojure.spec ถ้าไม่อยากใช้ก็ไม่บังคับ
– การแยกวิธีการประมวลผลของ transducer ทำให้เอามันไปใช้ได้กับ sequence หลายประเภท ทั้ง list, vector ธรรมดา, channel และอื่นๆ
– ทุกอย่างมี namespace (แม้กระทั่ง keyword) ทำให้ชื่อเดียวกันของต่าง component กัน ไม่จำเป็นต้องเป็นสิ่งเดียวกันเสมอไป
– เน้นการสื่อสารด้วย data ซึ่งแปลว่าแต่ละ component สามารถตีความหมายของ data ก้อนหนึ่งๆ ได้ตามความต้องการ
– การที่ Datomic แยก database ออกจาก storage ทำให้มันทำงานได้ทั้งบน memory, sql, DynamoDB, Cassandra, … แยก transactor ออกจาก query engine ทำให้ scale query engine ได้ทันที (ต่อตรงไปที่ storage)
– พวกไลบราลีต่างๆ ก็ค่อนข้างทำงานเฉพาะ เช่น ไลบราลีสำหรับ routing ที่ทำหน้าที่ parse url เป็น data อย่างเพียงอย่างเดียว
– Type มี polymorphism แต่ไม่มี inheritance เพราะ inheritance มันทำให้เกิดการผูกกันที่แน่นเกินไป

Isolation เป็นสิ่งที่ทั้ง community เห็นความสำคัญตรงกัน ในทาง implementation บางคนอาจทำได้มากได้น้อยก็แล้วแต่ความสามารถและประสบการณ์ของแต่ละคน แต่มันไปในทิศทางเดียวกัน บางอย่างภาษาอื่นอาจจะมีสิ่งที่คล้ายๆ กัน แต่ก็น่าจะไม่ได้รับความสำคัญเทียบเท่า

ผมว่าเรื่องนี้เป็น value ที่สำคัญมาก และอธิบายยากมากของ Clojure เวลาใครมาถามว่า Clojure ดีอย่างไร ผมจะรู้สึกว่ามันอธิบายยาก เพราะ syntax มันเป็นแค่ส่วนเดียว แต่จริงๆ แล้วความเชื่อใน simplicity และ isolation ของ community และทุกคนมุ่งไปทางนั้นมันสำคัญกว่ามาก

—จบ

Designing is fundamentally about taking things apart. It’s about taking things apart in such a way that they can be put back together (If that makes sense).
So separating things into things that can be composed that’s what design is.
— Rich Hickey

การแยก Identity, Value และ State ออกจากกันของ Clojure มีประโยชน์อย่างไร

จากคำถามของน้องบอสวันนี้ (โพสเต็มๆ อยู่ที่นี่ครับ)

เมื่อกี้พยายามอธิบายเรื่อง atom กับการ decouple identity กับ value ออกจากกัน ว่ามันมีประโยชน์ยังไง แต่รู้สึกว่ายังไม่เข้าใจประโยชน์ของมันจริงๆ ทุกคนมองยังไงกันครับ?

ด้านล่างนี้เป็นความพยายามในการอธิบายคำถามนี้ของผมครับ

ผมคิดว่าประโยชน์ของการแยก identity กับ value คือทำให้คนที่ observe value ของ identity แต่ละช่วงเวลายังเห็นภาพที่ถูกต้องของช่วงเวลาที่ตัวเองสนใจอยู่เสมอ

มาคุยเรื่องคำศัพท์กันก่อน เผื่อคนที่เพิ่งเคยได้ยิน

สิ่งๆหนึ่ง มีการเปลี่ยนแปลงคุณลักษณะ ไปตามเวลา
สิ่งหนึ่ง คือ identity
คุณลักษณะ คือ value
คุณลักษณะ ณ ช่วงเวลาหนึ่ง คือ state
เช่น ปีที่แล้วนาย ก. สูง 164 หนัก 61 ตอนนี้นาย ก. สูง 165 หนัก 60

  • identity คือ นาย ก.
  • value ขอมองว่ามี 2 value ก่อนคือ สูง 164 หนัก 61 และสูง 165 หนัก 60
  • state ถ้าเรามองที่เวลาปัจจุบัน คือ สูง 165 หนัก 60

Atom คือ state นั่นเอง มันใช้ว่าบอกว่า ณ เวลาปัจจุบัน identity มีค่าเป็นเท่าไหร่

ถามว่าประโยชน์คืออะไร ก็ต้องมาคุยว่าปกติที่เราไม่แยกสิ่งเหล่านี้เราทำกันอย่างไร

  1. เราก็จะมีที่ๆ นึงเก็บค่าปัจจุบันของนาย ก. ซึ่งจะโดนเขียนทับไปเรื่อยๆ ไม่มีการเก็บของเก่า อยากเก็บก็ต้องจัดการเอง มันอาจจะไม่ยากนักถ้ามีแค่ 2 ค่าแบบส่วนสูงและน้ำหนักในตัวอย่าง แต่ถ้าสิ่งที่เราสนใจนั้นมีชั้นความซับซ้อนสูงหละ เช่น เรามองภาพของประชากรทั้งโลกที่ประกอบด้วย ประชากรแต่ละประเทศ ประชาชนแต่ละเมือง ซ้อนกันไปเรื่อยๆ เราจะทำการเก็บของเก่าอย่างไร
  2. ถ้ามีการประมวลผลใดๆ กำลังทำงานอยู่ ณ เวลาที่ค่าของนาย ก. กำลังจะเปลี่ยน การประมวลผลนั้นอาจจะทำงานผิดพลาดได้
    ตัวอย่างความผิดพลาด เช่น เราบังเอิญไปคำนวณ BMI ของนาย ก. ตอนจังหวะมันจะเปลี่ยนค่าพอดี
    เช่น ตอนที่นาย ก. สูง 164 หนัก 61 คำนวณ BMI ได้ 22.68 แต่ขณะที่กำลังจะคืนค่ากลับไป นาย ก. ดันเปลี่ยนไปสูง 165 หนัก 60 พอดีซึ่งแปลว่าการประมวลผลครั้งน้ันผิดพลาดวิธีการป้องกันไม่ให้การประมวลผลใดผิดพลาดที่เค้าทำกันมามีความซับซ้อน เช่น locking ด้วยเทคนิคต่างๆ ซึ่งจากความซับซ้อนของมันอาจจะก่อให้เกิดความผิดพลาดในด้านอื่นได้อีก และมันยังทำให้ยากต่อการทำความเข้าใจระบบของเราด้วย เช่น ลองคิดดูว่าถ้าเราคำนวณ BMI ผิด ความคิดแรกสุดเลยเราคงไม่คิดหรอกว่าเกิดจากการโดนแก้ค่าขณะกำลังคำนวณพอดี เราคงคิดว่าวิธีการคำนวณ BMI ของเรามันผิด เราก็อาจจะต้องไปพยายามไล่สาเหตุอยู่นานกว่าจะเจอปัญหาที่แท้จริง ลองคิดภาพเรื่องนี้ในการประมวณผลของระบบใหญ่ๆ ที่มีความซับซ้อนสูงดู

อ่านเพิ่มเติม Working Models and Identity

Code as Data มีประโยชน์อย่างไร

มีคนเล่ามาทาง FB Message ว่า กำลังศึกษา Clojure อยู่ แต่ไม่ค่อยเข้าใจประโยชน์ของแนวคิด code as data

ผมเองก็ยังรู้สึกไม่ค่อยเข้าใจเหมือนกัน แต่ขอแชร์สิ่งที่ผมพอมองเห็น พอเข้าใจนะครับ

1. มันทำให้มี syntax เฉพาะของภาษาน้อย เพราะจำกัดอยู่แค่ data structure ของภาษา โดยทุกๆ อย่างมันอยู่ใน รูปนี้หมด


(form arg1 arg2 arg3 ... argN)

เช่น def, defn, if, let ก็อยู่ในรูปนี้ทั้งหมด มัน consistent ดี

2. ไม่มี syntax ไม่เคยต้องเรียน syntax ใหม่ มีแต่ไลบราลีใหม่ อะไรที่เติมเข้ามาในภาษาแค่เราเข้าใจว่า ตัวแรกในวงเล็บ(form) มันทำอะไรก็จะทำให้เข้าใจมันทันที

3. พอ code เป็น data แล้วก็แปลว่าสามารถใช้ tool ที่ manage data มา manage code ได้ เช่น เวลาเขียน macro

4. Parser ของภาษาก็เขียนง่าย แล้วเวลาเขียนพวก tool เช่น refactoring tool ให้ ide, editor ก็ใช้ไลบราลีที่ manage code นี่แหละในการเขียน tool พวกนั้น

5. ส่งโค้ดผ่าน network ง่าย serialize ง่าย อาจจะไม่ใช่สิ่งที่ทำบ่อยๆ แต่เราสามารถเขียนระบบแบบส่ง code ไปรันบน server อื่นได้ เช่น network repl

6. นอกจาก code as data แล้ว เค้ายังนิยมออกแบบอย่างอื่นให้เป็น as data อีก เช่น config, html ทุกอย่างก็จะ consistent กันหมด ใช้ tool เดิมแนวคิดเดิมจัดการทุกอย่าง

เขียนครั้งแรกที่ https://www.facebook.com/groups/clojurethai/permalink/1235319406506550/

เชื่อมต่อ Clojure REPL เข้าไปที่ VM ที่ทำงานอยู่

คนที่เขียน Java มาอาจจะเคยต้อง restart server เป็น debug mode เพื่อต่อ debugger ของ IDE เข้าไป debug ปัญหา โดยมีความยุ่งยากคือต้อง restart ซึ่งอาจจะรบกวนการใช้งานของ user หรือบางครั้งหลังจาก restart แล้วปัญหาก็ดันหายไป

คนที่เขียน Ruby มาอาจจะเคย start irb บน server เพื่อลองรันคำสั่งต่างๆ แต่มันก็เป็นคนละโปรเซสกับที่ server ทำงานอยู่

บน Clojure เราสามารถเปิด port เอาไว้เชื่อมต่อ repl เข้าไปที่ VM หรือ server ที่ทำงานอยู่ได้ง่ายๆ โดยใช้ฟีเจอร์ที่ชื่อว่า Socket Server ซึ่งถูกเพิ่มเข้ามาใน Clojure เวอร์ชั่น 1.8

การใช้งานทำได้โดยการเพิ่ม option นี้เข้าไปเวลา start JVM

-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

* :accept คือ ฟังก์ชันที่จะถูกเรียกเมื่อมีการติดต่อเข้ามาทาง socket

ในที่นี้ผมขอยกตัวอย่างรัน repl ปกติขึ้นมาหนึ่ง vm พร้อมเปิด socket ให้เชื่อมต่อ repl เข้ามาเพิ่มได้อีกละกัน

ปกติผมจะ start repl ด้วยคำสั่งนี้

$ rlwrap java -jar /path/to/clojure.jar

เราก็ทำการเพิ่ม option ข้างต้นเข้าไป

$ rlwrap java -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" -jar /path/to/clojure.jar

เมื่อทำการรันคำสั่งด้านบนนี้ repl จะถูก start ขึ้นมาปกติ เมื่อ start เสร็จแล้ว เราสามารถ connect เข้าไปที่ VM นั้นผ่าน socket ได้โดยใช้ telnet ดังนี้

$ telnet localhost 5555

เราก็จะได้ repl ขึ้นมาอีกอันซึ่ง share data และ memory เดียวกับ repl แรกทั้งหมด เราสามารถทำทุกอย่างได้ตามชอบใจกับ VM นั้น

รายละเอียดเพิ่มเติมที่ Launching a Socket Server

เขียนครั้งแรกที่ https://www.facebook.com/groups/clojurethai/permalink/1231413573563800/

ทำไม Clojure ไม่ใช้ class ในการ model domain

การปรับตัวสำคัญอย่างหนึ่งสำหรับคนที่เริ่มใช้ Clojure คือ Clojure แนะนำให้ model domain ด้วย data structure ไม่ใช่สร้าง class เหมือนตอนเขียนภาษา OOP

Rich Hickey อธิบายเรื่องนี้ในวิดีโอนี้ได้ดีมาก เค้าบอกว่าเรื่องนี้เป็นส่วนสำคัญอย่างมากที่ทำให้โค้ดที่เขียนด้วย Clojure สั้นกว่า https://www.youtube.com/watch?v=VSdnJDO-xdg&t=2869 (นาทีที่ 47:49 – 54:15)

class ทำให้เกิด specific method ที่เราต้องรู้จักเพิ่มขึ้นอีกมากมาย (Rich แซวว่านี่แหละสาเหตุที่ autocomplete ของ IDE จึงสำคัญมากสำหรับการเขียนภาษาอย่าง Java, C#)

ในขณะที่ฝั่ง Clojure ใช้ data structure ทำให้เราสามารถใช้ collection library ที่มีอยู่ทั้งหมด ที่เรารู้จักและคุ้นเคยในการจัดการกับ data model นี้ ทำให้เราลดการเขียนโค้ดใหม่ขึ้นมาจัดการโดยเฉพาะไปได้

Rich ยกตัวอย่าง HTTP request ซึ่งอันนึงถูก wrap ด้วย Java class HttpServletReq ที่มีเมท็อดเยอะมาก และมีเมท็อดที่ทำงานใกล้เคียงกันอยู่เป็นจำนวนมากที่ถูกใส่เข้ามาเพื่อให้ใช้งานได้ง่ายขึ้น

ในขณะที่ Clojure ใช้ map ธรรมดา ฟังก์ชันที่มีอย่าง get,get-in,assoc,assoc-in,update-in และอีกมากมาย (ใต้หัวข้อ Collections, Sequences ในหน้านี้ http://clojure.org/api/cheatsheet) สามารถนำมาใช้กับ data model ใหม่ของเราได้ทันที

Alan Perlis – Turing Award คนแรกกว่าไว้ว่า
“It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.”
http://www.cs.yale.edu/homes/perlis-alan/quotes.html

การ model domain เป็น data structure ยังมีข้อดีอื่น ๆ อีก อย่างเช่น
– สร้างง่าย ใครเคยต้อง new class ที่ต้องการ parameter มากมายและใน constructor มีการทำงานเยอะ น่าจะพอเห็นภาพ
– debug ง่าย อยากดูอะไรก็ print มันออกมาก เห็นทุกอย่างไม่ต้องไป override เมท็อด .toString
– serialize/deserialize ง่าย ทำให้ persist และส่งผ่าน network ง่ายด้วย ไม่ต้องเขียน serializer/deserializer ของทุก class ที่จะส่งผ่าน network ให้ตรงกับรูปแบบที่ data transfer format รองรับ

โพสครั้งแรกที่ https://www.facebook.com/groups/clojurethai/permalink/1216890918349399/

ควรเขียนโค้ดใหม่บ่อย ๆ

เมื่อกี้ดู talk นี้ตอนทำอาหาร ล้างจาน พอทำเสร็จแล้วต้องกลับมาเปิดดูซ้ำอีกครั้งหนึ่งทันทีเพราะมีรายละเอียดที่น่าสนใจเยอะมาก แนะนำให้ทุกคนที่อยู่ในวงการพัฒนาซอฟแวร์ดู หลาย ๆ แนวคิดมีเหตุผลและน่าสนใจแต่ท้าทายสิ่งที่เราคิดและพยายามทำกันอยู่ในปัจจุบันพอสมควร

ผู้พูด คือ Chad Fowler อดีต CTO ของ Wunderlist ที่ถูก Microsoft ซื้อไป ตอนนี้เค้าเปลี่ยนตำแหน่งไปเป็น General Manager

หัวข้อจริงๆ ของ talk คือ Impermanance – the ironic key to systems that survive โดยในภาพรวมเค้าเล่าถึงระบบของ Wunderlist ที่ก่อนเค้าเข้าไปทำงานเป็น Monolith Ruby application แล้วตอนนี้กลายเป็นระบบที่เป็น microserivces ซึ่งมีหลักหลายร้อย service

เค้ากล่าวว่า “The system is the asset. Code is a liability.”

เค้าเปรียบเทียบการที่ร่ายกายคนเราอยู่มาได้จนถึงวันนี้ เกิดจาก cell แต่ละส่วนในร่างกายตายและถูกสร้างขึ้นมาใหม่อยู่ตลอดเวลา

โค้ตต้องเป็น component เล็ก ๆ ถูกเขียนใหม่บ่อย ๆ เราควรวางแผนที่จะทำให้เราสามารถโยนโค้ดชุดใด ๆ ทิ้งได้ สามารถเขียนขึ้นใหม่แทนได้

ระบบที่รันอยู่นานและไม่เคยเปลี่ยนเลยพอถึงเวลาที่เราจะต้องเปลี่ยนมันจะเปลี่ยนยาก ซึ่งเป็นความเสี่ยง ถ้าเราเปลี่ยนมันอยู่บ่อย ๆ เราจะมีความมั่นใจที่จะเปลี่ยนจะแก้ไขมันอยู่เสมอ

ระบบจะถูกแก้ไขเปลี่ยนแปลงได้ง่ายถ้าส่วนประกอบของมันถูกแก้ไขไม่ได้ ถ้าจะทำการแก้ไขแต่ละส่วนก็โยนทิ้งเขียนใหม่เลย

เค้าเล่าว่าหลังจากเปลี่ยนเป็น microservice ในปีแรก โค้ดประมาณ 80% ถูกเขียนใหม่ ไม่ได้ถูกเขียนใหม่เพราะแค่เขียนใหม่ แต่เขียนใหม่เพราะต้องบำรุงรักษา ปรับปรุงหรือเพิ่มฟีเจอร์ให้แก่มัน

เค้ามีนโยบายให้ทีมงานเลือกใช้ภาษาอะไรก็ได้ แม้กระทั่งเด็กฝึกงาน ขอแค่เข้ากับระบบ deploy และระบบ metric ที่มีอยู่ได้ ประโยชน์ที่ได้รับคือ ไม่มีปัญหาเมื่อจะต้อง upgrade อะไร เพราะทำอยู่เสมอ ๆ อยากลองใช้ภาษาอะไรก็ทำได้ง่าย เค้าเคยมีเหตุการณ์ระบบมีปัญหาตอนกลางคืน และคนที่อยู่เวรวันนั้นอ่าน Haskell ไม่รู้เรื่องเลยเขียนใหม่เป็น Elixir เพื่อแก้ปัญหาตอนนั้นซะเลย

อีกนโยบายหนึ่งของเค้า คือ no shared code ระหว่าง component เพราะมันจะทำให้การแก้ไขแต่ละ component กระทบกันทำให้เกิดการเปลี่ยนแปลงยาก ให้ใช้ share service แทน

ระบบของเค้าใช้การ communication ในรูปแบบง่าย ๆ ได้แก่ http/json เป็นส่วนหนึ่งที่ทำให้เขียน component ใหม่ได้ง่าย เค้าบอกว่าระบบที่เข้าใจง่ายสำคัญกว่าระบบที่มีประสิทธิภาพ (แต่สุดท้ายระบบที่เค้าเขียนให้เข้าใจง่ายนี้ก็เร็วกว่า monolith ของเก่ามากอยู่ดี)

ส่วนที่เปลี่ยนยากที่สุดของระบบเค้าเขียนด้วย Haskell มันสมบูรณ์แบบมากและไม่เคยต้องถูกแก้เลย ซึ่งในตอนแรกก็ภูมิใจกับมันมาก แต่แล้วพอวันที่เกิดปัญหามาถึงเกิดปัญหาที่รุนแรงกว่าและแก้ไขได้ยากกว่าปกติ เค้าบอกว่าถึงโค้ดจะสมบูรณ์แบบแต่สิ่งรอบข้างเปลี่ยนแปลงอยู่ตลอดเวลา เช่น dependency เก่าอาจจะใช้ไม่ได้แล้ว

ระบบของเค้าไม่เน้นใช้ testing ในการควบคุมคุณภาพของระบบ แต่เน้นที่การ monitoring, metric ที่ครอบคลุมอยู่ทั้งระบบ เช่น ถ้าผู้ใช้งาน sign-in ไม่ได้เค้าจะรู้ทันที เค้ากล่าวว่าเราป้องกันปัญหาทุกอย่างไม่ได้ เราจึงควรหาวิธีที่ทำให้รู้ปัญหาได้อย่างรวดเร็วและสามารถแก้ไขมันได้อย่างรวดเร็ว

มีครั้งนึงที่ลูกทีมของเค้าอยากให้วางกรอบเลือกใช้ซักแค่ไม่กี่ภาษาก็พอ ซึ่งในตอนแรกเค้าก็ดีใจ เพราะเค้าไม่ต้องเป็นคนพูดเอง เมื่อลูกน้องพูดด้วยตัวเองก็แปลว่าพวกเค้าเต็มใจทำ แต่สุดท้ายแล้วเค้าก็มาคิดว่ามันจะทำให้กลับไปเจอปัญหาเก่า ๆ เช่น upgrade version ยาก หรือสุดท้ายบริษัทกลายเป็นที่ ๆ เขียนกันอยู่ภาษาเดียว เช่น Scala แล้วก็จะมีปัญหาเมื่อวันหนึ่งก็ต้องมาใช้ C# เพราะถูก Microsoft ซื้อ

“Small is not a goal”
“Healthy, longevity is the goal”
“Systems that survive are made of components that can change”
“If it hurts, do it more often” (เอามาจาก XP) ถ้าอะไรที่มันต้องเปลี่ยนอยู่แล้วก็เปลี่ยนมันบ่อย ๆ และหาทางทำให้การเปลี่ยนแปลงนั้นมันไม่รุนแรงและเปลืองทรัพยากร
Impermanence -> Decoupling, Longevity, Agility

คำถามช่วงท้าย

Q: การมีหลายภาษาแล้วไม่ expert มีผลไหม?
A: ปกติภาษาที่มีใช้ก็จะมี expert อยู่ในทีม แต่ถ้าเค้าไม่อยู่แล้วก็เขียนใหม่

Q: มีปัญหาด้านความรู้สึกภายในทีมไหมกับการที่ต้องทิ้งโค้ดตลอดเวลา เปลี่ยนแปลงตลอดเวลา (จากบทสนทนาและเสียงผมค่อนข้างมั่นใจว่าคนถามคือ Dave Thomas)
A: มี ต้องพยายามสร้างความเข้าใจถึงประโยชน์ และ celebrate การเปลี่ยนแปลงอยู่เสมอ ๆ

— x —

ใครอ่าน/ฟังเรื่องนี้แล้วสนใจ ผมขอแนะนำบล๊อกโพสซีรียส์เรื่อง The New Normal ครับ จากคนละคนกันแต่วิธีคิดคล้าย ๆ กัน

http://blog.cognitect.com/?tag=NewNormal+Series

เป็นแนวคิดที่เปิดหูเปิดตาดีอยากให้ได้อ่านกัน เคยเขียน email ไปขอแปลแล้วเค้าบอกว่าไม่ให้ republish