Semantic Versioning

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

version

อย่าง atom ก็บอกไว้ว่า version 1.3.2 คนทั่วๆไปก็คงไม่ได้สนใจว่า เลขเหล่านี้คืออะไร เข้าใจแค่เพียงว่า ตัวเลขเยอะขึ้นก็คือ version ใหม่กว่าเดิม แต่ถ้าคุณเป็น Developer เรื่องของ version จะกลายเป็นสิ่งสำคัญมาก เพราะเมื่อเขียนโปรแกรมไปสักพัก เราอาจจะต้องใช้ Library จากข้างนอก ที่เราไม่ได้เป็นเขียนขึ้นมาเอง เมื่อเลือก Library หรือ API แล้ว แน่นอนว่าไลบารีต่างๆที่เราเลือกใช้ ก็มักมีการ update เป็นเวอร์ชันใหม่ ซึ่งโดยปกติเราก็จะเปลี่ยนไปใช้เวอร์ชั่นใหม่ถูกไหม เพราะ library อาจจะมีการแก้ bug เพิ่ม feature อะไรก็ว่ากันไป  …. แต่หลายๆครั้งเราก็ต้องพบว่า อ้าวนี่เราต้องแก้ไขโค้ดใหม่ เพราะ API , Library เหล่านั้น ได้เปลี่ยนแปลง  บางฟังชั่นได้ยกเลิกไป หรือฟังชั่นที่เคยใช้งานใช้งานประจำ ต้องการพารามิเตอร์เพิ่มเติม อะไรแบบนี้  คำถามคือ เราจะรู้ได้ยังไงว่า ไลบารี่ที่เค้า update ใหม่เนี่ย โค้ดของเราจะยังใช้งานได้ โดยไม่ต้องแก้ไขอะไร ?

โอเค หลายคนอาจจะบอกว่า โปรเจคเล็กๆ แก้โค้ดของเราให้เป็น API ตัวใหม่ก็จบละ แต่ถ้าเป็นโปรเจคใหญ่ๆ มีโค้ดซับซ้อน การไปไล่แก้คำสั่งต่างๆย่อมปวดหัวแน่นอน และในมุมกลับกัน ถ้าหากเราเป็นคนที่เขียน library , api  เหล่านั้นเสียเองละ

โอเค .. ในวันแรกที่ปล่อย library ให้คนอื่นใช้งาน ก็ตั้งเป็น 1.0 ใช่ไหม ? ต่อมาพบว่ามันมีบั๊ก แก้ไขโค้ดเสร็จ ต้องการจะปล่อย lib ตัวใหม่ ก็ต้องปรับ เลข version กันสักหน่อย แต่ก็ไม่รู้จะตั้งเป็นเลขอะไรระหว่าง 1.1 หรือเปลี่ยนเป็น 2.0 ไปเลย เราจะมีหลักการยังไงในการตั้งเลข version ?

หรือเคยสังเกตกันไหมว่า บางทีเราใช้ library ที่โหลดมาจากเน็ท เค้าจะมีเลขบอกไว้ เช่น LibA 4.0 ต่อมาเมื่อมีการปรับปรุงแก้ไข bug ก็เปลี่ยนเลขเป็น 4.2 แล้วเคยคิดไหมครับว่า ทำไมมันไม่เป็น 5.0 หรือ 4.1 ละ ?

เมื่อหลายๆปีก่อน ผมเองก็เป็นคนหนึ่งที่เขียนเลข version แบบมั่วๆ โดยไม่มีหลักการอะไรเหมือนกัน อยากได้อะไรก็ตั้งเอาเอง เช่น 1.2.0 beta อะไรแบบนี้ เห็นมันเท่ดี แม้ว่าการเขียนเลข version นั้นไม่ได้มีแนวทางตายตัว ว่าต้องเขียนแบบนี้ถึงจะถูกต้อง อย่างไรก็ตามมันมีหลักการที่เป็นที่นิยมใช้อยู่ ซึ่งหนึ่งในนั้นคือ Semantic Version

เลข Semantic Version จะมีความสำคัญอย่างมาก สำหรับ Developer เพราะมันจะช่วยให้เราเลือก Version ของ Library ได้ถูกต้อง และถ้าหากเราเขียน Library ให้คนอื่นเอาไปใช้ยิ่งต้องเขียนเลข version ให้ดี ซึ่งอย่างกรณี JKNotificationPanel ที่เป็น cocoapod library ของผมเอง นั้นก็ตั้งเลข version ตามแบบ semantic นี้เหมือนกัน

วันนี้ก็เลยจะขอพูดถึง Semantic Version และหลักการของมัน อย่างคร่าวๆเพื่อให้เราสามารถเขียนเลข version ให้มีความหมาย มากกว่า แค่บอกว่า library , api ที่เราใช้อยู่นั้น ใหม่ หรือ เก่า

Semantic Version

การเขียน semantic version นั้นมักจะใช้ในการเขียน Library และ API ต่างๆ ซึ่งเลข version ที่เป็น semantic version นั้นจะประกอบไปด้วยเลข 3 ส่วนคือ  x.y.z ( ไม่มี z ก็ได้ )

โดยที่เลขแต่ละตัวนั้นหมายถึง

  • X คือ Major  Version
  • Y คือ Minor  Version
  • และสุดท้าย Z คือ Patch Version

แต่ก่อนที่จะไปทำความเข้าใจการเขียนเลข เหล่านี้จะขออธิบายเพิ่มเติมเกี่ยวกับศัพท์ที่จะยินอยู่บ่อยๆสักหน่อย นั่นคือ backward compatibility

คำว่า Backward compatibility คือ การทำงานเข้ากันได้กับ API เดิม เช่น สมมติว่า โปรเจคเราใช้ LibA 1.0 ซึ่งมีฟังก์ชั่น Sum( a, b ) ที่รับค่า integer สองตัวคือ a และ b และในเวลาต่อมา LibA ได้ออกเวอร์ชั่นใหม่เป็น 1.2  และปรับปรุงให้ฟังก์ชั่น Sum นั้นสามารถรับค่า double ได้

ถ้าโค้ดของเราที่เรียกฟังก์ชัน Sum ยังทำงานได้ โดยที่ไม่ต้องเปลี่ยนแปลงอะไร นั่นคือไม่ต้องเปลี่ยนค่า a,b เป็น double สามารถใช้ integer ต่อไปได้ นั่นหมายถึงว่า libA ตัวใหม่นี้รองรับ backward compatibility

เอาละ เมื่อเข้าใจคำศัพท์แล้ว ต่อไปก็มาดูหลักการกันเลย

อันที่จริงการตั้งตัวเลข semantic นี้มีหลักการ ที่เขียนไว้ใน http://semver.org/ อยู่แล้ว ไปอ่านกันได้ แต่โดยสรุปอย่างย่อแล้ว มันมีหลักการง่ายๆคือ

  • เลข x.y.z นี้จะต้องไม่เป็นค่าติดลบ คือ จะไม่เขียน -1.0.0 อะไรแบบนี้
  • ค่าจะต้องไม่เป็น 0 คือ เขียนเลข 0.0.0 ไม่ได้
  • ค่าจะต้องเพิ่มขึ้นเสมอ เช่นตอนนี้ใช้ 1.4 เมื่อเปลี่ยน version ก็ต้องมีค่ามากขึ้น จะเขียน 1.3 ไม่ได้
  • ถ้าเลข Major Version มีค่าเป็น 0 นั่นหมายถึงว่า เป็น version ที่กำลังเริ่มต้นพัฒนา API ต่างๆอาจจะเปลี่ยนแปลงได้เสมอ
  • การเปลี่ยนเลข Patch Version จะใช้กับการแก้ไข bug ที่ทำให้การทำงานของ API, Function ของ library นั้นไม่ถูกต้อง เช่นฟังชั่น Sum ส่งค่าลบเลข มาแทน ผลการบวกเลข ในกรณีนี้จะต้องทำการเปลี่ยนเลข Patch  เช่นจาก 1.0.0 เป็น 1.0.1 เป็นต้น
  • Minor Version จะเปลี่ยนก็ต่อเมื่อ มีการเพิ่ม Feature , API ใหม่ๆ แต่ยังคงรองรับ api เก่า (backward compatibility) และถ้าหากเปลี่ยนเลข Minor Version แล้ว เลข Patch จะต้องเปลี่ยนเป็น 0
    เช่น 1.1.3 ก็เปลี่ยนเป็น 1.2.0
  • Major Version จะเปลี่ยน เมื่อ มีการเพิ่ม Feature หรือ  API แต่ไม่รองรับ backward compatibility และเมื่อเปลี่ยนเลข Major ใหม่ ตัวเลข Minor , Patch จะต้องเปลี่ยนเป็น 0
    เช่น 1.1.3 ถ้าหากเปลี่ยนเป็น 2.0.0 หมายถึงว่า lib นี้ได้เพิ่ม feature และไม่รองรับ backward นั่นเอง
  • ในกรณีที่เป็น pre-release ( ยังไม่สมบูรณ์ แต่ออกมาให้ลองใช้ก่อน ) อาจจะเขียน ตัวหนังสือ หรือตัวเลขต่อท้ายได้ เช่น 1.0.0-alpha , 1.0.0-beta.2 และแน่นอนว่าเวอร์ชั่นที่ไม่มี prelease ต่อท้ายคือเวอร์ชั่นที่ใหม่กว่า เช่น (เก่าสุด) 1.0.0-alpha  < 1.0.0-beta < 1.0.0 < 1.0.1 (ใหม่ล่าสุด)

ทีนี้พอจะเข้าใจแล้วใช่ไหมครับว่า เลข version นั้นมีความสำคัญยังไง และเราก็ควรจะใช้ semantic version นี้ให้เป็นประโยชน์

เอาละครับก่อนจากกัน ลองตอบตัวเองกันสักหน่อยว่า

Swift 2.0 กับ Swift 2.2 นั้นหมายถึงอะไร

เชื่อว่าหลังจากอ่านบทความนี้จบ คุณก็ตอบได้ใช่ไหมละครับว่า 2.2 การเพิ่ม feature ใหม่ๆ และรองรับ backward compatibility คือพูดง่ายๆว่า ถ้าหากเราเขียนโค้ดด้วย Swift 2.0 มันจะยังทำงานกับ Swift 2.2 ได้ โดยที่ไม่ต้องเปลี่ยนแปลงอะไร แต่จะถูกแจ้งว่า โค้ดบางส่วน กำลังจจะ deprecate ( ยกเลิก ) ในอนาคต นั่นเอง

Leave a Reply