0:00 — การสร้างงานเสร็จสิ้น เอเจนต์บอกว่าเสร็จแล้ว ซึ่งเป็นการอ้างว่าโค้ดถูกเขียนเสร็จ ไม่ใช่การอ้างว่าโค้ดนั้นทำงานได้จริง ผู้ใช้ตัวสร้างเว็บด้วย AI ทุกคนต่างเคยรู้สึกถึงช่องว่างระหว่างสองสิ่งนี้อย่างน้อยครั้งหนึ่ง เปิดหน้าตัวอย่าง กดปุ่มที่สาม ไม่มีอะไรเกิดขึ้น ผมเคยเห็นห้องเดโมเงียบกริบตรงจุดนั้นพอดี ดังนั้นก่อนที่มนุษย์คนไหนจะได้เห็นงานสร้าง มันต้องผ่านห่วงโซ่ที่โต้เถียงกับตัวเองประมาณหกนาที นี่คือสิ่งที่เกิดขึ้นจริง ไล่เรียงจากงานสร้างชิ้นหนึ่งที่เราเห็นมันพลาดแล้วค่อยได้รับการแก้ไข
0:02 — เริ่มการรีวิวโค้ด ไม่ใช่เอเจนต์ตัวเดิมที่เขียนโค้ดมาอ่านการบ้านของตัวเองซ้ำ แต่เป็นเอเจนต์คนละตัว พรอมต์คนละแบบ ไม่มีผลประโยชน์ทับซ้อนกับการที่งานจะผ่านหรือไม่ ความแยกส่วนนี้สำคัญกว่าที่ฟังดู เอเจนต์ที่ตัดสินใจตอนบ่ายสองสิบสี่ว่าการเรียก fetch โดยไม่มีการจัดการข้อผิดพลาดนั้นโอเค ก็จะยังคิดแบบเดิมตอนบ่ายสองสิบห้าถ้าคุณให้มันตรวจงานของตัวเอง ผู้รีวิวคนใหม่ที่ได้รับคำสั่งว่า "หาสิ่งที่พัง แล้วอ้างอิงไฟล์" จะทำตัวเหมือนวิศวกรอาวุโสขี้บ่นที่คุณต้องการจริง ๆ ในหน้าที่นี้ ในงานสร้างครั้งก่อน มันจับได้ว่ายอดรวมตะกร้าสินค้าไม่เคยอัปเดตเลยอย่างเงียบ ๆ — ฟังก์ชัน `updateTotal` ถูกกำหนดไว้ใน `Cart.jsx` แต่ไม่เคยถูกเชื่อมกับ handler ที่จัดการการเปลี่ยนจำนวนสินค้า นั่นแปลว่าฟังก์ชันมีอยู่จริงแต่ไม่เคยถูกเรียกใช้เลย นี่คือประเภทของปัญหาที่การรีวิวโค้ดถูกออกแบบมาเพื่อจับ
0:04 — การตรวจสอบความปลอดภัย ซึ่งแคบกว่าที่ชื่อฟังดู โดยตั้งใจ — นี่ไม่ใช่ pentest แต่เป็นการล่าหารูปแบบข้อผิดพลาดจำนวนหนึ่งที่พบได้จริงในโค้ดที่สร้างโดย AI เช่น SQL ที่ต่อสตริงโดยตรง การตรวจสอบเฉพาะฝั่งไคลเอนต์ที่ถูกเชื่อถือราวกับเป็นทั้งหมด และตัวเด่นประจำ คือ API key ที่ฝังไว้แบบตายตัว เพราะเอเจนต์ที่เขียนฟีเจอร์นั้นไม่มีข้อกำหนดเรื่องตัวแปรสภาพแวดล้อมวางอยู่ตรงหน้า จึงเลือกวิธีที่ใช้ได้ตรงหน้าแทน เราเจอเคสแบบนี้บ่อยจนแทบไม่ถือเป็นเรื่องน่าแปลกใจแล้ว
0:07 — ลิงก์และ SEO ไม่ใช่เรื่องหวือหวา แต่จับสิ่งที่ไม่มีใครสังเกตจนกว่าลูกค้าจะเจอ: ลิงก์นำทางที่ชี้ไปยัง /pricing ตอนที่หน้าเว็บถูกสร้างขึ้นจริงที่ /price รายการใน sitemap สำหรับหน้าที่ขึ้น 404 หรือ meta description ที่ยังคงข้อความ placeholder จากเทมเพลตอยู่ ไม่มีสิ่งใดในนี้ที่ทำให้บิลด์พัง แต่ทั้งหมดนี้ค่อยๆ บั่นทอนสิ่งที่ผู้ใช้ส่วนใหญ่ของเราสร้างเว็บไซต์ขึ้นมาเพื่อทำ — ให้ถูกค้นเจอ ให้มีคนคลิก
0:09 — การเข้าถึง (accessibility) นี่คือการตรวจสอบอัตโนมัติด้วย axe-core ไม่ใช่การตรวจสอบด้วยมือแบบเต็มรูปแบบ และควรพูดตรง ๆ ว่าการแลกเปลี่ยนนี้ได้อะไรมา axe-core จับได้เรื่องอัตราส่วนความคมชัด ข้อความ alt ที่หายไป ช่องกรอกฟอร์มที่ไม่มีป้ายกำกับ กับดักลำดับการกด tab — นั่นคือชั้นเชิงกล คิดเป็นประมาณ 30-40% ของสิ่งที่การรีวิว WCAG แบบเต็มรูปแบบจะตรวจพบ มันจะไม่จับได้ว่าประสบการณ์การใช้งานผ่านโปรแกรมอ่านหน้าจอนั้นถูกต้องตามข้อกำหนดแต่ใช้งานจริงแล้วสับสน เราเลือกใช้แบบอัตโนมัติล้วน ๆ เพราะทำงานเสร็จในไม่กี่วินาที และสิ่งที่ปล่อยผ่านช่องทางนี้ส่วนใหญ่คือเว็บไซต์การตลาดและเครื่องมือขนาดเล็ก ไม่ใช่แอปพลิเคชันประเภทที่การตรวจสอบไม่ครบถ้วนจะเป็นความเสี่ยงจริงต่อใคร
0:11 — การตรวจสอบความสอดคล้อง (conformance) ชั้นนี้ไม่ได้ถามว่า "สิ่งนี้ดีไหม" แต่ถามว่า "สิ่งนี้ตรงกับที่สัญญาไว้หรือเปล่า" แผนบอกว่าจะมีสี่หน้า แต่งานสร้างส่งมอบแค่สามหน้า — conformance คือสิ่งที่จะสังเกตเห็นเรื่องนี้ แผนสัญญาว่าจะมีฟอร์มติดต่อที่ใช้งานได้ แต่สิ่งที่ส่งมอบคือฟอร์มที่ไม่มีการทำงานของปุ่มส่ง — ก็เป็นชั้นเดียวกัน จับได้เหมือนกัน นี่คือการตรวจสอบที่รับผิดชอบต่อผู้ใช้โดยตรงที่สุด เพราะวัดเทียบกับความตั้งใจที่ผู้ใช้ระบุไว้ ไม่ใช่แนวคิดนามธรรมเรื่องคุณภาพ
0:13 — การตรวจสอบในเบราว์เซอร์จริง และนี่คือจุดที่งานสร้างของเราพังจริง ๆ ชั้นนี้ปลอมได้ยากที่สุดเพราะมันไม่ได้อ่านโค้ด แต่ควบคุมเบราว์เซอร์จริง — คลิก พิมพ์ รอ ตรวจสอบว่า DOM เปลี่ยนไปตามที่ควรหรือไม่ งานสร้างในกรณีนี้คือเกมประเภท idle game และเกมจะได้รับการตรวจสอบเพิ่มเติมในชั้นนี้ เพราะเกมสามารถแสดงผลได้สวยงามไร้ที่ติแต่เล่นไม่ได้จริง — หน้าจอแสดงคะแนนอาจดูสมบูรณ์แบบทั้งที่ไม่เกี่ยวข้องกับตรรกะการคิดคะแนนเลย ตัวตรวจสอบเล่นเกมนี้จริง ๆ คะแนนอัปเดตปกติดี แต่เสียงไม่ดังเลย
สามรอบ แล้วยกระดับ
ปัญหาที่พบไม่ได้ถูกส่งมาเป็นรายงานบั๊ก แต่เข้าสู่รอบการแก้ไขทันที และห่วงโซ่ตรวจสอบซ้ำได้สูงสุดสามรอบภายในการสร้างงานนั้น รอบแรก การแก้ไขไปแตะที่การตั้งค่าเริ่มต้นของ mixer ซึ่งไม่มีปัญหาอยู่แล้ว เสียงจึงยังคงเงียบ รอบที่สอง การแก้ไขอีกจุดหนึ่งจัดการกับกรณีขอบของสถานะการโหลดที่ดูใกล้เคียงกัน และ — เรื่องนี้เกิดขึ้นบ่อยกว่าที่คาด — กลับสร้างปัญหาใหม่เล็ก ๆ ขึ้นมาโดยไม่ได้แก้ปัญหาเดิม รอบที่สาม เสียงยังคงเงียบ และถึงจุดนี้มักจะเป็นได้แค่สองแบบ คือปัญหายากจริง ๆ หรือเป็นการแจ้งเตือนที่ผิดพลาด ซึ่งครั้งนี้เป็นแบบที่ยาก
ระบบจึงยกระดับปัญหาขึ้นเอง โดยจัดคิวรอบแก้ไขเพิ่มเติมที่โฟกัสเฉพาะปัญหาที่ยังคงอยู่ ทำงานบนสำเนา (clone) ของงานสร้าง แทนที่จะเป็นตัวงานสร้างจริง หมายความว่ารอบการยกระดับนี้ล้มเหลวได้โดยไม่ทำให้เราเสียเวอร์ชันที่ใช้งานได้แล้วไป รอบนี้พบสาเหตุที่แท้จริง คือแฟล็กปิดเสียงที่ถูกตั้งไว้ระหว่างการดีบักครั้งก่อนแล้วไม่เคยถูกสลับกลับ ซึ่งอยู่ในไฟล์คนละไฟล์กับที่การแก้ไขสองครั้งก่อนหน้าเคยแตะ เมื่อล้างแฟล็กแล้วตรวจสอบซ้ำก็ผ่าน ไม่มีใครต้องมาดูงานสร้างนี้เลยจนกว่ามันจะทำงานได้จริงแล้ว
| คำสั่งซื้อ | ชั้นการตรวจสอบ | สิ่งที่ตรวจจับได้ |
|---|---|---|
| 1 | การรีวิวโค้ด | ตรรกะที่พัง handler ที่ไม่ทำงาน บั๊กเรื่องสถานะ |
| 2 | การตรวจสอบความปลอดภัย | ช่องโหว่การโจมตีแบบ injection ข้อมูลลับที่รั่วไหล รูปแบบที่ไม่ปลอดภัย |
| 3 | ลิงก์และ SEO | ลิงก์เสีย ข้อมูลเมตาที่หายไป ความถูกต้องของ sitemap/robots.txt |
| 4 | การเข้าถึง (Accessibility) | ตรวจสอบอัตโนมัติด้วย axe-core: ความคมชัด ป้ายกำกับ การนำทางด้วยแป้นพิมพ์ |
| 5 | ความสอดคล้อง (Conformance) | งานสร้างมีสิ่งที่แผนสัญญาไว้ครบหรือไม่ |
| 6 | การตรวจสอบในเบราว์เซอร์จริง | รันงานสร้างจริง — คลิก พิมพ์ ดูว่ามันตอบสนองอย่างไร |
สิ่งที่ฉันจะข้ามไปในครั้งหน้า
ไม่กี่เดือนก่อนที่เกม idle game นั้นจะถูกรัน เราเคยลองใช้ระบบนี้ในเวอร์ชันที่ผ่อนปรนกว่า — ให้ผู้ตรวจสอบยกข้อกังวลอะไรก็ได้ พูดยังไงก็ได้ ผลลัพธ์คือข้อค้นพบอย่าง "ลองแยกส่วนนี้เป็น helper ดูไหม" และ "ชื่อตัวแปรนี้อาจจะชัดเจนกว่าได้" ซึ่งอ่านดูเหมือนความละเอียดรอบคอบแต่ไม่ได้แก้อะไรเลย รอบการแก้ไขเสียเวลาทั้งรอบไปกับการขัดเกลาถ้อยคำแทนที่จะแก้สิ่งที่พังจริง ๆ เราจึงปรับกฎให้เข้มขึ้นเป็น: ต้องระบุไฟล์ อธิบายความล้มเหลว หรือไม่พูดอะไรเลย ผลลัพธ์จากผู้ตรวจสอบลดลงประมาณครึ่งหนึ่ง และเกือบทั้งหมดที่เหลือสามารถนำไปปฏิบัติได้จริง ถ้าให้สร้างระบบนี้ใหม่ตั้งแต่ต้น ผมจะข้ามเวอร์ชันผ่อนปรนไปเลยแล้วเริ่มจากกฎเรื่องหลักฐานทันที เราไม่จำเป็นต้องเรียนรู้บทเรียนนี้ด้วยวิธีที่แพงขนาดนั้น แต่ก็ทำไปแล้ว
กฎนี้มีต้นทุนจริง และผมจะไม่แสร้งว่าไม่มี ข้อกังวลที่คลุมเครือแต่เป็นจริงอย่าง "การออกแบบ API แบบนี้จะสร้างปัญหาในอีกหกเดือนข้างหน้า" ตอนนี้จะถูกทิ้งไปเฉย ๆ เพราะผู้ตรวจสอบไม่สามารถระบุให้ตรงกับความล้มเหลวที่เป็นรูปธรรมได้ เรายอมรับการแลกเปลี่ยนนี้แล้ว ห่วงโซ่ที่ทำการรีวิวสถาปัตยกรรมด้วยจะไม่เร็วพอที่จะรันกับงานสร้างทุกชิ้น และความเร็วคือประเด็นหลักของการรันระบบนี้แบบอัตโนมัติแทนที่จะให้มนุษย์ทำ
ถ้ามีใครถาม ผมก็จะแนะนำให้ข้ามการเพิ่มรอบที่สี่ไปด้วย เราปรับจำนวนรอบตามงานสร้างจริง และคุณค่าส่วนเพิ่มหลังรอบที่สามลดลงอย่างชัดเจน — รอบแรกแก้ปัญหาที่แก้ได้ส่วนใหญ่ รอบที่สองส่วนใหญ่เป็นการเก็บกวาดปัญหาที่รอบแรกสร้างขึ้นมา และเมื่อถึงรอบที่สามสิ่งที่เหลืออยู่ก็เป็นปัญหายากจริง ๆ หรือไม่ก็ไม่เคยพังจริงตั้งแต่แรก รอบที่สี่ส่วนใหญ่ให้แค่เวลารอที่นานขึ้นสำหรับผลลัพธ์เดิม
ไม่มีอะไรในนี้ที่ฟรี และไม่มีอะไรที่ไร้ที่ติ หกชั้นการตรวจสอบบวกกับจำนวนรอบแก้ไขที่ต้องใช้ ล้วนเพิ่มเวลาจริงให้กับทุกการสร้างงาน — ความแตกต่างระหว่างเสร็จภายในไม่ถึงหนึ่งนาที กับเสร็จภายในหลายนาที เราคิดว่านี่คือการแลกเปลี่ยนที่ถูกต้องสำหรับสิ่งที่คุณกำลังจะนำไปให้ลูกค้าของคุณเองใช้ แต่ "เร็ว" กับ "ผ่านการตรวจสอบแล้ว" ดึงไปคนละทาง และเราเลือกความถูกต้องผ่านการตรวจสอบ ผู้ตรวจสอบก็เป็น LLM เช่นกัน จึงอาจแจ้งเตือนสิ่งที่จริง ๆ แล้วไม่ได้พัง หรือพลาดสิ่งที่พังจริงไปบ้าง กฎเรื่องหลักฐานและวงจรหลายรอบเป็นเพียงการป้องกันความเสี่ยงนั้น ไม่ใช่การรับประกัน
สิ่งที่คุณได้รับในตอนท้ายคือบันทึกฉบับหนึ่ง: เลเยอร์ไหนทำงานบ้าง เจออะไรบ้าง แก้ไขอะไรไปแล้ว และเหลืออะไรไว้ให้ใช้วิจารณญาณของคุณเอง บันทึกนั้นใกล้เคียงกับผลิตภัณฑ์จริงมากกว่าตัวโค้ดเสียอีก — มันคือความแตกต่างระหว่างการเชื่อใจในบิลด์เพราะมันดูเสร็จสมบูรณ์ กับการเชื่อใจเพราะมีบางอย่างที่พยายามโจมตีมันแล้วล้มเหลว



