การพิสูจน์ที่ไม่มีทางตก
เรามีกติกาว่าเทสทุกตัวต้องถูกพิสูจน์ด้วยการจงใจทำโค้ดให้พังแล้วดูว่าเทสจับได้ไหม คืนหนึ่งเราทำโค้ดพังแล้วเทสยังผ่าน สาเหตุคือข้อมูลตัวอย่างในเทสมีแต่ตัวเลข บทความนี้เล่ากลไกของมัน และบทเรียนหนึ่งบรรทัดที่ได้
เทสที่ผ่านบอกเราได้สองอย่าง อย่างแรกคือโค้ดถูก อย่างที่สองคือเทสตัวนั้นจับอะไรไม่ได้เลย จากหน้าจอสีเขียว สองกรณีนี้หน้าตาเหมือนกันทุกประการ
เราจึงมีกติกาประจำคิวงานว่า เมื่องานเสร็จ ห้องต้องตรวจเองสามขั้น คืออ่านส่วนที่แก้เทียบกับโจทย์ รันเทส และ "ฉีดแดง" ฉีดแดงหมายถึงจงใจทำโค้ดให้ผิดหนึ่งจุด แล้วดูว่าเทสเปลี่ยนเป็นสีแดงไหม ถ้าเปลี่ยน แปลว่าเทสมีฟัน ถ้าไม่เปลี่ยน แปลว่าเทสนั้นผ่านได้ไม่ว่าโค้ดจะถูกหรือผิด ("ห้อง" ของเราคือ AI หนึ่งตัวที่ดูแลงานด้านหนึ่ง)
คืนวันที่ 10 ตุลาคม ราวตีหนึ่งครึ่ง เราฉีดแดงแล้วเทสยังเขียว
งานชิ้นนั้น
เป็นงานเก็บกวาดธรรมดา โค้ดส่วนที่ตรวจว่ารหัสอ้างอิงมีรูปแบบถูกต้องไหมถูกเขียนซ้ำกันอยู่มากกว่าหนึ่งที่ งานคือรวมให้เหลือที่เดียว โมเดลเปิดบนเครื่องของเราเป็นคนเขียนโค้ดและเขียนเทส ส่วนห้องซึ่งเป็น Claude เป็นคนตรวจ
รหัสอ้างอิงแบบนี้ประกอบด้วยตัวเลข 0-9 และตัวอักษร a-f โค้ดใหม่ต้องแปลงตัวอักษรเป็นตัวเล็กก่อนส่งต่อไปยังฐานข้อมูล เทสจึงต้องพิสูจน์ว่า ถ้าส่งรหัสตัวใหญ่เข้ามา ปลายทางจะได้รับตัวเล็ก
ทำไมแดงไม่ติด
ห้องฉีดแดงด้วยการถอดขั้นตอนแปลงเป็นตัวเล็กออก เทสควรตก แต่ไม่ตก
สาเหตุอยู่ในบันทึกของห้องประโยคเดียว: รหัสตัวอย่างที่เทสใช้มีแต่ตัวเลข การแปลงเป็นตัวใหญ่จึงไม่เปลี่ยนอะไรเลย แดงที่ฉีดเข้าไปจึงไม่มีวันกัด
พูดอีกแบบคือ เทสตั้งใจจะส่ง "รหัสตัวใหญ่" เข้าไป แต่รหัสที่มีแต่ตัวเลขไม่มีตัวใหญ่ตัวเล็ก ของที่ส่งเข้าไปกับของที่คาดว่าจะได้กลับมาเป็นข้อความเดียวกันตั้งแต่ต้น โค้ดจะแปลงหรือไม่แปลง ผลก็ตรงกันเสมอ เทสตัวนี้ผ่านมาตลอด และจะผ่านต่อไปแม้วันที่มีคนลบขั้นตอนนั้นทิ้ง
แก้อย่างไร
ห้องเปลี่ยนรหัสตัวอย่างเป็นรหัสที่มีตัวอักษร แล้วฉีดแดงซ้ำ คราวนี้เทสตก เพราะรหัสตัวใหญ่หลุดไปถึงฐานข้อมูลโดยไม่ถูกแปลง เมื่อคืนโค้ดกลับ เทสผ่าน 2 จาก 2 และชุดเทสข้างเคียงผ่าน 31 ตก 0
มีรายละเอียดหนึ่งที่เราอยากให้เห็น บันทึกของห้องเขียนว่า โค้ดที่โมเดลเปิดเขียนมาถูกตั้งแต่แรก มีเพียงเทสที่อ่อน ถ้าเราไม่ฉีดแดง เราจะได้โค้ดที่ถูกพร้อมเทสที่เขียว แล้วปิดงานได้โดยไม่มีอะไรผิดให้เห็น ความเสียหายจะมาทีหลัง ในวันที่มีคนแก้โค้ดส่วนนั้นแล้วเชื่อเทสตัวนี้
อีกเรื่องคือกติกาเรื่องใครแก้อะไร ปกติการแก้ข้อมูลตัวอย่างในเทสเป็นงานของโมเดลเปิด ไม่ใช่ของห้อง ข้อยกเว้นที่ตั้งไว้ก่อนหน้านั้นคือ เมื่อมันขวางการตรวจที่กำลังรันอยู่ ให้ห้องแก้เองแล้วจดไว้ในคำอธิบายงาน ห้องใช้ข้อยกเว้นนี้และจดไว้ตามนั้น
บทเรียนที่จดไว้
ห้องเขียนบทเรียนไว้หนึ่งบรรทัด: การพิสูจน์ด้วยแดงต้องใช้ข้อมูลตัวอย่างที่แยกความต่างของการเปลี่ยนแปลงนั้นได้ รหัส "ตัวใหญ่" ที่มีแต่ตัวเลขพิสูจน์อะไรไม่ได้
เราคิดว่าบทเรียนนี้กว้างกว่าเรื่องรหัสอ้างอิง ก่อนเชื่อเทสตัวใด ให้ถามว่าข้อมูลที่ป้อนเข้าไปทำให้โค้ดที่ถูกกับโค้ดที่ผิดให้ผลต่างกันจริงไหม ถ้าไม่ต่าง เทสตัวนั้นเป็นเพียงพิธี
ในบันทึกเดียวกันมีอีกกรณีที่ใช้แนวคิดเดียวกันจากอีกด้าน ผู้ตรวจอิสระของงานอีกชิ้นยืนยันว่าด่านตรวจใหม่กัดจริง ด้วยการปิดการสแกนของด่านนั้นแล้วดูว่ามีเช็กตกหรือไม่ ผลคือตก 6 รายการ
เรื่องที่ไม่น่าภูมิใจในคืนเดียวกัน
งานชิ้นนี้มีคนทำไปแล้ว ตั้งแต่ตอนเที่ยงของวันก่อนหน้ามีงานแก้เรื่องเดียวกันเปิดรอตรวจอยู่ โดยมีแต่โค้ด ไม่มีเทส ห้องของเราไม่ได้ค้นก่อนลงมือ จึงทำซ้ำ กติกาใหม่ที่ออกมาตอน 01:38 ของคืนนั้นคือ ก่อนรับงานหรือสั่งงานทุกครั้ง ให้ค้นงานที่เปิดอยู่ด้วยเลขของเรื่องนั้นก่อน เมื่อย้อนใช้กติกานี้กับงานของห้องเอง ก็พบว่าซ้ำอยู่สองเรื่อง
ห้องเสนอให้ใช้งานชิ้นใหม่แทนชิ้นเก่า ด้วยเหตุผลว่าโค้ดที่แก้เหมือนกัน แต่ชิ้นใหม่มีเทสที่แยกความต่างได้ การทำซ้ำครั้งนี้จึงได้ของติดมือกลับมาหนึ่งอย่าง แต่เราไม่อยากเล่าให้ดูเป็นความตั้งใจ มันเป็นงานซ้ำที่บังเอิญมีประโยชน์
สิ่งที่เรายังไม่รู้
- ห้องสังเกตเห็นครั้งแรกอย่างไรว่าแดงไม่ติด บันทึกเขียนเพียงว่า "แก้ได้แล้ว" ลำดับที่เล่าในหัวข้อ "ทำไมแดงไม่ติด" เป็นการเรียงจากผลที่บันทึกไว้ ไม่ใช่จากบันทึกนาทีต่อนาที
- บทเรียนบรรทัดนั้นถูกย้ายเข้าคู่มือการทำงานของห้องแล้วหรือยัง เราไม่พบหลักฐาน
- มีเทสตัวอื่นที่ใช้ข้อมูลตัวอย่างอ่อนแบบเดียวกันอีกกี่ตัว ยังไม่มีการกวาดตรวจเรื่องนี้ และยังไม่มีเครื่องมือที่จับข้อมูลตัวอย่างแบบนี้ได้เอง
- สุดท้ายงานชิ้นไหนถูกรวมเข้าสายหลัก บันทึกจบที่คำเสนอและการส่งงานคืนให้ทีมเจ้าของเรื่อง
ข้อคิดที่เอาไปใช้ได้
- เทสที่ไม่เคยเห็นตก ยังไม่ได้พิสูจน์อะไร ทำโค้ดให้พังหนึ่งจุดแล้วดูว่าเทสตกไหม ใช้เวลาไม่กี่นาที
- ถ้าทำพังแล้วยังเขียว ให้สงสัยข้อมูลตัวอย่างก่อน ข้อมูลที่ "สะอาดเกินไป" เช่น มีแต่เลข มีแต่ค่าว่าง มีรายการเดียว มักทำให้โค้ดถูกกับโค้ดผิดให้ผลเท่ากัน
- คนเขียนโค้ดกับคนพิสูจน์เทสควรเป็นคนละคน ในกรณีนี้คนเขียนคือโมเดลหนึ่ง คนพิสูจน์คืออีกโมเดลหนึ่ง และความผิดอยู่ในส่วนที่คนเขียนมั่นใจที่สุด คือเทสของตัวเอง
- ค้นก่อนลงมือ งานที่ดีที่สุดของคืนนั้นเกิดบนงานที่ไม่ควรต้องทำตั้งแต่แรก
คำถามที่พบบ่อย
ทำไมจงใจทำโค้ดพังแล้วเทสยังผ่าน
การพิสูจน์ด้วยแดงต้องใช้ข้อมูลตัวอย่างที่แยกความต่างของการเปลี่ยนแปลงนั้นได้ รหัส "ตัวใหญ่" ที่มีแต่ตัวเลขพิสูจน์อะไรไม่ได้
จะรู้ได้อย่างไรว่าเทสตัวหนึ่งจับบั๊กได้จริง
ทำโค้ดให้พังหนึ่งจุดแล้วดูว่าเทสตกไหม ใช้เวลาไม่กี่นาที