การตรวจสอบที่ขอบ API คือจุดที่คำถามทางเทคนิคกลายเป็นคำถามเชิงการออกแบบ เพราะการตัดสินใจว่าอะไรควรตรวจที่ฝั่งผู้ใช้และอะไรควรตรวจที่ฝั่งบริการ ส่งผลโดยตรงต่อความเร็วของหน้าจอ ภาระของเซิร์ฟเวอร์ และความสามารถของทีมสนับสนุนในการอธิบายว่าทำไมข้อมูลหนึ่งจึงถูกปฏิเสธ บทความนี้จะแยกให้เห็นว่าการตรวจสองแบบนี้ให้คำตอบต่างกันอย่างไร และเหตุใดผลลัพธ์ที่ไม่ตรงกันจึงไม่ใช่ข้อบกพร่อง
ควรตรวจสอบที่ฝั่งไคลเอนต์หรือฝั่งเซิร์ฟเวอร์?
คำตอบที่ตรงที่สุดคือทั้งสองฝั่ง แต่ทำคนละหน้าที่ ฝั่งผู้ใช้มีหน้าที่ให้ผลตอบกลับทันทีเพื่อให้การกรอกข้อมูลลื่นไหล จึงเหมาะกับการตรวจสิ่งที่คำนวณได้เร็วและไม่ต้องพึ่งข้อมูลภายนอก เช่นความยาว ชุดอักขระ และการจัดรูปแบบ
ฝั่งบริการมีหน้าที่ตัดสินอย่างเป็นทางการ เพราะเป็นฝ่ายเดียวที่เชื่อถือได้ ทั้งยังเข้าถึงกฎทางธุรกิจและฐานข้อมูลอ้างอิงได้ การตรวจที่ฝั่งนี้จึงเป็นด่านสุดท้ายก่อนข้อมูลจะถูกบันทึก
การตรวจที่ฝั่งผู้ใช้ไม่ควรถูกมองว่าเป็นการซ้ำซ้อน แต่ควรถูกมองว่าเป็นการลดจำนวนคำขอที่ต้องเดินทางไปกลับ หากปิดการตรวจฝั่งผู้ใช้ทั้งหมด ระบบจะตอบสนองช้าลงและสร้างภาระที่ไม่จำเป็น
ในทางกลับกัน การพึ่งการตรวจฝั่งผู้ใช้เพียงอย่างเดียวเป็นความเสี่ยงด้านความปลอดภัย เพราะผู้ใช้สามารถข้ามการตรวจนั้นได้เสมอ จึงไม่ควรใช้เป็นด่านตัดสินขั้นสุดท้าย
ที่ขอบระบบควรตรวจอะไร และปล่อยอะไรผ่าน
ความต่างที่สำคัญคือการตรวจที่คำนวณในเครื่อง กับการตรวจที่ต้องสอบถามออกไปนอกระบบ การตรวจในเครื่องได้แก่ความยาว ชุดอักขระ และการคำนวณหลักตรวจสอบตามสูตรที่ระบบรู้จัก การตรวจแบบนี้ให้ผลทันที ทำงานได้แม้ไม่มีการเชื่อมต่อ และให้ผลเหมือนเดิมทุกครั้งเมื่อข้อมูลนำเข้าเหมือนเดิม
- ตรวจในเครื่อง ได้แก่ความยาว ชุดอักขระ และการคำนวณหลักตรวจสอบตามสูตรที่ระบบรู้จัก
- ข้อดีของการตรวจในเครื่องคือให้ผลทันที ทำงานได้แม้ไม่มีการเชื่อมต่อ และให้ผลเหมือนเดิมทุกครั้ง
- การสอบถามภายนอก ได้แก่การยืนยันว่าหมายเลขมีอยู่จริงหรือมีสถานะอย่างไรในทะเบียนของผู้ออก
- ข้อจำกัดของการสอบถามภายนอกคือใช้เวลา อาจไม่ตอบ และมีจำนวนครั้งที่สอบถามได้จำกัด
การสอบถามภายนอกได้แก่การยืนยันว่าหมายเลขนั้นมีอยู่จริง หรือมีสถานะอย่างไรในทะเบียนของผู้ออก การตรวจแบบนี้ใช้เวลา ไม่สามารถรับประกันผลลัพธ์ได้เสมอ และมีข้อจำกัดเรื่องจำนวนครั้งที่สอบถามได้
การแยกสองแบบนี้ออกจากกันช่วยให้ระบบตัดสินใจได้ว่าควรตอบผู้ใช้ทันที หรือควรรอโดยแจ้งสถานะให้ทราบ หากนำสองแบบมารวมเป็นเส้นทางเดียว ผู้ใช้จะรอโดยไม่รู้สาเหตุเมื่อระบบภายนอกตอบช้า
ผลลัพธ์ที่ไม่ตรงกันระหว่างสองฝั่งเป็นเรื่องปกติ
กรณีที่ฝั่งผู้ใช้บอกว่าผ่านแต่ฝั่งบริการบอกว่าไม่ผ่านเกิดขึ้นได้ตามปกติ สาเหตุที่พบบ่อยคือทั้งสองฝั่งใช้กฎต่างเวอร์ชัน หรือฝั่งผู้ใช้ไม่ได้ตรวจชั้นที่ต้องอาศัยข้อมูลจากฐานข้อมูล
กรณีตรงข้ามก็เกิดได้เช่นกัน เมื่อกฎฝั่งผู้ใช้เข้มงวดกว่าที่เซิร์ฟเวอร์ต้องการ ทำให้ผู้ใช้ถูกสกัดกั้นโดยไม่จำเป็น
การจัดการที่ถูกต้องไม่ใช่การพยายามบังคับให้สองฝั่งตรงกันเสมอ แต่คือการออกแบบให้ฝั่งบริการมีอำนาจตัดสินสูงสุด และให้ฝั่งผู้ใช้ยอมรับผลจากฝั่งบริการเมื่อไม่ตรงกัน โดยแสดงข้อความที่อธิบายได้ว่ากฎข้อใดเป็นตัวตัดสิน
อีกสิ่งที่ช่วยได้คือการทำให้เวอร์ชันของกฎทั้งสองฝั่งตรวจสอบได้จากภายนอก เมื่อเกิดกรณีไม่ตรงกัน ทีมจึงวินิจฉัยได้เร็วว่าเกิดจากความคลาดเคลื่อนของเวอร์ชันหรือจากความผิดพลาดของข้อมูลจริง ๆ
ทำไมคำว่าตรวจไม่ผ่านจึงใช้แทนคำว่าไม่มีหมายเลขนี้ไม่ได้?
เพราะสองคำนี้ตอบคำถามคนละข้อ การตรวจไม่ผ่านบอกว่าข้อมูลที่ได้รับไม่สอดคล้องกับกฎที่ระบบมี ซึ่งเป็นข้อความเกี่ยวกับข้อมูลที่ส่งเข้ามา ส่วนการไม่มีหมายเลขนี้บอกว่าหมายเลขนั้นไม่มีอยู่ในทะเบียน ซึ่งเป็นข้อความเกี่ยวกับโลกความจริง
ระบบคำนวณเพียงอย่างเดียวไม่สามารถสรุปข้อความแบบหลังได้ เพราะการไม่มีอยู่จริงต้องยืนยันจากทะเบียนต้นทาง เมื่อระบบนำสองเรื่องนี้มาใช้แทนกัน ผู้ใช้จะได้รับข้อความที่รุนแรงเกินกว่าหลักฐาน และมักนำไปสู่ความเข้าใจผิดว่าข้อมูลของตนเป็นของปลอม
สิ่งที่ควรทำคือใช้ถ้อยคำที่บอกขอบเขตของสิ่งที่ตรวจได้ เช่นรูปแบบไม่สอดคล้อง หลักตรวจสอบไม่ผ่าน หรือยังไม่พบข้อมูลนี้ในทะเบียนอ้างอิง ซึ่งเป็นประโยคที่ผู้ใช้และเจ้าหน้าที่ตีความตรงกัน
การสอบถามภายนอกและข้อจำกัดด้านเวลา
เมื่อการตรวจต้องออกไปสอบถามภายนอก ควรถือว่าความล้มเหลวเป็นกรณีปกติ ไม่ใช่ข้อยกเว้น เพราะระบบภายนอกอาจตอบช้า ปฏิเสธคำขอ หรือไม่ตอบเลยก็ได้
ด้วยเหตุนี้จึงควรกำหนดเวลารอสูงสุดสำหรับการสอบถามไว้เสมอ ถ้าเกินเวลาก็ควรบอกผู้ใช้ว่ายังยืนยันไม่ได้ ให้ทำอย่างอื่นไปก่อน แล้วค่อยกลับมาตรวจใหม่ แทนการค้างรอไว้เฉย ๆ
อีกสิ่งที่ควรมีคือทางเลือกสำรองเมื่อระบบภายนอกใช้งานไม่ได้ และการเก็บผลการสอบถามไว้ใช้ซ้ำในช่วงเวลาสั้น ๆ เพื่อลดจำนวนคำขอซ้ำสำหรับหมายเลขเดิม ทั้งสองอย่างนี้ช่วยให้ระบบยังทำงานได้แม้ระบบภายนอกมีปัญหา
สิ่งที่ต้องไม่ลืมคือการบันทึกว่าการตัดสินใจครั้งนั้นอาศัยการสอบถามใด และได้ผลลัพธ์อะไรกลับมา หากไม่มีบันทึกนี้ การอธิบายย้อนหลังว่าทำไมระบบจึงยอมรับหรือปฏิเสธรายการหนึ่งจะเป็นไปไม่ได้เลย
สำหรับนักพัฒนา: แบ่งประเภทข้อผิดพลาดและบันทึกที่มา
ข้อเสนอที่สำคัญที่สุดคือการแบ่งข้อผิดพลาดออกเป็นกลุ่มที่ต้องจัดการต่างกัน อย่างน้อยควรแยกความผิดพลาดด้านรูปแบบ ความผิดพลาดของหลักตรวจสอบ และความล้มเหลวของการสอบถามภายนอก
สองกลุ่มแรกเป็นเรื่องที่ผู้ใช้แก้ได้ด้วยการแก้ข้อมูล จึงควรแสดงข้อความที่ชี้จุดให้ชัด ส่วนกลุ่มที่สามผู้ใช้แก้ไม่ได้ และมีโอกาสสำเร็จหากลองใหม่ จึงควรมีสัญญาณให้ระบบต้นทางรู้ว่าควรรอและลองใหม่ ไม่ใช่แสดงเป็นความผิดของผู้ใช้
อีกข้อคืออย่าให้ข้อความที่ส่งกลับไปยังผู้ใช้เป็นข้อความเดียวที่แปลว่าไม่สำเร็จ เพราะทำให้ระบบปลายทางแยกแยะไม่ได้ว่าควรทำอะไรต่อ การมีรหัสหมู่ของข้อผิดพลาดช่วยให้ฝั่งผู้ใช้เลือกวิธีแสดงผลได้ตรงกับสถานการณ์
สุดท้ายคือการทำกฎการตรวจให้มีเวอร์ชัน เมื่อข้อกำหนดของระบบต้นทางเปลี่ยนแปลง การตัดสินในอดีตต้องอธิบายได้ด้วยกฎที่ใช้ในเวลานั้น การเก็บเวอร์ชันของกฎไว้ในผลลัพธ์ช่วยให้การทบทวนย้อนหลังเป็นไปได้จริง แนวทางนี้ใช้ได้ทั้งการตรวจทีละรายการและการตรวจเป็นชุดตามที่อธิบายไว้ในขั้นตอนการตรวจสอบแบบกลุ่ม และสำหรับกรณีที่ระบบไม่รู้จักกฎเลย ควรใช้คำตอบที่อธิบายไว้ในบทความเรื่องหมายเลขที่ไม่มีหลักตรวจสอบ หากต้องการเทียบผลของสองชั้นกับค่าสมมติ เครื่องมือตรวจสอบจะแสดงส่วนประกอบที่ใช้ตัดสินทุกครั้ง
ก้าวต่อไป
เริ่มจากการเขียนรายการว่าการตรวจแต่ละข้อในระบบคุณคำนวณในเครื่องหรือต้องสอบถามภายนอก แล้วตรวจว่าข้อผิดพลาดทั้งสามกลุ่มถูกแยกออกจากกันจริงหรือยัง หากยังรวมอยู่ด้วยกัน นั่นคือจุดที่ผู้ใช้สับสนมากที่สุดในทางปฏิบัติ
ค่าทุกค่าที่ใช้ในตัวอย่างการเรียกใช้งานเป็นค่าสมมติ และไม่ควรนำหมายเลขจริงไปไว้ในบันทึกเหตุการณ์หรือชุดทดสอบไม่ว่าในกรณีใด