Photo by Ilya Pavlov, from Unsplash.com

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

ในบันทึกฉบับนี้ ผมจะแนะนำเครื่องมือจำพวก Static code analysis ครับ (จากนี้ไปจะเรียกย่อๆว่า SCA)

ทำความรู้จัก Static code analysis

ปกติแล้วการทำ SCA จะทำในขั้นตอนเดียวกันกับการ Build/Compile

คำว่า Static ถ้าแปลตรงตัวคือ “สถิตย์” ซึ่งหมายถึงว่าไม่มีการเคลื่อนไหว (เหมือนคำว่าพลังไฟฟ้าสถิตย์ที่เราเรียนม.ปลาย)

สาเหตุที่เรียกว่า Static เป็นเพราะขั้นตอนนี้จะไม่มีการทำงานของโปรแกรมเลย เป็นการแสกนโค้ดแล้วแสดงผลการวิเคราะห์ออกมาโดยตรง

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

โดย SCA นั้นมีการตรวจสอบหลายแบบ โดยมักจะทำตั้งแต่ก่อน Build ในที่นี้จะยกตัวอย่างที่เห็นบ่อยๆให้ดูครับ

  1. Syntax checking ในภาษาจำพวก Interpreter คือไม่มีการ Compile โปรแกรมก่อนการทำงาน หากเขียนโปรแกรม Syntax ผิดนิดเดียว ไม่มีทางรู้ได้จนกระทั่งถึงเวลาทำงานจริง ถึงแม้ปัจจุบันจะมี IDE ช่วย แต่โปรแกรมเมอร์เองก็เป็นมนุษย์ที่ผิดพลาดเป็นปกติ การตั้ง SCA ประเภทนี้ไว้ใน CI จึงมีประโยชน์มากสำหรับภาษาจำพวก Interpreter
  2. Style checking โค้ดต่างๆนั้นถูกเขียนขึ้นครั้งเดียว แต่อ่านหลายครั้ง ทีมจึงมักจะมีข้อตกลงในในเรื่องของการเขียนโค้ดให้เป็นไปในรูปแบบเดียวกันเพื่อหห้อ่านง่าย โดย SCA จำพวกนี้จะสามารถทำการ Config ค่าเพื่อให้เลือกตรวจรูปแบบการเขียนโค้ดให้ตรงตามที่ทีมกำหนด (Style/Convention) ตัวอย่างที่เช็คก็ได้แก่
  • ชื่อตัวแปร (Naming convention)
  • ขนาดและวิธีการเว้นช่องไฟ (Tab/space)
  • การเขียนคอมเม้นต์
  • ความซับซ้อนของโค้ด (Cyclomatic complexity)
  1. Bug/Bad practice detection ถึงแม้ SCA จะไม่ได้ลองรันโปรแกรมเพื่อทดสอบจริงๆ แต่มันสามารถตรวจจับลักษณะของโค้ดที่มักจะเกิดบั้กได้ เพื่อแจ้งเตือนเราก่อนล่วงหน้า ตัวอย่างเช่น
  • การสร้างตัวแปรทิ้งไว้โดยไม่ใช้งาน (ซึ่งอาจจะมาจากการพิมพ์ชื่อตัวแปรผิด)
  • การใช้ == แทน .equals() บน Object ใน Java
  • การใช้เรียกฟังก์ชั่นที่ไม่ได้รับการรองรับในบราวเซอร์รุ่นเก่าๆ (JavaScript)
  1. Code duplication เคยก็อบแปะกันไหมครับ? บางทีก็เปลี่ยนชื่อตัวแปรหน่อยนึง กะว่าลองใช้ดูก่อนว่าเวิร์คเปล่า แล้วค่อยมาแก้ทีหลัง แต่พอเอาเข้าจริงก็ไม่เคยแก้เลย SCA จำพวกนี้จะทำการแจ้งเตือนหากมีโค้ดที่หน้าตาคล้ายกันมากๆ หากในทีมมีปัญหานี้มากๆ การตรวจสอบแบบนี้จะช่วยตัดไฟตั้งแต่ต้นลมครับ

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

ถ้า Java ตัวที่นิยมก็จะมี CheckStyle, PMD, FindBugs มีคนสรุปข้อเปรียบเทียบเอาไว้ที่ ลิ้งก์นี้

ส่วน JavaScript ก็จะเป็น JSLint, JSHint, ESLint หรือพวก Transpiling Languages (ex. TypeScript, ES6 + Babel) ส่วนใหญ่ก็จะเพิ่มเติมการตรวจสอบมาให้โดยอัตโนมัติ

กรณีไหนควรใส่ SCA ลง CI?

ก่อนจะตัดสินใจ เรามาประมวลประโยชน์ของ SCA กันก่อน ว่าช่วยอะไรเราได้บ้าง

  1. คุมคุณภาพของโค้ดได้โดยไม่ต้องใช้คน ประหยัดเวลาและเที่ยงตรง
  2. Junior dev ได้เรียนรู้ Bad Practices ที่ไม่ควรทำ
  3. รันเร็ว เจอปัญหาเร็ว ใช้เวลาแก้น้อยกว่าบั้กที่เจอจากการเทสต์ในตอนท้ายๆเช่น UAT (User Acceptance Testing)

ดังนั้น ผมแนะนำให้ใส่เกือบทุกกรณีครับ เพราะ SCA นั้นจะเหนื่อยแค่ตอนใส่แค่ครั้งแรก สมัยนี้พวก CI Software ก็แทบจะมี Plugin ของตัวดังๆมาให้เกือบหมด กดฉึกๆก็มีรีพอร์ตให้อัตโนมัติแล้ว ไม่ได้ต้องมานั่งเขียน Bash Script กันให้วุ่นวาย

โดยเฉพาะถ้ามีทีมมี Junior Dev, Offshore หรือ Outsource กรณีนี้ยิ่งควรที่จะตรวจสอบอัตโนมัติเป็นอย่างยิ่ง เพราะจะช่วยให้โค้ดมีมาตรฐานในระดับหนึ่งได้โดยอัตโนมัติ

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

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

ข้อแนะนำในภาคปฏิบัติ

1. ค่อยเป็นค่อยไปเพื่อลดแรงต้าน

ในทางปฏิบัติจริง การใส่ SCA มักจะได้แรงต้านจากคนในทีมมากครับ เพราะจากที่เคยเขียนอะไรก็ได้ ต้องมารันเช็คอีกรอบนึงก่อน Push (แล้วถ้าลืม ก็จะไปพังบน CI Server ให้เสียเวลาอีก) รันเสร็จก็แดงเถือก ต้องมานั่งแก้กันอีก ซึ่งหลายๆครั้ง ที่เช็คไม่ผ่านก็ดูจะเหมือนจะเป็นเรื่องหยุมหยิม แค่รายละเอียดเล็กๆน้อยๆ

ขอเตือนว่า อย่าดูถูกเรื่องหยุมหยิมพวกนี้นะครับ โค้ดที่พวกเราก่นด่านกันว่าอ่านไม่รู้เรื่อง มันเริ่มจากการปล่อยปละละเลยเรื่องหยุมหยิมพวกนี้แหละ พอนานเข้า กองมันจะใหญ่เรื่อยๆแล้วต้องมาทนทุกข์ในภายหลัง

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

หลังจากนั้นจึงขอข้อตกลงในทีมว่าหลังจากผ่านไปสองเดือน เราจะเพิ่มการเช็คให้ละเอียดขึ้นเรื่อยๆนะ

2. โค้ดเดิมรัน SCA ทีแดงพรืด

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

อันนี้ต้องทำใจให้แข็งแกร่ง เพราะจะเร็วจะช้ามันเป็น Technical Debt ที่ต้องแก้

โดยในทางปฏิบัติ เริ่มจาก Config เพื่อตรวจแค่ตัวสำคัญๆก่อน แล้วจึงค่อยๆเพิ่มครับ เพราะแก้ทีเดียวหมื่นจุดนี่อาจจะใช้เวลานานมากกว่าจะเสร็จ

ในกรณีนี้ อาจจะต้องให้ PO (Product Owner) หรือ Manager ยอมให้เวลาเราในการแก้ไขคุณภาพของโค้ดด้วย ถ้าใช้ Scrum ก็ต้องมีการทำ Story ไว้บน Backlog ชัดเจน ว่าอีกกี่เดือนจะเริ่มเช็คอะไรเพิ่ม

ส่วนใหญ่ถ้า Manager มีพื้นฐานด้านเทคนิคจะคุยกันง่ายหน่อย แต่ถ้าเป็น PO ที่มาจากฝั่งธุรกิจ พยายามอธิบายให้เขาฟังเป็นตัวเลขครับ ว่าถ้าเกิดบั้กบนระบบจริงแล้วบริษัทจะเสียเงินหรือชื่อเสียงแค่ไหน หรือการแก้บั้กตั้งแต่ก่อน Push โค้ดกับรอให้ไปถึง UAT (User Acceptance Testing) แล้วเนี่ย เวลาที่ใช้มันต่างกันมหาศาลแค่ไหน

3. แก้แต่ละครั้งยากมาก

การที่เราแก้จุดที่ SCA ตรวจเจอไม่ได้ อาจเป็นสัญญาณของการออกแบบที่ไม่ดีนะครับ (Bad design)

ซึ่งอาการที่พบคือจะแก้จุดเดียว ต้องรื้อคลาสใหม่สามสี่คลาส

พวกนี้จะเจอบ่อยในเรื่องของ Bug/Bad practice check เวลาตั้งให้การตรวจเข้มงวด

พอถึงจุดนี้ปุ๊บ ทีมจะต้องเลือกระหว่างทำการ Override เพื่อให้ Build ผ่าน หรือลงทุนใช้เวลาแก้

การ Override มักจะอยู่ในรูปแบบของการเขียนคอมเม้นต์เพื่อบอก SCA Tool ให้เว้นการตรวจสอบบริเวณนี้

โดยหลักแล้ว ผมแนะนำให้ใ่ช้เวลาแก้ให้ดีครับ เพราะถ้า Override ไปครั้งแล้ว ส่วนใหญ่อีกสักพักก็จะโผล่ออกมาให้ Override อีก เพราะมันผิดตั้งแต่การออกแบบแล้ว

4. เข้มงวดในการ Override

พอทีมเริ่มมีการ Override กันสักครั้งสองครั้ง ก็จะเริ่มมีการแห่ Override กันพรึ่บ

ทีมควรมีข้อตกลงที่ชัดเจนครับว่ากรณีไหน Override ได้หรือไม่ได้ หรือถ้าเป็นกรณีใหม่ๆก็อาจจะต้องให้ Senior Dev รีวิวก่อน

หากปล่อยให้ Override กันตามใจ เป็นนิสัย ไอ้ SCA ที่เราอุตส่าห์สร้างมาก็ไม่มีประโยชน์ครับ

การปล่อยให้ Override SCA นี่บาปใกล้เคียงกับการ Comment Unit test ออกเวลาเทสต์พังนะครับ

ซึ่งกรรมจะตามทันแน่นอน การเขียนโปรแกรมนี่กรรมติดจรวดเลย

สรุป

หลังจากทีมมี Unit test และ CI Server แล้ว ผมแนะนำให้ทำการติด SCA เป็นลำดับถัดไปเลยครับ เพราะมันแทบไม่ต้องใช้เวลาในการดูแลรักษาเหมือนเทสต์ รันเร็ว ได้ผลเร็ว แล้วเป็นการสอนทีมให้หลีกเลี่ยง Bad Practice ไปในตัว

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

ถ้าคุณอยู่ในฐานะซีเนียร์หรือทีมลีด เป็นหน้าที่ของคุณที่จะต้องควบคุมคุณภาพของซอฟท์แวร์ กับความเร็วในการพัฒนาฟีเจอร์ใหม่ๆ ซึ่งกรณีนี้ บางทีเราก็ต้องยอมจ่าย Technical Debt เพื่อไปใช้คืนทีหลัง แต่ระวังอย่าให้ล้มละลายนะครับ ถ้ารู้สึกว่าทีมกำลังจ่ายเยอะไป ขืนทำแบบนี้ต่อไปจะไปไม่รอด ก็เป็นหน้าที่ของคุณที่ต้องเหยียบเบรค แล้วเข้าไปเจรจากับผู้เกี่ยวข้องทั้งหลาย