10 ขั้นตอนการทำ Workflow ร่วมกับ Github ตั้งแต่เริ่มต้น

Chai Phonbopit
20Scoops CNX
Published in
8 min readApr 6, 2018
รูปภาพไม่เกี่ยวกับบทความ :) Credit : https://unsplash.com/photos/5eBW5GomfhY

วันนี้ผมจะมาแชร์ Process การทำงานของเราที่ 20Scoops CNX กัน ซึ่งวันนี้จะมาโฟกัส ในส่วนที่เป็น Git Workflow กับ Github ละกัน ซึ่ง Process นอกเหนือจากนี้ เช่น การแบ่ง Task แบ่ง Sprint งาน ไม่พูดถึงนะครับ จะข้ามไปตอนที่เราได้รับมอบหมายงานเลย ว่าจะมี Process ในการจบ Task/Feature ที่เราได้รับยังไงบ้าง

Git Workflow Lifecycle

  1. Branch หลักในการทำงานคือ dev ทุกๆ Feature ที่ทำต้องแตกออกมาจาก Branch นี้เท่านั้น master สำหรับ Production และ staging สำหรับ Test ก่อน ขึ้น Production
  2. เมื่อได้รับ Task/Feature หรือสร้าง Task ย่อยขึ้นมาเองก็ได้จาก User Story ที่กำหนด
  3. แตก branch ออกมาจาก dev จะเป็นชื่อ feature/xxx, chore/xxx, issues/xxx, bug/xxx
  4. ทำการ Implement Code, ทำการของเราไป เมื่อเสร็จแล้ว ก็ Push ขึ้น Github ปกติ หลังจากนั้นทำการเปิด Pull Request และ Assign Reviewers เพื่อทำการ Review Code

5. เมื่อ Review และ Approved แล้ว ก็ทำการ Merge branch ที่เราทำมา เป็นอันจบ และก็วน loop เริ่มต้นทำงานแบบนี้ไปเรื่อยๆ

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

1. ทำการ Protected Branches

เริ่มแรกสมมติว่าเริ่มต้นสร้างโปรเจ็คขึ้นมา เลยละกัน ทำการ Setup อะไรเรียบร้อยแล้ว ก็ทำการสร้าง repository บน Github จากนั้นก็ทำการ Add Remote ซะ

เริ่มต้นสร้าง repo กันเลย
git remote add origin GIT_URL
git add .
git commit -m "Initial Project"
git push origin master

เมื่อเราได้ Repository บน Github แล้ว ขั้นตอนต่อมาคือ Setting -> Branches -> Protected branches เพื่อทำการ Protect Branch ไม่ให้ Push, Delete หรือ Merge Branch ที่เรากำหนดไว้ได้

เช่น Branch dev, staging และ master จะถูก Protected ไว้เลย ว่าไม่สามารถ push, delete, หรือ merge โดยตรงได้ ทำได้ก็เพียง Merge ผ่าน Web UI ของ Github เท่านั้น

master, dev และ staging จะถูก Protected Branch ไว้

เงื่อนไขในการทำ Protected Branch มีอะไรบ้าง?

  • ไม่สามารถ Force Push หรือ Delete Branch ได้
  • ต้องผ่าน Status Check ก่อน ถึง Merge ได้ เช่น Code ยังไม่ Up to Date หรือมี Conflict ก็จะ Merge ไม่ได้
ต้องผ่าน Status Check ก่อน คือ Up to Date และ Testing ผ่าน
  • ต้อง Approved จาก Reviewers 2 คนขึ้นไป ถึงจะ Merge ได้
สามารถกำหนดจำนวนขั้นต่ำของ Reviewers ได้
  • กำหนดสิทธิ์ว่าใครสามารถที่จะ Merge Branch ได้บ้าง (Merge ผ่าน Web UI ของ Github)

2. Setup CI/CD

ขั้นตอนต่อมาทำการ Setup CI/CD เพื่อให้มันรัน Test ทุกๆครั้งที่ Push Code ซึ่ง Testing ของ CI ก็เป็นหนึ่งในเงื่อนไขของการ Protected Branch ในขั้นตอนแรก ถ้าเกิดว่าเรา Test ไม่ผ่าน ก็จะไม่สามารถทำการ Merged ได้เลย

Service ที่เราเลือกใช้คือ Semaphore CI ซึ่งมันรองรับ Docker based ที่ในทีมของเราใช้อยู่ ก็เลยคิดว่าง่าย สะดวก รวมถึงความเร็วในการ Build, Run Test นั้นเร็วกว่าที่อื่นๆที่เราเคยใช้ ไม่ว่าจะเป็น Codeship, CircleCI หรือ Travis

สำหรับ ตัวอย่างโปรเจ็ค เราหยิบยกโปรเจ็ค Node.js API ขึ้นมา ซึ่งเป็น Service หนึ่งที่เราใช้ Docker Setup

ซึ่ง Script ในการรันก็ไม่มีอะไรมาก

  • ทำการติดตั้ง Dependencies ด้วย yarn install หรือ yarn
  • รัน yarn lint เพื่อเช็ค Code ด้วย ESLint ว่าผ่านหรือไม่
  • Start Service ด้วย Docker จากนั้นก็รัน yarn test
  • ถ้า Test ผ่านหมด ก็ OK คุณได้ไปต่อ 🎉

ต่อมา เราทำการ Setup Server ของ Semaphore CI เพื่อให้มัน Deploy Server ซึ่งเลือกได้ทั้ง Heroku, AWS หรือการเขียน Custom Script ขึ้นมาเอง (เลือกได้ว่าจะให้มัน Auto Deploy หรือเรากำหนดเอง) กรณีที่ Branch ที่เราต้องการรัน Test ผ่าน เช่น

  • เมื่อ Branch dev ผ่าน ก็ให้ทำการ Deploy ไป Development Server
  • เมื่อ Branch staging ผ่าน ก็ให้ทำการ Deploy ไป Staging
  • และเมื่อ master ผ่าน ก็ให้ Deploy Production เลย (ซึ่งโปรเจ็คเรายังไม่เสร็จเลยยังไม่มี :))
หน้าจอ Status แสดงผลเทส ผ่าน หรือไม่ผ่าน รวมถึง Deploy Server ด้วย

3. แตก Branch ตาม Feature/Task

ขั้นตอนต่อมา คือ เมื่อเราได้รับมอบหมายงานแล้ว สเตปถัดมาคือ แตก branch ตาม feature ที่เราได้รับมา เช่น

แยก branch name ให้ชัดเจน
  • feature/xxxx : เมื่อเราต้องการจะทำงานตาม Feature ที่เราได้รับ เช่น User Login, Edit User, Add Member Button เป็นต้น
  • chore/xxx : เมื่อเราต้องการจะเริ่มทำ Task ที่มันหยิบย่อย ต่างๆ ไม่ได้อยู่ใน User Story หรือ Planning
  • task/xxx : เมื่อเราต้องการแตก Task แยกย่อยออกมาจาก User Story หรือจริงๆ สามารถใช้แบบเดียวกับ chore/xxx ได้ ซึ่งข้อนี้เป็น (Optional)
  • bug/xxx หรือ issue/xxx: เมื่อเราได้รับ Bug หรือ Issue จาก Tester
  • hotfix/xxx : เมื่อเราได้รับ Bug หรือ Issue ที่เกิดตอน Production แล้ว ซึ่ง Branch นี้จะแตกต่างจากขั้นอื่น คือจะ Hotfix จาก Branch master เนื่องจากโค๊ดของเราที่ขึ้น Production คือ master และเมื่อทำการ Hotfix เรียบร้อยแล้วก็ต้อง Merge กลับมาทั้ง maste และ dev ผ่านการ Pull Request เหมือนปกติ
  • release/v1.x.x : สำหรับ release version ขึ้น Production

ขึ้นตอนต่อมาคือ ทุกๆ Commit เราจะทำการกำหนด Commit Message ประมาณนี้

  • [#ISSUE_ID] COMMIT MESSAGE : มี Issue/Task ที่ Reference ไปที่ Project Management ของเรา หรือจะเป็น Github Issues เป็นต้น
  • ตัว Commit Body ควรสรุปว่า commit นั้นๆทำอะไรไปบ้าง เช่น
[#1001] Update User Information
- Add new button
- Change API response message

ซึ่งจริงๆแล้ว Commit เราให้เป็น Guideline ซะมากกว่า ไม่ได้กำหนดเป็นตายตัวว่าจะต้องประมาณนี้

เมื่อจบงานที่ได้รับมอบหมายแล้ว ก็ Push Code ขึ้นไปได้เลย

git add .
git commit -m "[#100] Finish blah blah"
git push origin feature/xxx

4. เปิด Pull Request (PR)

ขั้นตอนต่อมา ทำการเปิด Pull Request ขึ้นมาซะ โดยถ้าเรามีการ Push Code โดย Branch ใหม่ขึ้นมา ตัว Github จะมี Popup ขึ้นมาให้เรากด Compare & pull request ได้เลย ซึ่งก็เหมือนกับการกดแท็ป Pull requests -> New pull request

Github จะขึ้น auto มาให้เราเลย กรณีเรา push branch ใหม่ขึ้นมา

ซึ่งมันจะ Compare กับ Branch หลักเราที่เราตั้งไว้ กรณีนี้คือตั้ง branch default เป็น dev แต่ถ้าวิธีปกติคือ ต้องเลือก Compare base และ branch ที่เราเพิ่ง commit มา

กรณีกดเลือก New pull request เอง

เมื่อทำการเปิด Pull Request เรียบร้อยแล้ว ขั้นตอนต่อมา ตั้ง Title และเขียน Description ให้ชัดเจน หรือจะมีการแนบรูป Screenshot เพื่อบอกว่าทำอะไรไป เพื่อให้คนอื่นเข้ามา Review จะได้รู้ว่า Pull Request นี้ทำอะไรบ้าง

ตัวอย่าง เช่น

ตัวอย่าง Pull Request
ตัวอย่าง Pull Request

สุดท้าย ทำการเลือก Reviewers เพื่อให้คนในทีมทำการ Review และ Approved โค๊ดของเรานั่นเอง

5. Code Review

ขั้นตอนต่อมา มาถึงขั้นตอน Code Review ซึ่งส่วนนี้จะเป็นหน้าที่ของในทีมคนอื่น ที่ไม่ใช่เจ้าของ Branch ซึ่งคนที่จะทำหน้าที่นี้ก็คือ Reviewers ที่ถูกเลือกจากขั้นตอนที่แล้ว

จุดประสงค์หลักๆของการทำ Code Review คือ

  • หาข้อผิดพลาดของโค๊ดที่เราทำการ commit ขึ้นมา หากว่ามีข้อผิดพลาด ก็ตีกลับเป็น Request Changed ได้ เพื่อให้แก้โค๊ดมาใหม่
  • พูดคุย แลกเปลี่ยน แสดงความคิดเห็น หรือแม้แต่แนะนำวิธีที่ดีกว่า หากว่า Reviewers เห็นว่าโค๊ดบางส่วนยังไม่โอเค

เพื่อแชร์ Knowledge ระหว่างคนในทีม ไม่ว่าจะเป็น Junior หรือ Senior ทำให้โค๊ดไม่ได้เป็นแค่ของคนใดคนหนึ่ง แต่ะเป็นของทุกคนในทีม

ข้อกำหนดในการทำในการ Code Review

  • ไม่ควรใช้เวลา Review นานเกิน โดยปกติแล้ว ควรจะ Review ต่อ Pull Request 10–30 นาที ขึ้นอยู่กับ Line of Code ซึ่งการ Review ของคนอื่นก็ถือเป็นส่วนหนึ่งในหน้าที่ของคนในทีมด้วย
  • ห้ามบังคับ ต่อว่า หรือออกคำสั่ง เช่น ทำอันนี้ เขียนแบบนี้ทำไม เขียนกากมาก เป็นต้น แต่ให้ใช้การแนะนำหรือเชิงออกความเห็นมากกว่า เช่น ทำไมทำแบบนี้ เราว่าทำแบบนี้โอเคกว่า หรือ เราคิดว่าแบบนี้โอเคกว่า เห็นด้วยมั้ย?
  • หลีกเลี่ยงพวก Code Standard หรือ Lint ถ้าหากว่าไม่จำเป็น พยายามให้การ Format Code ตาม ESLint ไปอยู่ที่ CI ซะ พยายามใช้เวลาตรงส่วนนี้ให้น้อยที่สุด เช่น Space, Tab วงเล็บ, Empty Line เป็นต้น อาจจะมีพวก Naming Convention บ้าง ที่ต้องแก้ เช่น camelCase หรือตัวแปรไม่เหมาะสม หรือชื่อไม่สื่อ
  • Feedback นั้นสำคัญ พยายามมี Feedback กลับไปในทุกๆ Pull Request ไม่ว่าจะเป็นโค๊ดที่ดีหรือโค๊ดที่ไม่ดี แน่นอนโค๊ดที่ไม่ดี เราก็ต้องมีการ comment พร้อมข้อเสนอแนะ หรือข้อคิดเห็นของเราอยู่แล้ว หรือแม้แต่โค๊ดที่ดี ก็ออกความคิดเห็นได้

6. Rebasing

Step ต่อมา ขั้นตอนนี้อาจจะไม่จำเป็นในบางสถานการณ์เช่นการ Commit และ Merge ปกติ แต่การ rebase จะเกิดกรณีที่โค๊ด Remote (Origin หรือ Server) กับ Branch ที่เราทำเพื่อไป Merge มีโค๊ดที่ไม่ up to date กัน เราจำเป็นต้อง Rebase branch เราเพื่อไปต่อหัวของ dev จะไม่ใช้การ Merge เพราะเราอยากให้ git เป็นเส้นเดียวกัน สวยงาม :)

วิธีการทำคือ

  • Checkout dev บน local จากนั้น pull โค๊ดล่าสุดมา
  • ทำการ checkout branch ที่เรากำลัง dev
  • แล้ว rebase จาก branch dev มาที่ branch ของเรา
  • ทำการ push ไป remote (ต้อง force push เพราะมีการ change history จาก rebase)

ซึ่งการทำด้านบน เมื่ออยู่ในรูป Command Line คือ:

# Update branch local with remote
git checkout dev
git pull origin dev
# หรือ git pull origin dev --rebase
# Rebase dev
git checkout feature/xxx
git rebase dev
# หากมี Conflict ให้ทำการแก้แล้วใช้
git rebase --continue # เพื่อ rebase step ถัดไป
git rebase --skip # เพื่อข้าม rebase step ปัจจุบัน
# Force push ไป remote
git push -f origin feature/xxx

ข้อควรระวังสำหรับ Rebase

  • เราจะใช้ Force Push กับ Branch ที่เราทำเท่านั้น จะไม่ทำใน Branch คนอื่น
  • แน่นอน การ Force Push จะทำให้ History มันเปลี่ยน แต่ในเมื่อเรารับผิดชอบงานใน Branch ของเราแล้ว และมีการ Pull Request แน่นอน โค๊ดที่เปลี่ยน ก็เฉพาะ Branch นั้นๆ ไม่กระทบ branch หลักอยู่แล้ว
  • หากเราทำการ Rebase โค๊ดที่เก่ามากๆ หรือมี Commit history ที่เยอะ การจะ rebase อาจจะใช้เวลานาน หรือมี Conflict อาจจะต้องใช้ Commit Squashing เข้ามารวม commit ได้เหมือนกัน

หลายๆคนอาจจะสงสัยว่าทำไมใช้ Rebase ไม่ใช้ Merge ละ จริงๆ ก็แล้วแต่ Team แล้วแต่องค์กรว่าจะใช้รูปแบบไหน แต่ที่เราใช้ Rebase จริงๆก็ได้แรงบันดาลใจ หรือดู Workflow จากหลายๆ Repo ไม่ว่าจะเป็น

หรือ Open Source Project ต่างๆอีกเพียบ ที่ใช้ท่า Rebase และก็มีบาง Article ที่บอกว่าอย่าใช้ Rebase นะ

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

7. Resolve Conflict

ขั้นตอนนี้มันเกี่ยวเนื่องกับ ขั้นตอนการ Rebase ซึ่งปกติแล้ว ไม่ว่าจะเป็นการ Rebase หรือ Merge หากว่าโค๊ดทั้งสอง branch มันไม่สามารถ auto merge ได้ ยังไงก็ต้องมีการ Conflict และทำการ Resolve Conflict ซะก่อน ก่อนที่จะ Push Code ใหม่ไปได้

ซึ่งรูปแบบการ Conflict จะเป็น <<<<<<<, =======, >>>>>>> ดังเช่น

Hello World,
<<<<<<< HEAD
I'm Chai!
=======
What's your name?
>>>>>>> feature/xxx
  • <<<<<<< HEAD : จนถึง ======= เป็นการบอกให้รู้ว่าเป็นโค๊ดของ Base Branch
  • ======= จนถึง >>>>>>> xxxx : เป็นโค๊ดของ Branch xxx

ซึ่งการแก้ Conflict ทำได้คือ จะเลือกเอาระหว่าง Base Branch หรือโค๊ดของ Branch เรา

อีกวิธีนึงคือ เลือก Code ทั้งสอง Branch แต่เจาะจงเอาแต่ละบรรทัดไปเลย (ซึ่งตัวอย่าง มันแค่บรรทัดเดียว อาจจะไม่เห็นผล)

ซึ่งแน่นอน หลายๆ Text Editor หรือว่า IDE ก็มี Built in Git เพื่อให้เราได้เลือก Merge ง่ายกว่าการดูจาก Command Line

ตัวอย่างการ Resolve conflict ด้วย WebStorm
ตัวอย่างการแก้ Conflict บน VS Code

8. Merge branch to dev

ขั้นตอนนี้เป็นขั้นตอนสำคัญ ซึ่งเราได้กำหนด Protected Branch ไว้แล้วว่า Branchdev นั้นใครที่สามารถกด Merge pull request ได้ ซึ่งไม่นับรวม Administrator ของ Team

โดยปกติแล้วเราจะให้สิทธิ์ 1–2 คน ในโปรเจ็คนั้นๆที่มีสิทธิ์ในการ Merge ได้

ซึ่งเงื่อนไขในการ Merge pull request คือ

  • ผ่านการ Approved จาก Reviewers 2 คนขึ้นไป (บางโปรเจ็คอาจจะคนเดียว หรือ 3 แล้วแต่โปรเจ็ค)
  • รันเทสผ่านทั้งหมด
  • โค๊ดไม่มี Conflicts กับ Base branch รวมถึง โค๊ด Up to date (ไม่มี ahead by x commits หรือ x commits behind)
หาก Rebase, Code up to date, Test passed, Review approved เรียบร้อย ก็ Merge pull request ได้แล้ว

9. Deploy to Server

ขั้นตอนนี้จริงๆเป็น Process ที่ถูกรันผ่าน Semaphore CI อยู่แล้ว ตัวคนที่ทำ Feature นั้นๆ หรือ Reviewers จะไม่ได้เกี่ยวข้องกับ Process ขั้นตอนนี้แล้ว

ซึ่งการ Deploy to Server เราตั้งไว้ เมื่อ Branch dev รัน Test ผ่านทั้งหมด ก็จะไป Trigger Script ให้ทำการ Deploy Server (ในโปรเจ็คของเราเป็น Custom Script ทำการ Build React และ Deploy ไปที่ AWS)

กำหนด ให้ dev มัน auto deploy
Deploy Server ที่เราทำการ Setup ไว้ 2 Environment คือ Dev และ Staging
ตัวอย่าง เมื่อ merge dev และรันเทสผ่าน มันก็ auto deploy เลย

ขั้นตอนการ Deploy Server ตัว CI อาจจะมีความล่าช้านิดนึง ขึ้นอยู่กับโปรเจ็ค โดยตัวโปรเจ็คที่ทำ ก็คือ React ก็จะมีการ Build bundle file, ก็อปปี้ Static files ไปที่ Hosting หรือไป build บน Hosting ก็แล้ว จากนั้นก็ Restart ตัว Server พร้อม nginx

10. Release Version

ขั้นตอนนี้ สืบเนื่องมาจาก branch dev กรณีที่เราจะทำการ Release version ต่างๆ ขึ้น Production เช่น v1.0.0 เราจะทำการ แตก branch โดยใช้ชื่อ release/v1.0.0 และ ทำการ Commit และ Push Code ขึ้นไป รวมถึงเปิด Pull Request เหมือนขั้นตอนปกติทุกอย่างเลย เพียงแค่ ตอนที่เราเลือก Compare Pull Request เราจะ Base ที่ Branch production แทนที่จะเป็น dev

พร้อมทั้งติด Tag ด้วย

git tag v1.0.0
git push --tags

ซึ่งเวลาเรา Push tag ลงไปที่ Github แล้ว ตัว Github จะทำ Release ให้เราเลย

Release จาก tag version ของเรา + เขียน Changelog เพิ่ม

ขั้นตอนก็เหมือนการ Review โค๊ด รอการ Approved และเทสผ่านทั้งหมด ก็ได้เวลา Merge Production เป็นอันเรียบร้อย

Conclusion

สรุปที่กล่าวมาทั้งหมดก็เป็นแนวทาง Process ส่วนนึงที่ทาง 20Scoops CNX ของเรา ใช้ในการทำงาน ซึ่งต้องบอกเลยว่า ช่วง Code Review หรือหลายๆขั้นตอน อาจจะมีความล่าช้า หรือเสียเวลาไปบ้าง แต่ก็แลกมาด้วย Code Quality และทุกๆคนในทีมได้แลกเปลี่ยน Knowledge ได้ด้วย

สุดท้ายหวังว่า Process ของเราที่ใช้กันอยู่จะเป็นแนวทางหรือเป็นประโยชน์ให้กับหลายๆทีมได้บ้างไม่มากก็น้อย 🙏

และใครที่รู้สึกว่าอยากมาลองกับ Process แบบนี้ อยากสัมผัสบรรยากาศ Workflow แบบนี้ที่เราใช้กัน สมัครเข้ามาได้นะครับ

--

--