ทำไมต้อง Code Review? แนะนำ Hound และ Hubot

Nuttavut Thongjor

สองปีก่อน ณ ค่ำคืนหนึ่งตอนตีสอง ลูกค้าฝั่งประเทศผู้ดี UK เจอบัคที่อยากให้แก้เดี๋ยวนั้น ด้วยความเป็นลูกจ้างที่ภักดีต่อเงินเดือน จึงต้องแก้ไปหาวไปอยู่คนเดียว เสร็จปุ๊บโยนขึ้น Github ปั๊บ CircleCI รันเทสผ่าน Deploy ทันที เอาหละ.. นอนซิ รออะไร!

เช้าวันรุ่งขึ้น ขายังไม่ทันก้าวเข้าออฟฟิต

เจ้านายฝรั่ง: "Nut, why %#%$#%!#@3@@"

ผม: (พึ่งตื่นมา ฟังไม่รู้เรื่อง เห็นเจ้านายหน้ายิ้มๆ ต้องชมแน่เลย เป็นไงหละเจอศิลปะการแก้บัคชั้นเลิศ)

หลังจากถอดรหัสแล้วจึงได้ความว่า เมื่อคืนนี้หลัง deploy โค๊ดขึ้นเซิฟเวอร์ ลูกค้าเมืองผู้ดีกรี๊งกร๊างมาต่อว่าเจ้านายว่า เข้าเว็บแล้วค้างไปเลย ไร้การตอบสนองจากสามโลก

ก้นหย่อนเก้าอี้จึงเปิด Sublime ขึ้นมาดู... น้อง binding.pry ที่เป็น debugger ของฝั่งภาษา Ruby เสนอหน้าโชว์ตัวเลยครับ นั่นละฮะท่านผู้ชม ผมใส่ debugger ไปในโปรแกรมบน production มันจึงติด debug ไม่ยอมทำอะไรเลย ไอคอน loading... จึงหมุนตะแหง่วๆไม่ยอมหยุด

นิทานเรื่องนี้สอนให้รู้ว่า จงลองเปิดเว็บหลัง deploy ซะ แต่ถ้าจะให้ดีกว่านั้นก็อย่าให้เรื่องแบบนี้เกิดขึ้น

เราจะหลีกเลี่ยงมันได้อย่างไร?

ปัญหาเกิดจากผมแก้บัคคนเดียว โยนโค๊ดที่แก้แล้วขึ้น Github แล้ว Deploy เลยโดยปราศจาก การตรวจสอบจากเพื่อนร่วมทีม เหตุนี้ Code Review จึงเป็นสิ่งสำคัญในการพัฒนาซอฟต์แวร์เพื่อหลีกเลี่ยงข้อผิดพลาดที่เราไม่อยากให้เกิดขึ้น

ทำอะไร ใครจะรู้?

ถ้าเราสร้าง feature ใหม่, แก้ไข bug หรือทำอะไรซักอย่าง พร้อมทั้งเปิด pull request บน Github เรียบร้อยแล้ว สิ่งหนึ่งที่ห้ามลืมทำเป็นอันขาดคือ บอกด้วยว่าเราทำหรือแก้ไขอะไรไป เมื่อโค๊ดนี้รวมเข้าโค๊ดหลักแล้วจะส่งผลอะไรบ้าง เพื่อให้ผู้ Review ได้ทราบว่าต้องพิจารณาเรื่องไหนเป็นพิเศษ

Description

ยาวไปไม่อ่าน

เคยไหมอ่านหนังสือติดต่อกันหลายหน้า อ่านไปซักพักเริ่มลืมเลือนว่าหน้าแรกๆที่อ่านกล่าวถึงอะไร Code Review ก็เช่นกัน SmartBear พบว่าจำนวนบรรทัดที่เหมาะสมต่อการ Review ไม่ควรเกิน 200-400 บรรทัด หมายความว่าอย่างไร?

งานหนึ่งชื้นที่คุณหยิบมา implement/fix bug/อื่นๆ ควรมีขนาดไม่ใหญ่มากนัก ไม่ซับซ้อนเกินไปจนสามารถผลิตโค๊ดผลลัพธ์ได้ถึง 400 บรรทัดซึ่งนั่นก่อความลำบากต่อการเขียนโค๊ดและการ Review การศึกษาของ SmartBear ยังพบว่าปริมาณบรรทัดของโค๊ดที่มากขึ้นต่อหนึ่งการ Review ทำให้พบข้อผิดพลาดของโค๊ดที่เขียนได้น้อยลง ดังภาพข้างล่าง

LOC-Defect

ไม่ต้องทำเสร็จก็เปิด pull request ได้

บางครั้งเราเขียนโค๊ดแค่บางส่วนก็สามารถเปิด pull request ให้ผู้อื่นมา Review ได้ทันที ประโยชน์ที่ได้จะเกิดขึ้นเมื่องานชิ้นนี้มีความซับซ้อนทำให้เกิดความไม่แน่ใจว่าสิ่งที่กำลังทำอยู่ดีที่สุดหรือถูกต้องไหม หากเราปล่อยให้ตนเองเขียนโค๊ดจนเสร็จแล้วค่อยเปิด pull request ความลำบากจะตามมาเมื่อเพื่อนในทีมมีไอเดียที่ดีกว่า ทำให้เราต้องเริ่มเขียนใหม่อีกครั้งจากศูนย์ การทยอยทำสลับกับเปิดให้คนอื่น Review จึงลดความเสี่ยงในข้อนี้ ทั้งนี้มีสองสิ่งที่ต้องไม่ลืมทำคือสร้าง Checklists ไว้ตรวจสอบด้วยว่าทำอะไรไปถึงไหนแล้ว และแปะ [WIP] ย่อมาจาก Work in progress ในชื่อของ pull request ดังตัวอย่างข้างล่าง

WIP

ต่างคนต่างสไตล์

JavaScript
1let message = 'hello world'
2let obj = { message: message }

เคยเจอคอมเมนต์แบบนี้ไหมครับ ทำไมไม่ใช่ single quote สำหรับข้อความ ห๊ะ? หรือ ทำไมไม่ใช้รูปย่อแบบนี้หละ var object = { message }? เรื่องแบบนี้เป็นสไตล์ส่วนบุคคลครับ นายเออาจชอบใช้ double quote แต่นายบีอาจไม่ชอบ เมื่อเป็นเช่นนี้จึงเกิดการคอมเมนต์ความคิดเห็นส่วนตัว เพื่อให้เกิดการตัดสินใจได้เราควรมีคนกลางผู้ช่วยกำหนดรูปแบบโค๊ดของทีมเรา นั่นคือใช้เครื่องมือประเภท Linter ต่างๆมาช่วย review โค๊ดของเราครับ เราเรียกสิ่งนี้ว่า Automated code review

แนะนำ Houndci

บทความนี้ผมจะแนะนำ Hound เป็นตัวช่วยวิเคราะห์รูปแบบโค๊ดของคุณบน Github ถ้ามันเจอโค๊ดของคุณบรรทัดไหนที่ดูแล้วไม่จรรโลงใจ มันจะเห่าใส่เป็นคอมเมนต์บน pull request ของคุณชนิดไม่แคร์สายตาใคร

Hound

Hound นั้นเห่าใส่ได้หลายภาษาไม่ว่าจะเป็น Ruby, JavaScript, CoffeeScript หรือ SCSS ผู้อ่านคนไหนสนใจอยากจับ Hound มาเลี้ยงเล่นๆบน Github ของคุณลองเข้าไปอ่านวิธีการใช้งานได้ครับ https://github.com/houndci/hound

อย่าหักหาญน้ำใจ

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

  • Don't do this again..
  • เมตตาคนอื่นเหอะ อย่าเขียนแบบนี้
  • ไปเขียนมาใหม่ให้มันรู้เรื่อง
  • ไหนหละคอมเมนต์?

ตัวอย่างคอมเมนต์สร้างสรรค์

  • I think it'd better if you...
  • ผมคิดว่า... น่าจะดีกว่าครับ
  • ปัญหานี้น่าจะแก้ด้วยวิธีนี้ (พร้อมแปะลิงค์)

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

Review กี่คนคือดีงาม?

ถ้าทีมเรามีเพื่อนร่วมทีมอยู่50ชีวิต ต้องให้ทั้ง50คนมา review pull request ทุกๆตัวหรือไม่? แค่คิดก็ไม่ต้องทำมาหากินอะไรกันแล้วใช่ไหมครับ กว่าจะ merge โค๊ดได้ต้องรอทุกคนมาร่วมสังฆกรรม แล้วแบบไหนหละถึงจะดี? อันนี้ขึ้นอยู่กับข้อตกลงของทีมคุณครับ ลองมาดูกันดีกว่าว่าเราสามารถเลือกกลยุทธิ์แบบไหนมาใช้ได้บ้าง

  • แบ่งการ review เป็นสองรอบ รอบแรกจำกัดคน review อาจใช้คน 2-5 คนช่วยกันดู เมื่อคนกลุ่มแรกเห็นว่าโค๊ดคือดีงามแล้ว จึงส่งให้เจ้าพ่อเผด็จการที่เราเห็นว่าเก่งที่สุดในทีมคอย review เป็นคนสุดท้าย เมื่อทุกอย่างเรียบร้อยเราก็ merge โลด
  • ใช้คนกลุ่มเดียวปริมาณจำกัดหรือ 2-5 คนมา Review เมื่อทุกคนเห็นพ้องในความอลังการของโค๊ดแล้วจึง merge
  • เลือกคนปริมาณจำกัดหรือ 2-5 คนโดยเป็นส่วนผสมที่มาจากโปรแกรมเมอร์ที่เชี่ยวชาญในโค๊ดฟังก์ชันนั้น อีกส่วนเป็นเด็กใหม่ การ Review ลักษณะนี้เสมือนเป็นการฝึกเด็กใหม่ในทีมให้เข้าใจโค๊ดของทีมไปด้วยในตัว
  • และอื่นๆ

เลือกคนมา Review อย่างจำกัดอย่างไรดี?

ตามที่ได้กล่าวข้างต้น เราควรเลือกคนปริมาณจำกัด ไม่ใช่ยกมา review ทั้งทีม อาจใช้คน 2-5 คนช่วยกันดู คำถามคือไอ้ 2-5 ชีวิตเนี่ย เราควรคัดเลือกอย่างไร?

เพื่อให้แน่ใจว่าโค๊ดใน pull request จะได้รับการการันตีว่าไร้บัค เราควรมีโปรแกรมเมอร์ผู้เชี่ยวชาญในโค๊ดหรือฟังก์ชั่นนั้นเข้ามาช่วยรีวิวด้วย อาจใช่ 1-2 คน ส่วนที่เหลือสามารถสุ่มจากเพื่อนร่วมทีมคนอื่น ทั้งนี้ต้องทำให้มั่นใจว่าทุกๆคนจะได้รับเลือกอย่างสุ่มเท่าๆกัน

ทำไมเราต้องสุ่มคนมา review? ชี้หน้าให้มา review เลยไม่ได้หรอ? คำตอบคือได้ครับ แต่ไม่ควรเพราะคงไม่มีใครอยากนั่งดูแต่ pull request ที่เกี่ยวกับเรื่องเดิมๆ สมมตินายเอไม่ได้รับการเลือกแบบสุ่ม นายเออาจโดนเจ้านายชี้หน้าให้มาดู pull request เกี่ยวกับ Network ตลอดเวลาก็ได้ (ในฐานะที่ดูแต่เรื่องนี้จนชำนาญ)

ให้ Bot ช่วยสุ่มคน

ผู้อ่านคนไหนเคยใช้ Hubot ไหมครับ? Hubot เป็นบอทที่เกิดจากความคิดสร้างสรรค์ของ Github ที่อนุญาติให้เราเขียนสคริปต์อะไรก็ได้สั่งงานให้เจ้าบอทตัวนี้ทำงานสนองต่อเหตุการณ์หนึ่งๆ เราสามารถเขียนสคริปต์สั่ง Hubot ให้เลือกคนในทีมของเรามาซัก2คนในทุกๆครั้งที่มีคนส่ง pull request

Code
1url = require('url')
2querystring = require('querystring')
3_ = require('lodash')
4
5module.exports = (robot) ->
6 reviewers = [
7 # คนในทีม
8 ]
9
10 robot.router.post "/hubot/gh-repo-events", (req, res) ->
11 query = querystring.parse(url.parse(req.url).query)
12 data = req.body
13 eventType = req.headers["x-github-event"]
14
15 switch eventType
16 when 'pull_request' then _handlePullRequestEvent(data)
17 # ...
18 # ...
19
20 res.end ''
21
22 _handlePullRequestEvent = (data) ->
23 switch data.action
24 when 'opened' then _assignReviewer(data)
25
26 _assignReviewer = (data) ->
27 ghMessage = JSON.stringify(body: "@#{reviewers.shift()} You are the first reviewer!, please review. Type :+1: when you are complete.")
28
29 robot.http(data.pull_request._links.comments.href)
30 .header('Authorization', "token #{process.env.HUBOT_GITHUB_ACCESS_TOKEN}")
31 .post(ghMessage)

สคริปต์ข้างบนตัดมาจากส่วนที่เคยทำไว้ พัฒนาด้วยภาษา CoffeeScript ครับ เป็นการเลือกคนในทีมเพียงคนเดียวมาทำการ Review หากท่านใดสนใจการเขียนสคริปต์เพื่อใช้งานกับ Hubot แนะนำให้อ่านจากเอกสารได้ ที่นี่ ครับ

เอกสารอ้างอิง

SmartBear. BEST PRACTICES FOR CODE REVIEW. Retrieved April, 24, 2016, from https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/

Wikipedia. Code Review. Retrieved April, 24, 2016, from https://en.wikipedia.org/wiki/Code_review

Hound. Retrieved April, 24, 2016, from https://houndci.com/

Hubot. Retrieved April, 24, 2016, from https://hubot.github.com/

สารบัญ

สารบัญ

  • เราจะหลีกเลี่ยงมันได้อย่างไร?
  • ทำอะไร ใครจะรู้?
  • ยาวไปไม่อ่าน
  • ไม่ต้องทำเสร็จก็เปิด pull request ได้
  • ต่างคนต่างสไตล์
  • แนะนำ Houndci
  • อย่าหักหาญน้ำใจ
  • Review กี่คนคือดีงาม?
  • เลือกคนมา Review อย่างจำกัดอย่างไรดี?
  • ให้ Bot ช่วยสุ่มคน
  • เอกสารอ้างอิง