Release planning

Chokchai Phatharamalai
odds.team
Published in
4 min readApr 11, 2022
Photo by M Burke on Unsplash

ผมทำ web application ตัวหนึ่งที่มีฟีเจอร์ในการสร้างใบแจ้งหนี้และใบเสร็จให้ลูกค้าซึ่งพอผมพัฒนาฟีเจอร์สำคัญ ๆ เสร็จ ก็ใช้งานมันเรื่อยมา หลังจากนั้นไม่ได้ upgrade version ของ library อะไรอีก เพราะหลังจากเสร็จแล้วก็ไม่ได้จะไปพัฒนาอะไรเพิ่มเติม ตัวมันก็ใช้งานได้ดีอยู่ มีแค่ปรับตรงนั้นนิดตรงนี้หน่อย เล็ก ๆ น้อย ๆ เท่านั้น แต่พอผ่านมา 2 ปีเทคโนโลยีที่ผมใช้มันก็เก่ามาก

จริง ๆ แล้วตอน application นี้เกิดมา มันเป็น VueJS version 2 ซึ่งตอนนี้มี Vue 3 ออกมาใหม่แล้ว แต่ผมไม่ได้อัพเกรดเป็น Vue 3 นะ ผมแค่จะอัพเกรดจาก VueJS เป็น version 2 ที่ใช้ library ล่าสุดที่มันเข้ากันได้เฉย ๆ เพราะตัวปัจจุบันมันไม่สามารถ build บน CI system ที่ใช้ OS linux ตัวใหม่ ๆ ได้แล้ว จะขึ้น version ใหม่ทีต้อง build ด้วยมือเท่านั้น ซึ่งก็มี human error ตามมาต้องคอยตามแก้เรื่อย ๆ

พอผมจะลองอัพเกรดให้เป็น library version ล่าสุดด้วย npm ธรรมดา ปรากฏว่ามันก็พังกระจุยกระจายจนผมไม่สามารถ start แอพพลิเคชั่นขึ้นมาใหม่ได้ พอผมลองอัพเดท library อยู่หลายครั้งแต่ไม่สำเร็จ เลยตัดใจแล้วคิดจะสร้างแอพพลิเคชั่นใหม่ด้วย library version ล่าสุด แล้วค่อย ๆ ย้ายของจากแอพพลิเคชั่นเก่าเข้ามาแทน

ถ้าจะทำวิธีนี้ ไม่เกิน 2 วันก็น่าจะเสร็จ แต่พอเสร็จแล้วปรากฏว่าผมไม่กล้า deploy ขึ้น production ซะงั้น ผมเลยตัดสินใจโทรไปปรึกษาเพื่อนร่วมทีมที่ชื่อเจน

แนะนำตัวละครซักนิด เจนเป็นเพื่อนร่วมทีมที่เขียนแอพพลิเคชั่นนี้มาด้วยกัน 2 คน เรียกว่าเราสองคนเป็นเหมือน developer ในทีมที่พัฒนาระบบนี้ละกัน แต่ในชีวิตจริงเจนเป็น product owner ส่วนผมเป็น Scrum master

ซึ่งเราสองคนตัดสินใจทำระบบนี้ขึ้นมาด้วยเจตนา 4 ประการดังนี้

  • ใช้แอพพลิเคชั่นนี้เป็นเหมือนต้นแบบในการสอนทีมเรื่อง engineering practice ต่าง ๆ พวกการเขียนโค้ด เขียน automate tests และการ design เราสองคนตั้งใจทำแอพพลิเคชั่นนี้มาก ทุก ๆ design decision ทุก ๆ practice เรียกว่าเราทำสุดฝีมือ เพื่อใช้โค้ดในนี้เป็นตัวอย่างเวลาจะโค้ชทีมต่าง ๆ ที่เราเจอในชีวิตจริง
  • eat your own dog food — เราอยากจะใช้โอกาสนี้สัมผัสประสบการณ์การทำตาม practice ที่เราสอนทีมและ product owner ทั้งหลายที่เราโค้ช ถ้าเราทำตามไม่ไหว เราจะมีหน้าไปสอนผู้คนต่าง ๆ ได้ยังไง
  • เราอยากใช้โค้ดนี้เป็นตัวอย่างในการทำ static code analysis ด้วย sonarcloud เพื่อจะได้มีตัวอย่างเปิดให้คนที่เราโค้ชดูได้ว่าเครื่องมือนี้ช่วยบอกอะไรเราได้บ้าง ทำไม product owner ถึงต้องรู้จักกับ technical debt แล้ว สนับสนุนทีมให้ไม่สร้างมันขึ้นมาได้อย่างไร
  • อยากจะสร้างใบแจ้งหนี้และใบกำกับภาษีบนมือถือได้นี่แหละ เพื่อเพิ่มความคล่องตัวในการทำงานส่วนนี้ในบริษัทเรา

ผมสารภาพกับเจนตรง ๆ ถ้าทำวิธีนี้แล้วผมไม่กล้า release เพราะมันเป็นการยกเครื่องทั้งระบบใหม่ ถึง major version ของ framework จะเป็น version 2 เหมือนเดิมก็เถอะ

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

หลังจากเจนได้ฟังความกังวลผม เจนก็ถามผมว่า

เจน: ถ้ามันล่มไปจริง ๆ มันน่าจะใช้เวลานานแค่ไหนเพื่อจะเอามันกลับมา?

ผม: ถ้าโชคร้าย อาจจะเป็น 2 สัปดาห์

เจนถามต่อว่า: ผลกระทบทาง buisness คืออะไร?

ผม: ต้องประกาศให้ผู้ใช้งานทุกคน (มี 2–3 คน) ให้ fallback business process กลับไปออกใบแจ้งหนี้ด้วย word file ธรรมดา

พอฟังได้ฟังดังนี้ เจนก็บอกว่า เราทำ automate test สุดเบอร์ มีทั้ง automate unit และ acceptance tests ถ้าเรายังไม่กล้า deploy แล้วเราจะมีหน้าไปโค้ชลูกค้าเราให้เค้า deploy ของเวอร์ชั่นใหม่ได้อย่างไร?

พอกลืน dog food คำนั้นลงไปผมก็กล้าหาญชาญชัยขึ้น แล้วก็ถามเจนว่าเราจะลบโค้ด version เก่าเลยไหม?

เจนบอกว่าไม่

ยอม duplicate ไปทำที่ใหม่

เราจะ copy ของไปทำ folder ใหม่ เผื่อไม่เหลือทางถอยอะไรจริง ๆ กลับไป build ที่ folder เก่าด้วยมือก็ยัง rollback กลับมาได้ เราจะยอมให้มี technical debt จาก duplication นี้เหลืออยู่จนกว่า version ใหม่จะเสถียรแล้วเราค่อยลบมัน

หลังจากทำใจได้แล้ว เราก็ยกเครื่องใหม่ ทุกอย่างผ่านไปได้ด้วยดี automate tests ผ่านหมด ระบบ version ใหม่ก็เกิดขึ้น ผมลองทำ manual test ดูเองด้วย feature สำคัญ ๆ ที่ใช้บ่อย ๆ ทำงานได้ครบถ้วนดีไม่มีปัญหา

หลังจากนั้นผมก็สบายใจ มีเพิ่มฟีเจอร์เล็ก ๆ น้อย ๆ เพื่อให้ navigation สะดวกขึ้นแล้วออก minor release ไปอีก 2 เวอร์ชั่นด้วยโดยที่ผมกับเจนไม่ได้รู้ตัวเลยว่าตอนนี้ระบบมีปัญหายิ่งใหญ่ที่ทำให้ไม่สามารถออกใบแจ้งหนี้ได้อีกต่อไปแล้ว

วันออกใบแจ้งหนี้จริง

ผมเก็บ folder เก่าไว้ ตั้งใจว่าจะลบเมื่อได้ออกใบแจ้งหนี้จริงบนระบบเวอร์ชั่นใหม่ สองสัปดาห์ผ่านไป วันที่ต้องออกใบแจ้งหนี้จริง ๆ ก็มาถึง ผมก็ใช้งานฟีเจอร์นี้อย่างคล่องแคล่วเหมือนที่เคยทดสอบมาและมีอยู่ใน automate test script ด้วย

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

ผมนั่งตัวเย็นซักพัก พอได้สติก็นั่งทบทวนว่าผมรู้อะไรบ้าง

  • ยังมี folder เก่า ที่เตรียมเอาไว้ build มือเพื่อถอยได้อยู่
  • มี docker image ที่ push เก็บไว้ใน registry ถ้า rollback จากตรงนั้นได้น่าจะเร็วกว่าและมีความเสี่ยงน้อยกว่า
  • การ rollback กลับไปเวอร์ชั่นที่แล้ว ผมเสียแค่ความสะดวกในการใช้งานเล็ก ๆ น้อย ๆ จาก nagivation ตัวใหม่ที่ทำมาหายไป
  • ผมน่าจะมีเวลาลอง rollback มันไม่เกิน 2 ชั่วโมง ถ้ายังไม่ได้ จะต้องถอยกลับไปสร้างใบแจ้งหนี้ด้วยมือก่อน เพราะไม่งั้นเดี๋ยวจะกระทบ cash flow

คิดได้แบบนี้ก็ตัดสินใจลอง rollback ดูก่อน

ไม่สามารถ rollback docker image จาก registry ได้

เพราะไม่ได้ติด tag ที่ image เอาไว้ในทุก release พอไม่มี tag ติดไว้ จะไปคุ้ย image registry ก็ได้แต่เลข commit แต่ไม่มีเลข tag

ถึงตรงนี้ทำให้นึกได้ว่าตอนไปโค้ชหลาย ๆ ทีมที่เค้ายังไม่ได้ทำ continuous delivery ไปจนถึง production แค่มี pipeline deploy ไปบน test environment ที่ใช้ทำ sprint review เฉย ๆ สิทธิ์ในการเข้าถึง production นั้นอยู่นอกทีม ทำให้ definition of done ไม่รวมการ deploy production ด้วย ตอนที่ทีมจะขึ้น production ทีนึง ก็มักจะมี requirement จากทีม operation มาขอให้แก้ build pipeline ให้ติด tag image ให้ด้วย ไม่งั้นเค้า rollback ลำบาก ผมมาเข้าใจสิ่งที่ทีม operation ขออย่างลึกซึ้งตอนนี้นี่เอง

แต่ต่อให้ติด tag ที่ image ไว้ ก็ต้องเอาเลข tag เก่ามาอัพเดทใน file docker-compose อยู่ดี เพราะเราอยากเก็บ history ของการ rollback นี้ไว้ด้วย

โชคดีที่ product ตัวนี้มี private repository ที่เอาไว้จัดการ docker-compose file และพวก config ต่าง ๆ ที่เอาไว้ ผมเลยกลับไปดูได้ว่าตอน deploy เวอร์ชั่นเก่า (ที่อยู่ใน folder backup) นั้นมันคือ commit อะไรใน config project นี้ แล้วผมก็ย้อน master กลับไปที่ commit เก่าในอดีต แล้ว force push เลย (-___-”)

หลังจากนั้น 5 นาที ผมก็ได้ระบบเวอร์ชั่นเก่ากลับมา logo กลับมาเป็นของบริษัทเหมือนเดิม พร้อมกับ navigation อันใหม่ที่หายไป

เราไม่ rollback ตอนทำ continuous delivery

ผมเข้าใจคำว่า always roll forward มากขึ้นวันนี้แหละ การที่ผมกลับไปหาเลข commit เก่าในอดีต แล้ว push ขึ้นมาให้ระบบมัน deploy อีกครั้งจาก production configuration อันใหม่ (แม้ว่าจะเป็นการเอาของเดิมมาทับก็ตาม) เรียกว่า roll forward เพราะผมไม่ได้ไปดึง image ที่เคย build เก็บไว้ใน registry มา deploy

พอเราจะทำ continuous delivery ไปถึง production นั่นหมายถึง พอสมาชิกในทีมคนนึง push code ขึ้นไปผู้ใช้งานจะเห็นความเปลี่ยนแปลงจาก commit นั้นบน production ได้ทันทีเพราะ build system จะไป deploy เวอร์ชั่นใหม่ของระบบทันทีที่ automate test ทั้งหมดผ่าน

นั่นแปลว่า ถ้าเราจะติดเลข tag ให้กับ image เลข tag จะเยอะแยะเท่ากับเลข commit เลย สรุปว่าเลข commit คือเลข tag นั่นแหละ แถมยัง automate ยากด้วย เพราะหลายทีมที่ผมโค้ช push code กันวันนึงเป็นร้อยครั้ง

สรุปการทำ continuous delivery เราจะไม่มานั่น track version กันแล้ว เราจะ always roll forward คือไม่มีการเตรียมการเผื่อจะต้อง rollback นั่นเอง

ที่ทำแบบนี้ได้ เพราะเรามั่นใจใน automate test ที่เราทำไว้ว่ามันครอบคลุม critical case ทั้งหลายที่ impact business หนัก ๆ หมดแล้ว ถ้า test ทั้งหมดนี้ผ่าน ก็วางใจให้เอา version นั้นขึ้น production ได้ เราไม่กังวลว่าถ้า build อีกรอบผลลัพธ์จะไม่เหมือนเดิม เพราะถ้า test ทั้งหมดนี้ผ่าน ต่อให้ผลของการ build อีกรอบจะต่างไป เราเชื่อว่าส่วนต่างนั้นไม่สำคัญ

ประโยชน์ของการทำ continuous delivery คือทำให้ทีมได้ feedback ที่เร็วจากผู้ใช้งานจริง ๆ ผลของสิ่งที่ทีมทำจะสะท้อนใน report ต่าง ๆ ของ product นั้นเลยว่าผู้ใช้งานใช้มันง่ายขึ้นไหม หรือว่ายอดขายสูงขึ้นไหม

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

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

release ของที่มี feature เท่าเดิม

การ release ของเพื่อยกเครื่องใหม่ครั้งนี้ เนื่องจากมันมี feature เท่าเดิม ทำให้ risk ที่จะเกิดขึ้นเป็น technical risk ล้วน ๆ แล้วถ้าเราต้องถอยให้กลับไปเป็นเวอร์ชั่นเก่า เราตัดสินใจทำได้ทันที

แค่ผมลองจินตนาการว่าผมออกแบบ release นี้ให้มีการยกเครื่องใหม่ และมีฟีเจอร์สำคัญ ๆ ที่ต้องใช้งานต่อด้วย ตอนที่มันพังขึ้นมาผมจะไม่มีทางให้ถอยไปเวอร์ชั่นเก่าได้เลย เพราะถ้าผมถอยแล้ว feature ใหม่ที่สำคัญนั้นหายไป แล้วผู้ใช้งานจะทำงานต่อกันอย่างไร? ผมต้องสู้ศึกสองด้านคือทั้ง technical risk และ business risk ใน release เดียวกัน

โชคดีที่ยังไม่ได้ลบ folder เก่าไป

ถ้าไม่ใช่เพราะเจนแนะนำให้ยอมเก็บ folder เก่านั้นไว้เพื่อให้มีทางถอย ตอนผมถอย configuration กลับไปเป็นเหมือนเดิมที่ใช้งาน folder เก่า มันจะ build ไม่ผ่าน เพราะผมต้องถอย code ตามด้วย โชคดีที่ตัดสินใจจะลบมันหลังจากได้ออกใบแจ้งหนี้จริง ๆ แล้ว 1 ใบ

จริง ๆ แล้วถ้าว่ากันจากมุมมองของโค้ช การเหลือ technical debt ไว้เป็นสิ่งที่น่าเป็นห่วง เพราะบ่อยครั้งมันมักจะ “ถูกปล่อยให้เป็นแบบนั้นชั่วคราว” ตลอดไป แต่จากมุมมองของผู้ใช้งานระบบ (โชคดีที่ครั้งนี้ผมก็เป็นหนึ่งในผู้ใช้งานนั้น) ผมไม่อยากถอยกลับไปออกใบแจ้งหนี้ด้วย Word แบบเดิม แล้วเผชิญกับ human error อีกมากมายและความเสี่ยงที่จะกระทบ cash flow เลยอยากได้ทางถอยเก็บไว้เผื่อเหตุไม่คาดฝันเช่นกัน

ขอบคุณกับประสบการณ์ความกลัวตอนต้อง release ของ

ตอนผมโค้ชทีม บ่อยครั้งผมเห็นทีมวุ่นวายกับการ test ระบบมากมายหลายเวอร์ชั่นก่อน release ใหญ่ ๆ ที่สำคัญ ๆ ต้อง test ทั้งกรณีเปิดใช้งานฟีเจอร์นี้ ปิดใช้งานฟีเจอร์นั้น และบ่อยครั้งเช่นกันที่ได้ยินทีมบ่นว่าเหนื่อย จะกังวลอะไรกันมากมาย ที่ผ่านมาผมได้แต่ให้กำลังใจ แต่ไม่มีคำพูดอะไรจะไปปลอบประโลมพวกเค้าที่กำลังเร่งเทสกันหัวฟูได้เลย

วันนี้ผมพบว่าคำถามที่เจนถามผมวันนั้น

  1. ถ้าเกิดเหตุไม่คาดฝันขึ้นแล้วระบบเสียหายไปจริง ๆ ทีมมั่นใจว่าจะเอาระบบกลับมาได้ภายในเวลาเท่าไหร่?
  2. จากระยะเวลาที่ทีมตอบมา ผลกระทบทางธุรกิจคิดเป็นความเสียหายเท่าไหร่?

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

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

เก็บเกี่ยวการเรียนรู้

การอัพเกรดเวอร์ชั่นของ dependency library บ่อย ๆ จะทำให้ cost ในการอัพเกรดมันถูกกว่ามาก เพราะ cost การทำเหล่านี้จะเพิ่มขึ้นเท่าทวีทุก ๆ วันที่เปลี่ยนแปลงไป ถ้าผมหมั่นอัพเดท library ด้วย npm update อยู่เรื่อย ๆ ตอนปรับ feature เล็ก ๆ น้อย ๆ ก็คงไม่เจอทางตันแบบวันนี้

ผมมีบทเรียนเรื่อง technical debt ไปแบ่งปันกับ product owner อีกเรื่องละ

อีกเรื่องที่เราได้เรียนรู้จากกฎที่เราสอนทีมเสมอ ๆ ก็คือ ถ้ามี defect บน production อย่าซ่อมมันเฉย ๆ แต่ต้องกลับมาทำ automate test เพื่อให้มั่นใจว่าเราจะไม่ล้มที่เดิมอีก ผลจากวิกฤตครั้งนี้เราเลยเห็นความสำคัญของการทำ automate test เพื่อตรวจสอบว่า logo ที่ใช้นั้นเป็นของจริง ตอนนี้คิดไว้คร่าว ๆ ว่าจะใช้ Cypress Visual Testing เพื่อเปรียบเทียบรูป logo ที่คาดหวังครับ ไว้ได้เรียนรู้อะไรใหม่ ๆ จะมาเล่าให้ฟังต่อนะครับ

Credits

ขอบคุณเจนสำหรับคำถามให้ผมคิดตอนผมไม่กล้า release คุณนี่เป็นสุดยอด product owner จริง ๆ

บทความที่เกี่ยวข้อง

อ้างอิง

--

--