Optimize….. เดี๋ยวววว ใจเย็นๆ

อันนี้เป็นเรื่องที่เกิดขึันในที่ประชุมวันนี้ครับ คือเรามีบั๊กตัวหนึ่งที่เกิดตอนสร้าง transaction โดยที่อันนี้เป็นตอนที่สร้างบันทึกการซื้อขายหลาย ๆ record พร้อม ๆ กันบน frontend แล้วระบบจะต้องไปหาคำสั่งการโอนเงินมาให้การซื้อขายแต่ละอัน โดยคำสั่งการโอนเงินจะต้องเข้ากับการซื้อขายนั้น ๆ แต่เราพบว่าคำสั่งการโอนเงินอันแรกสุดถูกใช้ในทุก ๆ การซื้อขายเลย

ที่มาเกิดจากโค๊ดหน้าตาแบบนี้ครับ อันนี้เขียนเป็น Java เพื่อให้อ่านเข้าใจง่ายขึ้น

BankTransferInstruction instruction = null;
for(Transaction transaction: transactions) {
  if(instruction == null) {
    instruction = findBankTransferInstruction(transaction);
  }
  transaction.setBankTransferInstruction(instruction);
}

เห็นบั๊กหรือยังครับ ? ถ้ายังลองคิดดูสักห้านาทีนะครับ

โค๊ดที่แก้ไขบั๊กนี้จริง ๆ ง่ายมากเลย แค่นี้เอง

for (Transaction transaction : transactions) {
  BankTransferInstruction instruction = findBankTransferInstruction(transaction);
  transaction.setBankTransferInstruction(instruction);
}

น่าจะเห็นแล้วนะครับว่า โค๊ดที่มีปัญหามีการมองหาคำสั่งการโอนเงินแค่ครั้งแรกครั้งเดียว (คือไม่เป็น null) ส่วนเราต้องการให้หาทุกครั้ง เราก็แค่สั่งให้มันหาใหม่เท่านั้นเอง

วันนี้มีการคุยกับเรื่องนี้ แล้วก็มีคนเสนอว่า เฮ้ย ทำไมเราไม่ optimize เพิ่มเติมอีกสักหน่อย เอาแบบนี้ …

BankTransferInstruction instruction = null;
for(Transaction transaction: transactions) {
  if(instruction == null ||
     instruction.isApplicable(transaction) {
    instruction = findBankTransferInstruction(transaction);
  }
  transaction.setBankTransferInstruction(instruction);
}

เพิ่มเงื่อนไขมาอีกอัน ก็น่าจะทำให้เร็วขึ้นนะ เพราะไม่ต้องหา instruction ใหม่บ่อย ๆ ใช่ไหมครับ ? ถ้าเรามองกันที่ performance อย่างเดียวก็แน่นอนว่ามันเร็วขึ้นอยู่แล้ว (แต่ก็มีกรณีที่ทำให้ช้าลงได้เหมือนกันครับ เช่นถ้า isApplicable() ดันต้องเรียก database ใหม่ จะกลายเป็นว่าในกรณีที่แย่ที่สุดจะต้องเรียก database สองครั้งต่อหนึ่ง transaction)

คำถามคือ มันจำเป็นต้องทำไหม ?

สิ่งแรกที่เราต้องประเมินกันก่อนที่จะทำ optimization คือ ณ.จุดนั้นมันเป็นปัญหาจริง ๆ หรือเปล่า ? อย่างอันนี้ในกรณีทั่ว ๆ ไปของระบบผม ลูกค้าจะไม่ค่อยสร้างคำสั่งซื้อหรือขายพร้อม ๆ กันมาก ๆ คือ ซื้อพร้อม ๆ กันสักสิบรายการก็แฮ่กแล้วครับ ดังนั้นจำนวนมันจะน้อยมาก ๆ ถ้ามันไม่ได้เป็นคอขวดจริง ๆ เราไม่จำเป็นจะต้องไปแก้ครับ

สิ่งที่สองคือ เราจะใช้ effort มากขนาดไหนในการ optimize อย่างกรณีนี้ถ้าเราเขียนฟังก์ชันใหม่ เราก็ต้องทดสอบ isApplicable() ว่าทำงานอย่างถูกต้องจริง ๆ เผลอ ๆ เราต้อง refactor หลาย ๆ ฟังก์ชันเพื่อโค๊ดดูแลรักษาได้ง่ายขึ้น (ใคร copy โค๊ดจาก findBankTransferInstruction() มาใช้ตรง ๆ นี่ผมด่าจริง ๆ นะเออ) ดังนั้นเราก็ต้องทดสอบหลาย ๆ ฟังก์ชันว่าทำงานถูกใหม่ ถ้าโค๊ดใช้ unit test ก็ต้อง refactor ตัว unit test ด้วย อะไรแบบนี้ ซึ่งตรงนี้มันก็ใช้เวลา

ถึงจุดนี้ ถ้าเราตัดสินใจจะทำจริง ๆ สิ่งที่เราต้องทำต่อคือ ลอง benchmark แยกกัน ระหว่างโค๊ดเก่า กับโค๊ดใหม่ ซึ่งในขั้นนี้อาจจะเป็นโค๊ดที่ยังไม่สมบูรณ์ก็ได้ครับ เอามาเทียบกัน แล้วดูว่า ความแตกต่างกันมีมากน้อยแค่ไหน และประเมินต่อว่า เมื่อโค๊ดใหม่เข้าไปในระบบแล้วจะช่วยให้เราประหยัดเวลาไปได้มากน้อยแค่ไหนในระยะเวลาหนึ่ง (สมมติว่าหนึ่งเดือนหรือหนึ่งปีก็ได้) แล้วดูว่าถ้าจะ implement ต่อจนเสร็จ ทดสอบเรียบร้อย ผ่าน Function Testing และ UAT ไปแล้วจะต้องใช้เวลามากน้อยแค่ไหน ใช้งบประมาณเท่าไหร่

ผมอาจจะประเมินคร่าว ๆ ว่า หน้าคำสั่งซื้อขายนี้เนี่ย ผู้ใช้หนึ่งคนอาจจะใช้เวลากับมันประมาณ 1 ชั่วโมงต่อวัน หรือคิดเป็น workload 12.5% ถ้าผมสามารถลดเวลามันลงไป 15 นาที ก็เท่ากับว่าผมสามารถทำให้ efficiency ของผู้ใช้คนนี้เพิ่มขึ้น 25% ของ 1 ชั่วโมงจาก 8 ชั่วโมงต่อวัน ก็จะเป็น 25% * 12.5% = 3.125% ซึ่งผมว่าตัวเลขที่มากกว่า 1% ก็คุ้มค่าที่จะลงทุนนะครับ อันนี้แก้ไปเถอะ

แต่ถ้าแบบ ลดเวลามันลงไปได้ 3 นาที … ก็เท่ากับเพิ่มได้แค่ 0.625% อันนี้ก็ต้องเริ่มดูละว่ามันคุ้มจริง ๆ หรือเปล่า ?

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

อย่างกรณีผมที่ทำงานกับ Legacy Code เป็นหลัก โค๊ดไม่ได้เขียนเป็น procedure มากนัก ภาษาก็เก่า โอกาสที่พลาดสูง และไม่มี unit test เนี่ย ผมมองว่ามันเป็นการเพิ่มปัจจัยเสี่ยงมากเกินไปและผลตอบแทนก็ไม่คุ้ม ก็เลยแย้งไปว่าอย่าทำเลยดีกว่า …

ทั้งนี้การทำ optimization ก่อนที่จะประเมินเรื่องผลตอบแทนและค่าใช้จ่ายนั้น เป็นการทำ premature-optimization รูปแบบนึงครับ (อีกรูปแบบนึงคือโค๊ดยังไม่เป็นรูปเป็นร่างเลยแต่ทำ optimize แล้ว)

Legacy Coder

เคยเขียนถึง Legacy Code ไปเมื่อหลายปีก่อน วันนี้จะเล่าเรื่องที่คล้าย ๆ กันนิดนึง

เมื่อประมาณต้นปีพอดีมีโปรเจคแก้โค๊ดระดับค่อนข้างใหญ่ ก็คือ การแก้ไข functionality ที่สำคัญมากอันนึงในทั้ง product เป็นปัญหาที่มีร่วมกันในทุก ๆ function ย่อย ๆ ของ product ซึ่งมีการตัดสินใจว่าจะทำการแก้ปัญหาลักษณะเดียวกันพร้อมกันไปเลย

วิธีการแก้ไขตรงนี้คือมีคนเขียนคลาสที่จัดการเรื่องการคำนวนตรงนี้ เป็นคลาสตรงกลางที่ใช้ร่วมกัน

สิ่งที่ผมต้องทำคือทำการทดสอบว่า function ที่ได้รับมอบหมายมามีปัญหาหรือไม่ ถ้ามีก็ค่อยแก้ แต่ตัวโค๊ดที่ใช้แก้มีการทำมาให้แล้วอย่างที่บอกไว้ข้างบน สิ่งที่ผมพบคือโค๊ดที่มีการเขียนนั้นมีการใช้ API ที่มีปัญหา และที่จริงก็มี API อีกชุดหนึ่งจาก 3rd party ที่สามารถใช้ทดแทนกันได้เลยโดยไม่ต้องเขียนใหม่

ผมเดินไปคุยกับคนที่ดูแลโปรดักท์ตัวนี้ว่า ผมคิดว่าวิธีที่เราใช้นั้นไม่ถูกต้อง การใช้โค๊ดจาก 3rd party ที่มี unit test ครบถ้วนและ functionality ที่ครอบคลุมกว่าจะเป็นประโยชน์กับทีมในระยะยาว อย่างน้อยเราก็ไม่ต้องมานั่งดูแล library ตัวนี้ และมั่นใจได้มากกว่าว่ามันจะไม่สร้างปัญหา

คนที่ผมคุยด้วยก็ตอบว่าเข้าใจในประเด็นที่ผมต้องการจะสื่อ แต่ในขณะเดียวกันเขาก็บอกว่า ไม่ใช่ทุกคนในทีมที่เก่งเหมือนผม …

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

ผมไม่คิดว่าคนที่ผมคุยด้วยเป็นที่ไม่รับฟังอะไร แต่เขาต้องคิดถึงอีกหลายคนที่จะต้องมาเขียนโค๊ดต่อ เขาก็เลยเลือกวิธีที่คนอื่น ๆ ในทีมคุ้นเคย ซึ่งก็เป็นเรื่องที่น่าเศร้าเหมือนกัน อีกปัญหานึงคือตอนที่ผมไปคุยเป็นตอนที่ค่อนข้างสายเกินไปแล้ว ตอนนั้นคือเทสต์กันจะเสร็จหมดแล้วก็เลยต้องปล่อยเลยตามเลย ผมค่อนข้างเสียดายเพราะถ้าสามารถแก้ได้น่าจะลดปัญหาที่อาจจะเกิดขึ้นในอนาคตได้อีกมากทีเดียว (ระบบที่ผมทำงานอยู่ไม่มีการเขียน Unit Test ครับ ดังนั้นเรื่องความมั่นใจในโค๊ดเนี่ยแทบจะ 0 เลย ผมต้องตีไว้ก่อนว่ามันจะมีปัญหาแน่ ๆ )

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

ปัญหาที่น่ากลัวพอ ๆ กันคือคนกลุ่มนี้ก็มักจะสอนให้คนใหม่ ๆ ให้ทำวิธีเดียวกับตัวเอง โดยอ้างว่าเป็นวิถีของคนที่มี seniority เขาทำกัน ส่วนตัวผมอยากเห็นคนรุ่นใหม่ ๆ กระตือรือร้นในการเรียนรู้อะไรใหม่ ๆ และสามารถ contribute ความรู้ที่ตัวเองเรียนมที่คนเก่า ๆ เขาไม่เคยเรียนรู้มาในการะทำงานด้วย ไม่ใช่ว่าพอเรียนเรื่องดี ๆ มาแล้วเจอคนเก่า ๆ ล้างสมองให้ทำตามวิธีของตัวเองหมด (โดยอ้างว่าชีวิตการทำงานมันต่างจากการเรียน)

แต่ผมก็คิดนะว่าบางทีเด็กมันก็เป็นแบบนี้มาตั้งแต่แรกแล้วเหมือนกัน ซึ่งก็เป็นเรื่องน่าเศร้าพอสมควร ผมพบว่าเราจะค่อนข้างให้คุณค่ากับคนที่สามารถทำตามวิธีของเราได้ โดยไม่สนว่าเขาอาจจะมีวิธีที่ดีกว่า ซึ่งนั่นหมายถึงคนที่เรียนเก่งมักจะเป็นคนที่ไม่นำเสนอเรื่องใหม่ ๆ แต่มักจะเป็นคนที่ทำตามวิธีของคนอื่นได้มากกว่า (และพอถึงจุดที่ต้องนำเสนอวิธีใหม่ ๆ ก็มักจะทำได้ไม่ดี)

เอาเถอะ ผมคงต้องพยายามจนกว่าจะอยู่ในจุดที่สามารถนำเสนอความคิดของตัวเองได้เต็มที่ ซึ่งก็คงอีกนานทีเดียว 😛

อยากเป็นโปรแกรมเมอร์จะเริ่มต้นยังไง

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

  1. คือ เริ่มหัดเขียนเลย เริ่มจากเขียนอะไรก็ได้ เริ่มจากลอกโค๊ดชาวบ้านจากหนังสือหรือสื่อการสอนต่าง ๆ นี่ล่ะ คือ ณ.จุดนี้เรียนรู้จากการลอกงานคนอื่นเป็นอย่างแรกครับ (จะเป็นภาษาอะไรไม่สำคัญ เอาให้มันเขียนออกและทำงานได้ก่อน) แล้วเมื่อลอกมาเสร็จให้ลองทำความเข้าใจว่าไอ้สิ่งที่เราเขียนลงไปนั้นมันทำอะไรและทำงานยังไง ใช้ความเข้าใจของตัวเอง ถูกผิดค่อยว่ากันทีหลังครับ

  2. หา mentor หรือ adviser ก็คือหาคนมาช่วยแนะนำเป็นที่ปรึกษา ตรงนี้สำคัญแต่หลาย ๆ คนไม่ได้ทำ ผมเชื่อว่าถ้าเราหัดเองประมาณมันก็จะทำได้ประมาณนึงใช้เวลาประมาณนึง แต่ถ้ามีคนมาให้คำปรึกษาจะทำให้ลดระยะเวลาลงไปได้มาก แต่ในทางกลับกัน ถ้ามีที่ปรึกษาที่ไม่ดี ก็อาจจะทำให้เราใช้เวลานานเกินไปได้เหมือนกัน อันนี้ต้องระวัง สำหรับน้อง ๆ นักเรียน ก็ลองคุยกับอ.ที่ปรึกษาที่โรงเรียนก็ได้ แล้วก็ลองคุยกับอ.แนะแนวดูก็ดีนะครับ อ้อ อ.ที่สอนคอมพิวเตอร์ด้วย อ.คอมพิวเตอร์หลาย ๆ ที่ก็เขียนโปรแกรมได้ดีนะครับ

  3. หาพรรคพวกเรียนไปด้วยกัน เป็นกลุ่มย่อย ๆ 3-5 คน การเรียนรู้เป็นกลุ่มจะทำให้เกิดการพัฒนาที่ดีกว่าการเรียนคนเดียว (แต่ต้องเป็นกลุ่มที่มีขนาดเล็กและสนิทกันระดับหนึ่งนะครับ)

  4. ศึกษาภาษาอังกฤษ และศึกษาจากสื่อภาษาอังกฤษ สองอันนี้เป็นส่วนที่จะเติมเต็มซึ่งกันและกัน เราต้องรู้ภาษาอังกฤษก่อนจะไปอ่านหนังสือ แต่การอ่านก็ทำให้เรามีทักษะที่ดีขึ้นด้วย (ขึ้นกับความสามารถในการเรียนรู้แต่ละบุคคล) ผมคิดว่าสื่อการสอนในบ้านเราไม่ค่อยหลากหลาย และคุณภาพก็ไม่ดีนัก (ส่วนใหญ่) ทั้งหนังสือแปลและเขียนใหม่ สื่อการสอนภาษาอังกฤษจะมีคุณภาพดีกว่าและหลากหลายกว่า ทั้งนี้การเลือกสื่อการสอนตรงนี้สำคัญมาก เราต้องเรียนทั้งทฤษฎีและปฎิบัติควบคู่กันไป การปฎิบัติที่ขาดความเข้าใจในทฤษฎีเป็นข้อบ่งชี้ถึงปัญหาในระดับพื้นฐาน และจะทำให้การศึกษาในระดับที่สูงขึ้นทำได้ยากด้วยครับ

  • อ้อ ไม่จำเป็นต้องเป็นหนังสือเสมอไปครับ วิดีโอ พอดแคสต์ หรืออะไรก็ได้ ทั้งนี้ควรคุยกับ mentor ว่าสื่อการสอนอันไหนน่าสนใจและเหมาะกับตัวเราดู
  1. ศึกษาต่อในคณะสายตรง (Software Engineer, Computer Science เป็นต้น) ในระดับมหาวิทยาลัย ผมไม่ได้บอกว่ามหาวิทยาลัยจะให้อะไร และผมก็เชื่อว่ามีหลาย ๆ คนที่ไม่ได้เรียนด้านนี้แต่ประสพความสำเร็จเช่นเดียวกัน แต่ผมกำลังจะบอกว่าในมหาวิทยาลัยคุณจะได้พบกับอาจารย์ที่มีความสามารถจำนวนมากที่คุณสามารถขอให้เขาเป็น mentor หรือ adviser ได้ (มีให้เลือกเลยนั่นแหละ) และก็มีเพื่อนที่ร่วมเรียนไปในทิศทางเดียวกัน คอยเสริมกันและแข่งขันกัน นั่นจะเป็นข้อได้เปรียบเมื่อเทียบคนที่ไม่ได้จบเฉพาะทางมา
  • (ทั้งนี้ก็ต้องระวังเรื่องการคบคนด้วยนะครับ ไอ้ประเภทเมามันทุกเย็นก็ไม่ไหวนะ)
  1. เข้าร่วมสัมนา งานมีทติ้ง และอื่น ๆ ก็เข้าสังคมนั่นล่ะครับ เข้าไปคุยแลกเปลี่ยนทัศนะกัน ได้พบเจอคนมากขึ้น อาจจะได้เจอเพื่อนร่วมเรียนและที่ปรึกษาจากงานประเภทนี้ได้เช่นกัน

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

  3. แบ่งปันความรู้ ความรู้เป็นเรื่องแปลกครับ ยิ่งแบ่งปันมากยิ่งรู้มาก เราไม่ได้อยู่ในสมัยที่เราควรปิดบังเรื่องที่เรารู้ทั้งหมดเพื่อที่จะสร้างความได้เปรียบ คือปิดบ้างก็ได้ไม่เป็นไร แต่เรื่องพื้น ๆ สอน ๆ ไปก็ไม่ได้เสียหาย ในทางกลับกันการสอน การแบ่งปัน การพูดเสวนา จะเป็นการวัดระดับความเข้าใจของตัวเราเอง มันจะทำให้เรารู้ว่าเราเข้าใจมากน้อยแค่ไหน คนเราไม่สามารถอธิบายเรื่องราวที่ตัวเองไม่เข้าใจได้ ถ้าเราไม่เข้าใจอะไร เวลาพูดออกมาคนก็จะฟังไม่รู้เรื่องเช่นกัน (และในทางกลับกัน การที่เราพูดไม่รู้เรื่องในเรื่องราวอะไร ก็แปลว่าเราไม่เข้าใจมันเช่นกันครับ)

  4. เปิดโค๊ด คือ ผมคิดว่า การเปิดโค๊ดโปรเจคของเรา (โดยเฉพาะไอ้ที่ไม่ค่อยสร้างรายได้หรือขายไม่ได้) ก็เป็นการเปิดโอกาสให้คนอื่นเข้ามาให้ความเห็นกับโค๊ดของเรา เป็นการพัฒนาตัวเองในอีกรูปแบบหนึ่ง ไอ้พวกโปรเจคเล็ก ๆ น้อย ๆ เขียนเล่น ๆ นี่เปิดไปเถอะครับ เราอาจจะเปิดโค๊ดโปรเจคเรา แล้วให้คนอื่นเข้ามาดูได้ ก็ไอ้พวกบรรดาเพื่อน ๆ เรานี่ล่ะที่จะมาช่วยยำเป็นกลุ่มแรก ๆ

  • ทั้งนี้ผมไม่ได้บอกให้เปิดทุกอันนะครับ อะไรที่ดูแล้วน่าจะต่อยอดทางธุรกิจได้ ก็เก็บ ๆ ไว้ขายบ้างก็ได้เหมือนกัน

คิดออกแค่นี้ มีความอะไรก็ทิ้งไว้ได้นะครับ ขอบคุณครับ