คุณเคยให้ใครยืมของบางอย่างแล้วคืนให้คุณในสถานะอื่นหรือไม่?
ฉันเห็นสถานการณ์ที่แน่นอนนี้เกิดขึ้นในที่ทำงานในสัปดาห์นี้ ผู้ใช้รายงานว่าเมธอด SDK ซึ่งเกี่ยวข้องกับลำดับการเรียก API ล้มเหลวหลังจากการเรียกครั้งแรก การตรวจสอบพบว่าไคลเอนต์ HTTP ที่ใช้โดย SDK กำลังแก้ไขวัตถุส่วนหัวที่แชร์ระหว่างการเรียก API อย่างเงียบ ๆ
ซอฟต์แวร์ที่เทียบเท่ากับการยืมรถของเพื่อนคุณแล้วส่งคืนพร้อมฮูดสีอื่นอย่างไม่ตั้งใจ
ติส ติส ติ ส.
วิธีแก้ปัญหา อย่างน้อยก็จนกว่าจุดบกพร่องจะได้รับการแก้ไขในไคลเอนต์ HTTP คือการคัดลอกวัตถุส่วนหัวเพื่อป้องกันก่อนที่จะส่งต่อไปยังไคลเอนต์ แต่เหตุการณ์ทั้งหมดทำให้ฉันคิดถึงความไม่แน่นอนและมารยาทในการใช้รหัส
วิธีที่คุณจัดการกับอินพุตมีความสำคัญ
ความแปรปรวนเป็นคุณสมบัติ ไม่ใช่จุดบกพร่อง
สำหรับไคลเอนต์ HTTP การกลายพันธุ์ของออบเจกต์ส่วนหัวหมายถึงการจัดสรรหน่วยความจำน้อยลงและปรับปรุงประสิทธิภาพ นั่นเป็นประโยชน์อย่างมากของความไม่แน่นอน แต่ก็มีความเสี่ยงสูง
สำหรับผม บรรทัดล่างคือ:
นั่นเป็นข้อผิดพลาดที่ไคลเอนต์ HTTP ทำ
ผลลัพธ์ควรได้รับการบันทึกไว้อย่างชัดเจน รวมถึงผลลัพธ์ ที่สื่อความหมายโดยนัย เช่น ผลข้างเคียง อนุสัญญา ของ Julia ต่อท้าย !
สัญลักษณ์สำหรับชื่อของฟังก์ชันที่เปลี่ยนข้อโต้แย้งของพวกเขาทำให้ฉันเห็นว่าเป็นรูปแบบที่มีประโยชน์สำหรับการเปิดเผยพฤติกรรมนี้อย่างดัง !
แจ้งเตือนผู้ใช้ถึงผลข้างเคียงทุกครั้งที่ใช้ฟังก์ชั่น
มีกรณีที่ดีที่จะทำเพื่อไม่กลายพันธุ์วัตถุแม้ว่า การบังคับใช้การเปลี่ยนแปลงไม่ได้ช่วยแก้อาการปวดหัวได้มากเพราะมัน:
- กำจัดบั๊กและปืนยิงทั้งคลาส
- เพิ่มความมั่นใจในความถูกต้องของโค้ด โดยเฉพาะอย่างยิ่งในสถาปัตยกรรมที่ซับซ้อน ซึ่งความไม่แน่นอนจะเชิญชวนให้ มีการดำเนินการในระยะไกล
- ทำให้การจัดการพร้อมกันง่ายขึ้น
ต่อไปนี้คือแหล่งข้อมูลบางส่วนเกี่ยวกับประโยชน์ของการไม่เปลี่ยนรูป:
เคล็ดลับความปลอดภัยในการเขียนโปรแกรม: เหตุใดคุณจึงควรใช้ออบเจกต์ ที่ไม่เปลี่ยนรูป โดย Charles Kann แสดงให้เห็นว่าข้อผิดพลาดของหน่วยความจำที่เข้าใจยากนั้นแก้ไขได้ง่ายด้วยการไม่เปลี่ยนรูปได้อย่างไร
The Sins of Perl มาเยือน โดย Mark-Jason Dominus กล่าวถึงที่มาของคำว่า “การกระทำในระยะไกล”
การ อ่านเกี่ยวกับความปลอดภัยของเธรด จากหลักสูตรการสร้างซอฟต์แวร์ของ MIT มีภาพรวมที่ดีเกี่ยวกับความไม่เปลี่ยนรูป ความปลอดภัยของเธรด และการทำงานพร้อมกัน
NASA’s The Power Of Ten: Rules For Development Safety Critical Code โดย Gerald Holzmann ให้เหตุผลสำหรับการเขียนโปรแกรมเชิงป้องกัน
คุณควรรักษาความไม่เปลี่ยนรูปในโค้ดอย่างเคร่งครัดหรือไม่นั้นเป็นคำถามที่คุณจะต้องตอบด้วยตัวคุณเองหรือกับทีมของคุณ ไม่ต้องสงสัยเลยว่า สถานการณ์หนึ่งที่ช่วยเปลี่ยนรูปไม่ได้กำลังจัดการกับรหัสที่ไม่น่าเชื่อถือ
กุญแจสำคัญคือการได้รับ การป้องกัน
เหตุการณ์ที่เกิดขึ้นกับไคลเอนต์ HTTP นั้นทันเวลาสำหรับฉันในทางใดทางหนึ่ง
ฉันได้อ่าน Grokking Simplicity โดย Eric Normand หนังสือเล่มนี้จะกล่าวถึงวิธีการใช้การเขียนโปรแกรมเชิงฟังก์ชัน ในทางปฏิบัติ โดยส่วนใหญ่หลีกเลี่ยงทฤษฎีโดยเน้นไปที่แนวคิดเชิงฟังก์ชันที่สามารถปรับปรุงระบบในโลกแห่งความเป็นจริงได้อย่างไร ก่อนหน้านี้ไม่ถึงสองสัปดาห์ ฉันอ่านหัวข้อเกี่ยวกับ การป้องกันการคัดลอก
นี่คือวิธีที่ทีม SDK จัดการกับปัญหากับไคลเอนต์ HTTP:
ให้ฉันอธิบายความหมายของ รหัสที่ไม่น่าเชื่อถือ
คนมองโลกในแง่ร้ายเชื่อว่ารหัสทั้งหมดไม่น่าเชื่อถือ แต่ข้อสันนิษฐานนั้นใช้ไม่ได้จริงเสมอไปในธุรกิจการเขียนโค้ดแบบวันต่อวัน อย่างน้อยที่สุด ฉันคิดว่าโค้ดที่ไม่น่าเชื่อถือคือโค้ดใดๆ ที่:
- แสดงพฤติกรรมที่ไม่ดี เช่น ไคลเอ็นต์ HTTP หรือ
- ไม่สามารถตรวจสอบให้สอดคล้องกับความคาดหวังของคุณ หรือมีค่าใช้จ่ายสูงเกินกว่าจะแก้ไขเพื่อให้เป็นเช่นนั้น ซึ่งมักจะเกิดขึ้นกับรหัสดั้งเดิม
การคัดลอกเชิงป้องกันช่วยในทั้งสองกรณี
นี่คือปัญหาเดียวกันกับที่ทีม SDK เผชิญใน Python:
>>> headers = {"some_key": "some_value"} >>> response = http_client.get( "/endpoint", headers=headers ) >>> headers {'some_key': 'some_value', 'boo!': 'did\'t expect me, did ya?'}
กุญแจ "boo!"
ด้วยค่าตัว "didn't expect me, did ya?"
ปรากฏในพจนานุกรม headers
โดยไม่คาดคิด เมื่อมีการใช้ headers
ในคำขออื่น เนื้อหาที่ไม่คาดคิดจะทำให้คำขอล้มเหลว
หากต้องการใช้การป้องกันการคัดลอกที่นี่ ให้ทำสำเนา headers
ก่อนที่จะส่งต่อไปยัง http_client.get()
:
>>> headers = {"some_key": "some_value"} >>> from copy import deepcopy >>> response = http_client.get( "/endpoint", headers=deepcopy(headers) ) # ^^^^^^^^^^^^^^^^^ >>> # / >>> # A copy of `headers` is sent to the client >>> headers {'some_key': 'some_value'}
เนื้อหาของ headers
ไม่เปลี่ยนแปลงเนื่องจากสำเนาของ headers
ซึ่งเป็นวัตถุที่แตกต่างกันโดยสิ้นเชิงซึ่งสร้างด้วย ฟังก์ชัน deepcopy()
ของ Python ถูกส่งไปยังไคลเอนต์แทน
ตอนนี้คุณสามารถส่งสำเนา headers
ใหม่ไปยังคำขอถัดไปได้อย่างปลอดภัย ลูกค้ายังคงกลายพันธุ์สำเนา แต่วัตถุต้นฉบับได้รับการป้องกัน การปกป้องนั้นมาพร้อมกับราคา: โอเวอร์เฮดหน่วยความจำที่เพิ่มขึ้น แต่ในกรณีนี้และอื่นๆ อีกมากมาย การปรับปรุงด้านความน่าเชื่อถือเป็นการพิสูจน์ให้เห็นถึงการแลกเปลี่ยน
การป้องกันการคัดลอกไม่เพียงแค่ปกป้องวัตถุที่ส่งผ่านจากโค้ดของคุณไปยังโค้ดที่ไม่น่าเชื่อถือเท่านั้น นอกจากนี้ยังป้องกันการเปลี่ยนแปลงของวัตถุที่เปลี่ยนแปลงได้ซึ่งส่งผ่านไปยังโค้ดของคุณจากแหล่งที่ไม่น่าเชื่อถือ
พจนานุกรมส่วนหัว เมื่อมองจากมุมมองของไคลเอนต์ HTTP นั้นมาจากโค้ดที่ใช้ API ของไคลเอ็นต์ ซึ่งเป็นตัวอย่างคลาสสิกของโค้ดที่ไม่น่าเชื่อถือ การทำสำเนาของอาร์กิวเมนต์ที่ส่งไปยังไคลเอนต์ก่อนที่จะทำการกลายพันธุ์นั้นจะช่วยทุกคนได้
อาจเป็นเพียงฉัน แต่ดูเหมือนว่าเป็นสิ่งที่ต้องทำด้วยความระมัดระวัง
สิ่งที่ต้องอ่านต่อไป
เรียนรู้วิธีจดจำและลบผลลัพธ์โดยปริยาย เช่น ออบเจ็กต์ส่วนหัวที่กลายพันธุ์ในตัวอย่างสัปดาห์นี้ในโค้ดของคุณเอง:
เจาะลึก
เรียนรู้เพิ่มเติมเกี่ยวกับการคัดลอกเชิงป้องกันและกลยุทธ์อื่น ๆ ในการบังคับใช้การไม่เปลี่ยนรูปในหนังสือ Grokking Simplicity ของ Eric Normand
เข้าถึงได้ทันทีจาก Manning * หรือซื้อฉบับพิมพ์จาก Amazon *
* ลิงค์พันธมิตร ดู การเปิดเผยพันธมิตร ของฉันสำหรับข้อมูลเพิ่มเติม