การกำหนดเวอร์ชันแบบ Semantic เมื่อแก้ไขข้อบกพร่องที่สำคัญ


18

ฉันกำลังจัดการห้องสมุดซึ่งมีจำนวนมากของการใช้งานของประชาชนและผมมีคำถามเกี่ยวกับความหมายเวอร์ชัน ฉันต้องการ refactor หนึ่งส่วนที่สำคัญพอสมควรของห้องสมุดซึ่งถูกนำไปใช้อย่างไม่ถูกต้อง - และถูกนำไปใช้อย่างไม่ถูกต้อง แต่การทำเช่นนี้จะหมายถึงการเปลี่ยนแปลง API สาธารณะซึ่งเป็นการตัดสินใจที่สำคัญ

การเปลี่ยนแปลงที่ฉันต้องการทำให้หมุนรอบวิธีใช้ตัววนซ้ำ ขณะนี้ผู้ใช้ต้องทำสิ่งนี้:

while ($element = $iterator->next()) {
   // ...
}

ซึ่งไม่ถูกต้องอย่างน้อยใน PHP พื้นเมืองอินเตอร์เฟซ Iterator ฉันต้องการแทนที่ด้วยสิ่งนี้:

while ($iterator->valid()) {
   $element = $iterator->current();
   // ...
   $iterator->next();
}

ซึ่งคล้ายกับ:

foreach ($iterator as $element) {
    // ...
}

หากคุณดูที่คู่มือของ Tom เกี่ยวกับการกำหนดเวอร์ชันแบบ semantic เขาระบุไว้อย่างชัดเจนว่าการเปลี่ยนแปลงใด ๆ กับ API สาธารณะ (เช่นสิ่งที่ไม่เข้ากันได้ย้อนหลัง) ควรแสดงให้เห็นถึงการเปิดตัวครั้งใหญ่ ดังนั้นห้องสมุดจะกระโดดจาก 1.7.3 เป็น 2.0.0 ซึ่งสำหรับฉันมันเป็นขั้นตอนที่ไกลเกินไป เรากำลังพูดถึงคุณสมบัติเพียงอย่างเดียวที่ถูกแก้ไข

ฉันจะมีแผนการที่จะปล่อยในที่สุด 2.0.0 แต่ผมคิดว่านี้คือเมื่อคุณสมบูรณ์เขียนห้องสมุดและดำเนินการต่าง ๆ นานาการเปลี่ยนแปลงประชาชน API การแนะนำของการเปลี่ยนโครงสร้างนี้รับประกันการออกรุ่นใหญ่หรือไม่? ฉันไม่สามารถเห็นได้ว่ามันเป็นอย่างไร - ฉันรู้สึกสะดวกสบายมากขึ้นในการปล่อยเป็น 1.8.0 หรือ 1.7.4 ใครบ้างมีคำแนะนำบ้าง?


อะไรทำให้คุณไม่สามารถทำงานร่วมกับระบบย้อนหลังได้
mouviciel

ในขณะนี้next()วิธีการนี้ใช้เพื่อดึงองค์ประกอบปัจจุบันและย้ายตัวชี้ภายในไปข้างหน้า ซึ่งเป็นสิ่งที่ผิด next()ควรเลื่อนตัวชี้และcurrent()ใช้เพื่อเรียกคืน ...
hohner

6
ดังนั้นในเวอร์ชั่นใหม่ที่ผู้คนไม่ควรสนใจเกี่ยวกับค่าตอบแทนnext()เพียงอย่างเดียวนั่นคือการย้ายตัวชี้สิ่งนี้ไม่ได้ทำลายความเข้ากันได้จริงๆ
ratchet freak

คำตอบ:


29

คุณลังเลเพราะคุณไม่ต้องการสร้างเวอร์ชันเชิงความหมายคุณต้องการสร้าง "โฆษณารองรับเวอร์ชัน" คุณคาดหวังว่าหมายเลขรุ่น "2.0" จะบอกให้โลกรู้ว่าคุณมีคุณสมบัติเจ๋ง ๆ มากมายในห้องสมุดของคุณตอนนี้ไม่ใช่ว่าคุณจะเปลี่ยน API ไม่เป็นไร (บริษัท ซอฟต์แวร์และ / หรือผู้พัฒนาจำนวนมากทำเช่นนั้น) IMHO คุณมีตัวเลือกต่อไปนี้:

  • ติดรุ่น semantic และอยู่กับความจริงที่ว่าคุณต้องเปลี่ยนหมายเลขรุ่นเป็น 2.0.0
  • เปลี่ยนรูปแบบการกำหนดเวอร์ชันเป็นตัวเลข 4 ตัว "1.1.7.3" เป็นเวอร์ชั่นของคุณตอนนี้ "1.2.0.0" อันถัดไปหลังจากเปลี่ยน API และ "2.0.0.0" เป็นเวอร์ชั่นแรกของ "ตระกูลผลิตภัณฑ์ 2.x ใหม่ทั้งหมด"
  • ทำให้การแก้ไขของคุณเข้ากันได้ย้อนหลัง (ดังนั้นอย่าเปลี่ยนการทำงานของnextเพียงเพิ่มvalidและcurrentฟังก์ชั่น) จากนั้นคุณสามารถใช้ "1.8.0" เป็นหมายเลขเวอร์ชั่นถัดไป หากคุณคิดว่าการเปลี่ยนพฤติกรรมของnextมันมีความสำคัญจริงๆให้ทำใน 2.0.0

เท่าที่ตัวเลือกสุดท้ายจะเป็นทางออกที่สมบูรณ์แบบ: คุณไม่สามารถขอnext()ให้ทำสิ่งที่มันทำต่อไปได้ ในการใช้งานฟังก์ชั่นอย่างถูกต้องมันจะต้องทำบางสิ่งที่แตกต่าง ดังนั้นถ้าฉันทำให้มันเข้ากันได้ย้อนหลัง - ฟังก์ชั่นใหม่ / การแก้ไขจะผิดและทำลายจุดทั้งหมดของการเปลี่ยนแปลง
hohner

2
คำแนะนำที่กว้างขึ้นที่คุณทำในสัญลักษณ์แสดงหัวข้อย่อยที่สามของคุณ (ทำให้การแก้ไขย้อนกลับเข้ากันได้) เป็นสิ่งที่ดีที่จะต้องพิจารณา มันอาจไม่ทำงานในกรณีนี้โดยเฉพาะ แต่เทคนิคโดยรวมถือว่าคุ้มค่า ฟังก์ชันนี้ซับซ้อนมากขึ้น แต่อาจเป็นเส้นทางที่ใช้การได้

ขอบคุณทุกคน: ถ้าฉันสามารถยอมรับสองฉันจะ ฉันลงเอยด้วยการแฮ็คnext()วิธีใหม่เพื่อทำหน้าที่ใหม่ทั้งหมดรวมถึงสิ่งที่จำเป็นเพื่อให้ใช้งานร่วมกันได้ย้อนหลัง รู้สึกแย่มากที่ต้องเปลี่ยนฟังก์ชั่นใหม่เช่นนี้ แต่เฮ้โฮ
hohner

10
@hohner: ตอนนี้เป็นเวลาที่จะบันทึกพฤติกรรมเก่าที่เลิกใช้แล้วดังนั้นคุณสามารถลบออกได้ใน 2.0.0
Jan Fabry

7

ติดกับคู่มือของ Tom เพื่อกำหนดเวอร์ชันเชิงความหมาย

การเปลี่ยนแปลงที่สำคัญใด ๆ กับ API สาธารณะจะต้องทำที่จุดใดจุดหนึ่งในสอง:

  1. ไม่เคย
  2. ในการอัพเดทครั้งใหญ่

การลงคะแนนของฉันเป็นวิธีแรก แต่ฉันยอมรับว่ามันเหมาะสมสำหรับเรื่องขี้ประติ๋วเท่านั้น

ปัญหาคือการรักษาความเข้ากันได้ย้อนหลังและตรวจสอบให้แน่ใจว่าคุณไม่ได้แยกสิ่งต่าง ๆ สำหรับผู้ใช้ก่อนหน้าของ API ของคุณ

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

  1. กำหนดรหัสการแก้ไขเพื่อใช้วิธีการใหม่
  2. ตรวจสอบการแก้ไขและตรวจสอบให้แน่ใจว่าไม่ได้ทำอะไรเสียหาย
  3. จัดส่งผลิตภัณฑ์รุ่นใหม่ให้กับผู้ใช้

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

สำหรับสิ่งเล็ก ๆ น้อย ๆ นี้ฉันจะปล่อยวางและไม่ใส่ใจกับมัน
ถ้ามันรบกวนคุณจริงๆ (ซึ่งเห็นได้ชัดว่ามันทำหรือคุณไม่ได้ถาม) ฉันจะทำสิ่งต่อไปนี้

  1. สร้างสาขา v2.0.0 ในทรีโค้ดของคุณ
  2. ทำสิ่งแรกที่สนับสนุน v2.0.0 branch ซึ่งเป็นการเปลี่ยนแปลงนี้
  3. ส่งตัวอย่างRelease Notesก่อนเวลาออกอากาศที่การเปลี่ยนแปลงกำลังจะมาถึง

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


ทางเลือกอื่นคือการเพิ่มฟังก์ชั่นใหม่ที่ทำงานในแบบที่คุณต้องการ

หากคุณมีfoo()แล้วคุณจะสร้างfooCorrect()เพื่อให้การแก้ไข แต่ยังรักษาความเข้ากันได้ย้อนหลังอย่างเต็มที่ และในบางจุดคุณสามารถเลิกใช้foo()เพื่อให้คนอื่นรู้ว่าจะไม่ใช้มัน

ความท้าทายที่มีคือการที่คุณจะได้พบกับสิ่งอื่นภายในfooCorrect()ซึ่งจำเป็นต้องปรับปรุงมันและคุณจะจบลงด้วยfooCorrectedCorrect()หรือบางเรื่องไร้สาระโง่อื่น ๆ

หากคุณต้องการแก้ไขปัญหานี้ในตอนนี้วิธีการทางเลือกนี้น่าจะเป็นเส้นทางที่ดีที่สุด ระวังและระมัดระวังในการสร้างฟังก์ชั่นพิเศษมากมายด้วยวิธีนี้เพราะจะทำให้ API ทำงานได้ยากขึ้น และการรับรู้นั้นอาจเพียงพอที่จะป้องกันปัญหาที่เลวร้ายที่สุดประเภทนี้

แต่นี่อาจเป็นวิธีที่ "เลวน้อยที่สุด" เพื่อพิจารณาสิ่งเล็ก ๆ


ฉันเห็นด้วยกับคุณ. ปัญหาที่ฉันพบคือฉันต้องการเขียนไลบรารีสำหรับ v2.0.0 ใหม่ทั้งหมด (เนื่องจากมีปัญหาเหล่านี้จำนวนมากที่ต้องแก้ไข) ดังนั้นฉันไม่ต้องการการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ในฐานะผู้วนซ้ำเพื่อสร้างพื้นฐานของการเปลี่ยนแปลงครั้งใหญ่นี้ ดังนั้นตัวเลือกของฉันคือ: ละเว้นข้อผิดพลาดนี้หรือแก้ไขข้อผิดพลาดและใส่ลงในรุ่นหลักใหม่
hohner

@hohner - คำตอบที่ได้รับการอัปเดตเพื่อมอบแนวทางใหม่ในการสร้างฟังก์ชั่นใหม่ โปรดระลึกไว้ว่าฟังก์ชั่นใหม่ที่มีชื่อคล้ายกันจำนวนมากนั้นเกือบจะแย่พอ ๆ กับการเปลี่ยน API เอง

3
@hohner: ผิดอย่างสม่ำเสมอ> ไม่สอดคล้องกันในกรณีนี้ พฤติกรรมยังคงทำงาน แต่มันก็ไม่ได้เป็นสำนวน พิจารณาว่าหากคุณทำการเปลี่ยนแปลงนี้คุณกำลังทำลายรหัสลูกค้า การทำเช่นนี้โดยไม่มีการเตือนจะไม่ได้รับการชื่นชม
Phoshi

@ GlenH7 ในกรณีนี้การใช้วิธีที่มีชื่ออื่นจะไม่ทำงาน ตัวแปลดั้งเดิมของ PHP อาศัยวิธีการเหล่านี้ (เช่นnext()ไม่ใช่nextCorrect()) ฉันจะดูว่าฉันสามารถแก้ไขต่อไป () ดังนั้นจึงเข้ากันได้ย้อนหลังและทำงานเมื่อใช้Iteratorอินเตอร์เฟซ
hohner

1
@Phoshi คุณเห็น - ฉันเห็นด้วยอย่างสมบูรณ์ในขณะนี้ ตอนนี้ได้เวลาลองโค้ดที่เป็นไปไม่ได้: D
hohner
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.