ข้อกำหนดที่ไม่ใช่หน้าที่หลัก (Non-functional Requirements) ของ AI Coding คืออะไร? เหตุใด LLM จึงไม่เก่งเท่ากับข้อกำหนดด้านหน้าที่หลัก (Functional Requirements) และเกณฑ์ที่มนุษย์ควรเป็นผู้ตัดสินใจ

ข้อกำหนดที่ไม่ใช่หน้าที่หลัก (Non-functional Requirements) ของ AI Coding คืออะไร? เหตุใด LLM จึงไม่เก่งเท่ากับข้อกำหนดด้านหน้าที่หลัก (Functional Requirements) และเกณฑ์ที่มนุษย์ควรเป็นผู้ตัดสินใจ

ข้อกำหนดที่ไม่ใช่หน้าที่การทำงาน (Non-functional requirements) คือข้อกำหนดที่ไม่ได้กำหนดว่าระบบจะ "ทำอะไร" แต่กำหนดว่าระบบจะ "ทำงานได้อย่างปลอดภัย รวดเร็ว ประหยัด และดูแลรักษาได้ง่ายเพียงใด" ในปัจจุบัน เอเจนต์สำหรับการเขียนโค้ดอย่าง Claude Code เริ่มได้รับความไว้วางใจให้รับผิดชอบการนำข้อกำหนดด้านหน้าที่การทำงาน (Functional requirements) ไปใช้งานจริงมากขึ้น เช่น การสร้างหน้าจอหรือ API ให้เป็นไปตามสเปก ในขณะเดียวกัน คุณ mizutani (@m_mizutani) ซึ่งเป็นวิศวกรด้านความปลอดภัยและซอฟต์แวร์ ได้ระบุไว้ใน โพสต์บน X ว่าสิ่งที่เขาชี้แนะให้กับ Claude นั้น "ร้อยละ 90 เกี่ยวข้องกับข้อกำหนดที่ไม่ใช่หน้าที่การทำงาน เช่น ความปลอดภัย ประสิทธิภาพ ต้นทุน และความสามารถในการบำรุงรักษา"

บทความนี้จัดทำขึ้นสำหรับ Tech Lead และผู้รับผิดชอบด้านการพัฒนาที่เริ่มมอบหมายงานการเขียนโค้ดให้กับ AI โดยจะสรุปเหตุผลตามผลการวิจัยที่เผยแพร่ว่าเหตุใด LLM จึงสามารถจัดการกับข้อกำหนดด้านหน้าที่การทำงานได้ค่อนข้างดี และเหตุใดงานด้านข้อกำหนดที่ไม่ใช่หน้าที่การทำงานจึงยังคงต้องอาศัยมือมนุษย์ นอกจากนี้ ยังจะแสดงให้เห็นว่ามนุษย์ควรตัดสินใจเรื่องใดและต้องส่งมอบงานในรูปแบบใดเพื่อให้ LLM สามารถทำงานได้ตามมาตรฐานที่กำหนด

ความแตกต่างระหว่าง Functional Requirements และ Non-functional Requirements คืออะไร?

ความแตกต่างระหว่าง Functional Requirements และ Non-functional Requirements คืออะไร?

ฟังก์ชันนัลรีไควร์เมนต์ (Functional Requirements) คือการกำหนดว่า "จะตอบสนองต่ออินพุตอย่างไร" ในขณะที่นอนฟังก์ชันนัลรีไควร์เมนต์ (Non-functional Requirements) คือการกำหนดว่า "กระบวนการนั้นจะทำงานภายใต้เงื่อนไขใดและในระดับใด"

การทำความเข้าใจการจำแนกประเภทตามมาตรฐาน จะช่วยให้ทีมมีภาษากลางในการหารือกันว่าควรให้ AI รับผิดชอบส่วนใด และส่วนใดที่มนุษย์ควรเป็นผู้ตรวจสอบ

นิยามและการจำแนกประเภทที่สำคัญ (ISO/IEC 25010 และ Non-functional Requirements Grade)

ตัวอย่างเช่น "ผู้ใช้ที่ล็อกอินสามารถดูรายการใบแจ้งหนี้ของตนเองได้" คือความต้องการเชิงฟังก์ชัน (Functional Requirements) ส่วนการกำหนดว่า "รายการต้องแสดงผลภายใน 1 วินาที" "แม้จะเปลี่ยน URL ก็ไม่สามารถดูใบแจ้งหนี้ของบริษัทอื่นได้" หรือ "ค่าใช้จ่ายในการดำเนินงานรายเดือนต้องอยู่ในงบที่กำหนด" สำหรับหน้าจอเดียวกันนั้น คือความต้องการที่ไม่ใช่เชิงฟังก์ชัน (Non-functional Requirements) อย่างแรกสามารถตรวจสอบได้ว่าบรรลุผลหรือไม่เพียงแค่ลองใช้งานหน้าจอ แต่อย่างหลังต้องมีการจัดเตรียมเงื่อนไขและวัดผลจึงจะทราบได้

มาตรฐานสากล ISO/IEC 25010 ซึ่งกำหนดคุณลักษณะด้านคุณภาพของซอฟต์แวร์ ได้แบ่งคุณภาพของผลิตภัณฑ์ออกเป็น 9 คุณลักษณะในฉบับปี 2023 (คำอธิบายเกี่ยวกับ arc42 Quality Model) โดย 8 คุณลักษณะจากทั้งหมด (ยกเว้นความเหมาะสมด้านฟังก์ชัน) ถือเป็นมุมมองของความต้องการที่ไม่ใช่เชิงฟังก์ชันโดยรวม ในประเทศญี่ปุ่น Non-functional Requirements Grade ของ IPA (Information-technology Promotion Agency) ถูกนำมาใช้เป็นรายการตรวจสอบเพื่อป้องกันความเข้าใจที่ไม่ตรงกันระหว่างผู้ว่าจ้างและนักพัฒนา

แม้ว่าการจำแนกทั้ง 2 ประเภทจะมีมุมมองที่แตกต่างกัน แต่ทั้งคู่สามารถใช้เป็นรายการตรวจสอบเพื่อค้นหา "คุณสมบัติที่ไม่ใช่ฟังก์ชัน" ได้อย่างครบถ้วน

การจำแนกมุมมองตัวอย่างค่าที่ต้องกำหนด
ISO/IEC 25010 (คุณภาพผลิตภัณฑ์)ประสิทธิภาพ, ความเข้ากันได้, ความสามารถในการโต้ตอบ, ความน่าเชื่อถือ, ความปลอดภัย, ความสามารถในการบำรุงรักษา, ความยืดหยุ่น, ความปลอดภัยทางกายภาพเวลาตอบสนอง, จำนวนผู้ใช้ที่รองรับพร้อมกัน, การกู้คืนจากความเสียหาย, ความง่ายในการแก้ไข
IPA Non-functional Requirements Gradeความพร้อมใช้งาน, ประสิทธิภาพ/ความสามารถในการขยายตัว, การดำเนินงาน/การบำรุงรักษา, การย้ายระบบ, ความปลอดภัย, สภาพแวดล้อมระบบ/นิเวศวิทยาอัตราการใช้งาน (Availability), ระยะเวลาเป้าหมายในการกู้คืน (RTO), จำนวนรุ่นของข้อมูลสำรอง, ระยะเวลาในการย้ายระบบ

Non-functional Requirements Grade เป็นสิ่งที่ใช้สำหรับตกลงความต้องการด้านโครงสร้างพื้นฐานของระบบ โดยไม่ได้กำหนดไปถึงวิธีการเขียนโค้ดของแอปพลิเคชัน เมื่อต้องสร้างเกณฑ์เพื่อส่งให้ AI การใช้รายการเหล่านี้เพื่อตรวจสอบความครบถ้วนและนำเฉพาะหัวข้อที่มีความหมายต่อระบบของตนเองมาแปลงเป็นตัวเลขถือเป็นวิธีที่ใช้งานได้จริงที่สุด

ลักษณะของ Non-functional Requirements: "ครอบคลุมทุกส่วน", "การแลกเปลี่ยน (Trade-off)", และ "ปรากฏขึ้นภายหลัง"

ข้อกำหนดที่ไม่ใช่หน้าที่หลัก (Non-functional requirements) มีคุณสมบัติ 3 ประการที่ข้อกำหนดด้านหน้าที่หลักไม่มี

ประการแรก คือความเป็นองค์รวม (Cross-cutting) การรั่วไหลของสิทธิ์การเข้าถึงไม่ได้เกิดขึ้นแค่หน้าจอเดียว แต่ครอบคลุมไปถึง API ทั้งหมดที่จัดการ ID ของผู้เช่า (Tenant) เช่นเดียวกับประสิทธิภาพการทำงานที่คิวรีเพียงหนึ่งรายการที่ทำงานช้าจะฉุดให้การตอบสนองของหน้าจออื่นช้าลงไปด้วย ไม่ว่าคุณจะเปิดไฟล์ไหนขึ้นมา ก็ไม่มีจุดใดที่เขียนโค้ดแล้วถือว่า "เสร็จสิ้น"

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

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

คุณสมบัติทั้ง 3 ประการนี้ ตรงกันข้ามกับ "สิ่งที่ LLM ถนัด" ซึ่งจะกล่าวถึงในหัวข้อถัดไปพอดี

ทำไม LLM ถึงสามารถจัดการ Functional Requirements ได้ค่อนข้างดี?

ทำไม LLM ถึงสามารถจัดการ Functional Requirements ได้ค่อนข้างดี?

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

สามารถตรวจสอบคำตอบที่ถูกต้องได้จาก Input และ Output

ข้อกำหนดที่สามารถเขียนได้ว่า "ถ้าใส่ค่านี้ ต้องได้ผลลัพธ์นี้" สามารถนำมาเปลี่ยนเป็นแบบทดสอบ (Test) ได้ หากมีแบบทดสอบ LLM จะสามารถวนลูปการทำงานด้วยตัวเองได้ ทั้งการเขียนโค้ด การรัน และการอ่านผลลัพธ์เพื่อแก้ไขหากเกิดข้อผิดพลาด แนวทางปฏิบัติที่ดีที่สุดของ Claude Code จาก Anthropic ก็แนะนำให้ส่งตัวตรวจสอบที่ให้ผลลัพธ์เป็นผ่านหรือไม่ผ่านเข้าไปด้วย โดยอธิบายว่าหากไม่มีวิธีตรวจสอบ Claude จะมีเกณฑ์ตัดสินเพียงแค่ว่า "ดูเหมือนจะเสร็จสมบูรณ์แล้ว" ซึ่งส่งผลให้มนุษย์ต้องทำหน้าที่เป็นตัวทดสอบแทน

ข้อกำหนดเชิงฟังก์ชัน (Functional Requirements) มีคุณสมบัติที่เอื้อต่อการวนลูปนี้ได้ง่าย เพราะสามารถเขียนค่าที่คาดหวังไว้ล่วงหน้าได้ ไม่ว่าจะเป็นการทำงานบนหน้าจอ การตอบสนองของ API หรือผลลัพธ์จากการคำนวณ แม้ว่าข้อความในสเปกจะมีความคลุมเครืออยู่บ้าง แต่หากระบุตัวอย่างอินพุตและเอาต์พุตสัก 2-3 ตัวอย่าง LLM ก็มักจะเข้าใจเจตนาได้ ในฝั่งของมนุษย์เอง เมื่อลองรันดูแล้วเห็นว่าถูกต้องหรือไม่ ก็จะสามารถให้คำแนะนำที่เฉพาะเจาะจงได้ว่า "ต้องการให้ผลลัพธ์ออกมาเป็นแบบนี้เมื่อใส่ค่านี้เข้าไป"

ในทางกลับกัน หลายสถานการณ์ที่เรารู้สึกว่าความสามารถในการเขียนโปรแกรมของ LLM พัฒนาขึ้น ก็คือสถานการณ์ที่มีการวนลูปการตรวจสอบเกิดขึ้นนั่นเอง แม้จะเป็นโมเดลเดียวกัน แต่หากเป็นข้อกำหนดที่ไม่สามารถเขียนแบบทดสอบได้หรือไม่มีตัวอย่างให้ ผลลัพธ์ที่ได้ก็จะด้อยลง และข้อกำหนดที่ไม่ใช่เชิงฟังก์ชัน (Non-functional Requirements) ส่วนใหญ่ก็จัดอยู่ในกลุ่มที่เขียนแบบทดสอบได้ยากในรูปแบบเดิม

สามารถแก้ไขได้ด้วยรูปแบบที่คุ้นเคย

อีกเหตุผลหนึ่งคือ ข้อกำหนดเชิงฟังก์ชันจำนวนมากประกอบขึ้นจากการรวมกันของปัญหาที่มีตัวอย่างการใช้งานจริงอยู่มากมายแล้ว ไม่ว่าจะเป็นการตรวจสอบข้อมูลในฟอร์ม (Form validation), การแบ่งหน้าแสดงรายการ (Pagination) หรือการส่งออกไฟล์ CSV ล้วนมีรูปแบบการเขียนที่เป็นมาตรฐานในแต่ละภาษาและเฟรมเวิร์ก ซึ่ง LLM มีความเชี่ยวชาญในการเลือกรูปแบบที่เหมาะสมกับสถานการณ์จากโค้ดจำนวนมหาศาลที่ได้เรียนรู้มา

แนวโน้มนี้ยังปรากฏให้เห็นในการสำรวจด้านความปลอดภัยอีกด้วย Veracode บริษัทด้านความปลอดภัยของแอปพลิเคชันได้ทำการสำรวจอย่างต่อเนื่องโดยให้ LLM กว่า 100 รุ่นแก้โจทย์เดียวกันและนำโค้ดที่สร้างขึ้นมาตรวจสอบด้วยการวิเคราะห์แบบสถิต (Static analysis) ผลปรากฏว่าในเงื่อนไขที่ไม่ได้ระบุคำสั่งเกี่ยวกับความปลอดภัยใดๆ อัตราการป้องกัน SQL Injection ได้สำเร็จอยู่ที่เฉลี่ย 83% และการเลือกวิธีการเข้ารหัสลับอยู่ที่ 87% (Veracode「2026 GenAI Code Security Report」) ซึ่งมาตรการที่มีรูปแบบตายตัว เช่น การใช้ Placeholder สำหรับ SQL หรือการหลีกเลี่ยงวิธีการเข้ารหัสแบบเก่า LLM สามารถเลือกใช้ได้เองในหลายกรณีแม้จะไม่ได้รับคำสั่งก็ตาม

อย่างไรก็ตาม รายงานฉบับเดียวกันระบุว่า แม้ความสามารถของโมเดลในการเขียนโค้ดที่ถูกต้องตามหลักไวยากรณ์จะเพิ่มขึ้น แต่อัตราการผ่านเกณฑ์ด้านความปลอดภัยโดยรวมกลับยังคงที่ โดยอัตราการผ่านเฉลี่ยอยู่ที่ 56% ซึ่งแทบไม่เปลี่ยนแปลงจากรายงานฉบับแรกที่ 55% ส่วนที่แก้ได้ด้วยรูปแบบมาตรฐานนั้นพัฒนาขึ้น แต่ส่วนที่ไม่สามารถแก้ด้วยวิธีนั้นกลับไม่มีการพัฒนา ซึ่ง "ส่วนที่ไม่สามารถแก้ด้วยวิธีนั้น" คือหัวข้อที่จะกล่าวถึงในส่วนถัดไป

ทำไม Non-functional Requirements ถึงยังต้องอาศัยมือมนุษย์?

ทำไม Non-functional Requirements ถึงยังต้องอาศัยมือมนุษย์?

ข้อกำหนดที่ไม่ใช่หน้าที่การทำงาน (Non-functional requirements) มีลักษณะคือคำตอบไม่ได้อยู่ในโค้ด ข้อบกพร่องจะปรากฏให้เห็นข้ามกระแสข้อมูล และไม่สามารถมองเห็นได้ในข้อมูลขนาดเล็ก เนื่องจาก LLM ไม่สามารถตรวจสอบด้วยตนเองผ่านการทำงานแบบไปกลับ (round-trip) ได้ง่ายนัก จึงยังคงจำเป็นต้องอาศัยมือของผู้กำหนดเกณฑ์มาตรฐานในการตัดสินใจ

คำตอบที่ถูกต้องไม่ได้อยู่แค่ในโค้ด

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

คุณ mizutani ได้เขียนไว้ใน โพสต์ถัดมา ว่า Claude ยังคงไม่ถนัดในการกำหนดจุดที่เหมาะสมสำหรับความต้องการที่ไม่ใช่เชิงฟังก์ชัน (Non-functional requirements) และยังไม่เก่งในการเสนอไอเดียว่าจะทำให้สำเร็จได้อย่างไร หากมนุษย์เป็นผู้กำหนดทิศทาง วางแนวทาง และตั้งเกณฑ์มาตรฐานไว้ให้ มันก็จะสามารถทำตามได้ในระดับหนึ่ง แต่ปัญหาคือการเตรียมข้อมูลไปถึงจุดนั้นยังคงเป็นเรื่องยาก ซึ่ง "จุดที่เหมาะสม" ในที่นี้ก็คือจุดตัดสินใจของ Trade-off นั่นเอง

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

ข้อบกพร่องที่ข้ามผ่านกระแสข้อมูลและบริบทมักถูกมองข้ามได้ง่าย

การสำรวจของ Veracode ที่กล่าวถึงในหัวข้อก่อนหน้านี้ยังมีข้อมูลต่อเนื่อง โดยอัตราการผ่านเกณฑ์ที่เคยสูงกว่า 80% ในด้าน SQL Injection และการเข้ารหัสลับ (Encryption) นั้น กลับลดลงเหลือเพียง 15% สำหรับ Cross-Site Scripting (XSS) และ 12% สำหรับ Log Injection รายงานระบุว่าสาเหตุที่ทั้งสองหัวข้อนี้มีอัตราการผ่านต่ำ เป็นเพราะการป้องกันขึ้นอยู่กับความเข้าใจในกระแสข้อมูล (Data Flow), บริบทของแอปพลิเคชัน และวิธีการที่ข้อมูลจากผู้ใช้เคลื่อนที่ผ่านระบบ แม้ว่าในการอัปเดตข้อมูลในเวลาต่อมา อัตราการผ่านเฉลี่ยจะเพิ่มขึ้นเป็น 62% แต่ผลลัพธ์ของ Log Injection ยังคงอยู่ในระดับเดิมแทบไม่เปลี่ยนแปลง (รายงานจาก CybrSec Media)

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

การแยกส่วนผู้เช่า (Tenant Isolation) และการอนุญาตสิทธิ์ (Authorization) ก็มีลักษณะเดียวกัน การเขียน API หนึ่งตัวให้ถูกต้อง กับการทำให้ API ทั้งหมดตัดสินใจเลือก Tenant จากเซสชันนั้นเป็นคนละประเด็นกัน LLM สามารถเขียนโค้ดที่สมเหตุสมผลได้ภายในขอบเขตของไฟล์ที่ได้รับมา แต่หากไม่ได้รับคำสั่งให้ตรวจสอบ มันจะไม่ยืนยันว่าข้อตกลงเดียวกันนั้นถูกรักษาไว้ทั่วทั้ง Repository หรือไม่ กรณีศึกษาที่เคยกล่าวถึงใน ตัวอย่างความล้มเหลวของ Vibe Coding ซึ่งเป็นการเปิดเผยข้อมูลโดยไม่ได้ตั้งค่าการควบคุมการเข้าถึงระดับแถว (Row-level Access Control) ในฐานข้อมูล ก็เกิดขึ้นในแอปพลิเคชันที่ฟังก์ชันการทำงานต่างๆ สามารถใช้งานได้ตามปกติ

ไม่สามารถมองเห็นปัญหาได้จากข้อมูลขนาดเล็ก

ข้อบกพร่องด้านประสิทธิภาพและต้นทุนมักไม่ปรากฏให้เห็นในปริมาณข้อมูลที่ใช้ระหว่างการพัฒนา งานวิจัยที่ชื่อว่า EffiBench ได้เปรียบเทียบเวลาในการประมวลผลของโค้ดที่เขียนโดย LLM จำนวน 42 รุ่น กับคำตอบที่เขียนโดยมนุษย์ซึ่งมีประสิทธิภาพสูงสุดในโจทย์ของ LeetCode ผลปรากฏว่า แม้แต่โมเดลเชิงพาณิชย์ชั้นนำในขณะนั้น ก็ยังมีเวลาประมวลผลเฉลี่ยช้ากว่าถึง 3.12 เท่า และในโจทย์ที่ห่างกันมากที่สุดนั้นช้ากว่าถึง 13.89 เท่า ทั้งหมดที่นำมาเปรียบเทียบล้วนเป็นคำตอบที่ผ่านการทดสอบแล้ว โค้ดที่ถูกต้องแต่ทำงานช้าจะสามารถผ่านการทดสอบฟังก์ชันไปได้

ในการพัฒนาที่ทำอยู่ก็เกิดเหตุการณ์คล้ายกัน API ของ Supabase (PostgREST) จะจำกัดจำนวนแถวที่ส่งคืนในการเรียกหนึ่งครั้งไว้ที่ 1,000 แถวโดยค่าเริ่มต้น และจะตัดส่วนที่เกินออกโดยไม่มีการแจ้งเตือนข้อผิดพลาด กระบวนการดึงรายการมาสรุปผลนั้นทำงานได้อย่างถูกต้องในขณะที่จำนวนข้อมูลยังมีน้อย แต่เมื่อนำไปใช้จริงและจำนวนแถวเกิน 1,000 แถว ระบบก็เริ่มตัดข้อมูลบางส่วนออกไปอย่างเงียบๆ สิ่งที่ทำให้รู้ตัวคือรายงานจากผู้ใช้งานว่ายอดเงินรวมไม่ถูกต้อง ไม่ว่าโค้ดจะเขียนโดยมนุษย์หรือ AI หากไม่มีใครกำหนดสมมติฐานเรื่องจำนวนข้อมูลที่เพิ่มขึ้น การทดสอบก็จะยังคงแสดงผลเป็นสีเขียวเสมอ

เมื่อมองในระดับทีมหรือองค์กร แนวโน้มก็เป็นไปในทิศทางเดียวกัน ผลสำรวจ DORA ของ Google รายงานว่า ในขณะที่การนำ AI มาใช้มีความสัมพันธ์เชิงบวกกับปริมาณงานที่พัฒนาได้ (Development Throughput) แต่กลับยังคงมีความสัมพันธ์เชิงลบกับเสถียรภาพในการนำไปใช้งานจริง (Deployment Stability) ซึ่งอธิบายได้ว่า หากปริมาณการเปลี่ยนแปลงเพิ่มขึ้นโดยไม่มีการทดสอบอัตโนมัติ การจัดการเวอร์ชันที่ครบถ้วน และกลไกการรับข้อมูลป้อนกลับที่รวดเร็ว ระบบก็จะขาดเสถียรภาพ (ประกาศจาก Google Cloud)

มนุษย์ควรตัดสินใจอย่างไรเพื่อให้ LLM ปฏิบัติตาม Non-functional Requirements?

มนุษย์ควรตัดสินใจอย่างไรเพื่อให้ LLM ปฏิบัติตาม Non-functional Requirements?

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

กำหนดจุดที่ยอมรับได้ด้วยตัวเลขและข้อห้าม

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

ตัวอย่างเช่น ให้เขียนดังนี้:

  • API รายการต้องตอบสนองภายใน 500 มิลลิวินาทีที่ p95 โดยจำกัดจำนวนรายการต่อครั้งไว้ที่ 100 รายการ และให้ใช้การแบ่งหน้า (Pagination)
  • Tenant ต้องมาจาก Session เท่านั้น ห้ามใช้ Tenant ID ที่รับมาจากเนื้อหาของ Request หรือ Query
  • การลองใหม่ (Retry) สำหรับ External API ให้ทำได้สูงสุด 3 ครั้งต่อ 1 Request ส่วน API ของ LLM ให้กำหนดขีดจำกัดของ Token ที่ใช้ใน 1 กระบวนการ
  • หากไฟล์ใดมีเกิน 500 บรรทัด ให้เสนอการแบ่งไฟล์ก่อนที่จะเพิ่มฟังก์ชันใหม่เข้าไป

ตัวเลขเหล่านี้ไม่จำเป็นต้องถูกต้องตั้งแต่แรก สิ่งสำคัญคือการที่สิ่งที่มนุษย์ได้ตัดสินใจโดยยอมรับการแลกเปลี่ยน (Trade-off) แล้วนั้น ถูกบันทึกไว้เป็นลายลักษณ์อักษร การเพิ่มเหตุผลที่ตัดสินใจไว้สัก 1 บรรทัด จะช่วยเป็นเบาะแสให้ LLM ตัดสินใจในสถานการณ์ที่ไม่คาดคิด และยังมีประโยชน์เมื่อต้องทบทวนตัวเลขเหล่านั้นในอีกครึ่งปีข้างหน้า เพื่อป้องกันไม่ให้ตกหล่นหัวข้อสำคัญ คุณควรใช้ ISO/IEC 25010 หรือรายการระดับความต้องการที่ไม่ใช่ฟังก์ชัน (Non-functional Requirements Grade) มาใช้เป็นตารางสำหรับตรวจสอบ

ทำให้เกณฑ์การตัดสินอยู่ในรูปแบบที่ตรวจสอบได้

เกณฑ์ที่กำหนดไว้หากเป็นเพียงข้อความเฉยๆ LLM ก็จะรายงานผลด้วยตัวเองเพียงว่า "น่าจะทำตามได้แล้ว" แล้วจบไป แต่หากเปลี่ยนให้อยู่ในรูปแบบที่ส่งผลลัพธ์ผ่าน/ไม่ผ่านออกมาโดยอัตโนมัติ LLM ก็จะสามารถแก้ไขข้อกำหนดที่ไม่ใช่หน้าที่การทำงาน (Non-functional requirements) ได้ในการโต้ตอบรอบเดียวกับที่แก้ไขข้อกำหนดหน้าที่การทำงาน (Functional requirements)

แนวคิดนี้มีชื่อเรียก ผู้เขียนหนังสือ 『Evolutionary Architecture』 (สถาปัตยกรรมเชิงวิวัฒนาการ) ได้เรียกกลไกที่ใช้ประเมินคุณลักษณะของสถาปัตยกรรมอย่างเป็นกลางว่า "Architectural Fitness Function" (Thoughtworks Technology Radar) ซึ่งเป็นแนวคิดที่กว้างขวาง ครอบคลุมไปถึงเมทริกซ์และการตรวจสอบติดตาม ไม่ใช่แค่เพียงการทดสอบหน่วย (Unit test) เท่านั้น

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

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

แยก "คำสั่งให้อ่าน" ออกจาก "กลไกบังคับใช้"

แม้จะสร้างกลไกการตัดสินใจขึ้นมา แต่หากปล่อยให้ LLM เป็นผู้ตัดสินใจว่าจะรันกลไกนั้นหรือไม่ ก็จะมีบางครั้งที่มันไม่ยอมรัน Best Practices ของ Claude Code ได้แบ่งคำสั่งออกเป็น 2 ประเภท ได้แก่ คำสั่งที่โมเดลจะอ่านและตัดสินใจว่าจะปฏิบัติตามหรือไม่ เช่น Prompt, CLAUDE.md และ Skill กับกลไกที่ทำงานทุกครั้งโดยไม่ขึ้นอยู่กับการตัดสินใจของโมเดล เช่น hook, deny rule และ CI โดยแนะนำว่าคำสั่งประเภทแรกนั้น "มักจะปฏิบัติตาม" แต่ไม่มีการรับประกัน ดังนั้นกฎที่ไม่อนุญาตให้มีข้อยกเว้นควรย้ายไปไว้ในประเภทหลัง

เกณฑ์ที่ไม่อนุญาตให้มีข้อยกเว้นควรวางไว้ในกลไกที่บังคับใช้ ส่วนแนวทางที่การตัดสินใจอาจเปลี่ยนไปตามสถานการณ์ควรวางไว้ในคำสั่งที่ให้อ่าน

ตำแหน่งที่วางตัวอย่างเกณฑ์ที่เหมาะสม
CLAUDE.md・Skill (คำสั่งที่ให้อ่าน)นโยบายการตั้งชื่อและการแบ่งส่วน, วิธีการเขียน Log, สถานการณ์ที่อนุญาตให้ใช้ Cache
hook・deny rule (บังคับใช้)ห้ามอ่านไฟล์ข้อมูลลับ, ห้ามแก้ไขไดเรกทอรีของผลลัพธ์ที่สร้างขึ้น, ให้รันการจัดรูปแบบทุกครั้งที่มีการแก้ไข
CI・Branch Protection (บังคับใช้)การตรวจสอบ Type, การทดสอบการอนุญาต (Authorization), การทดสอบประสิทธิภาพ, การตรวจสอบช่องโหว่ของ Dependency, ไม่สามารถ Merge ได้หากไม่มีการอนุมัติจากคน

ในสภาพแวดล้อมการพัฒนาบนเครื่องส่วนตัว การอ่านไฟล์ env, การเริ่มเซิร์ฟเวอร์สำหรับพัฒนา, และการสลับ Repository ที่แก้ไข ก็ไม่ได้เพียงแค่เขียนไว้ใน CLAUDE.md เท่านั้น แต่ยังใช้ hook ในการหยุดการทำงานด้วย ทั้งหมดนี้เป็นกฎที่หากเกิดความผิดพลาดเพียงครั้งเดียวจะส่งผลกระทบสูง และการที่ "ส่วนใหญ่ปฏิบัติตาม" นั้นไม่เพียงพอ ในทางกลับกัน หากเพิ่มกฎที่บังคับใช้มากเกินไป ก็จะเกิดปัญหาการตรวจจับที่ผิดพลาด (False Positive) และภาระในการบำรุงรักษาที่เพิ่มขึ้น เอกสารอย่างเป็นทางการยังระบุด้วยว่าควรเก็บกฎที่บังคับใช้ไว้ให้น้อย และปล่อยให้เป็นหน้าที่การตัดสินใจของ LLM ในเรื่องที่เป็นความชอบส่วนบุคคล สำหรับวิธีการตั้งค่า hook สามารถดูได้ที่ คู่มือการนำ Claude Code ไปใช้ในทีม

พิจารณาตามมุมมอง: ขอบเขตที่มอบหมายให้ LLM และขอบเขตที่มนุษย์ต้องรับผิดชอบ

พิจารณาตามมุมมอง: ขอบเขตที่มอบหมายให้ LLM และขอบเขตที่มนุษย์ต้องรับผิดชอบ

เมื่อแบ่งความต้องการเชิงคุณภาพ (Non-functional Requirements) ออกเป็น 5 ด้าน สิ่งที่ LLM สามารถรับหน้าที่ไปทำได้ง่ายคือ "การเขียนโปรแกรมให้เป็นไปตามเกณฑ์ที่กำหนด" ส่วนสิ่งที่มนุษย์ต้องเป็นผู้กำหนดคือ "การตั้งเกณฑ์" และ "การตัดสินใจในกรณีข้อยกเว้นที่อยู่นอกเหนือเกณฑ์"

ด้านงานที่ LLM รับหน้าที่ได้ง่ายสิ่งที่มนุษย์ต้องกำหนดตัวอย่างกลไกการตรวจสอบ
ความปลอดภัย (Security)การเขียนโค้ดตรวจสอบอินพุตหรือการทำ Escape, การใช้ไลบรารีเข้ารหัสมาตรฐานการจำแนกประเภทข้อมูลที่ต้องปกป้อง, รูปแบบการอนุญาต (Authorization Model), ความเสี่ยงที่ยอมรับได้การทดสอบการอนุญาตใน Tenant อื่น, SAST, การตรวจสอบช่องโหว่ของ Dependency
ประสิทธิภาพ (Performance)การเพิ่ม Index, การแก้ไขปัญหา N+1 Query, การเขียนโค้ดทำ Paginationเป้าหมายเวลาในการตอบสนอง, ปริมาณข้อมูลและจำนวนการเข้าถึงที่คาดการณ์การทดสอบโหลดด้วยปริมาณข้อมูลใกล้เคียงกับระบบจริง, การตรวจสอบค่า p95
ต้นทุน (Cost)การเขียนโค้ดเพื่อทำ Cache หรือการรวมกลุ่มการประมวลผลงบประมาณรายเดือน, ขีดจำกัดต่อหนึ่งคำขอ (Request)การทดสอบจำนวนครั้งในการเรียกใช้ External API, การแจ้งเตือนเมื่อยอดใช้งานเกินกำหนด
ความสามารถในการบำรุงรักษา (Maintainability)การแยกส่วนประกอบ (Refactoring), การทำให้ชื่อเรียกเป็นมาตรฐาน, การเพิ่ม Testวิธีการแบ่ง Layer, ทิศทางของ Dependency ที่อนุญาต, ความซับซ้อนที่ยอมรับได้การตรวจสอบจำนวนบรรทัดและทิศทางของ Dependency, การตรวจสอบ Type
ความพร้อมใช้งานและการดำเนินงาน (Availability/Operations)การเขียนโค้ดจัดการ Retry หรือ Timeout, การส่ง Logระยะเวลาที่ระบบหยุดทำงานได้, เป้าหมายในการกู้คืนระบบ, ผู้รับผิดชอบเมื่อเกิดเหตุขัดข้องการทดสอบโดยจำลองเหตุการณ์ขัดข้อง, การเฝ้าระวังและการแจ้งเตือน

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

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

ความผิดพลาดที่พบบ่อยและวิธีหลีกเลี่ยง

ความผิดพลาดที่พบบ่อยและวิธีหลีกเลี่ยง

ความผิดพลาดในการส่งมอบ "ข้อกำหนดที่ไม่ใช่หน้าที่การทำงาน" (Non-functional Requirements) ให้กับ LLM มักเกิดจากการส่งมอบโดยที่เกณฑ์ยังมีความคลุมเครือ และการเข้าใจผิดว่าการที่ผ่านการทดสอบ (Test) แล้วหมายถึงการผ่านเกณฑ์ในส่วนที่ไม่ใช่หน้าที่การทำงานนั้น

การเขียนเพียงว่า "ให้ระวังเรื่องความปลอดภัย" แล้วจบไป

การเขียนกำกับไว้ใน CLAUDE.md ว่า "ให้ระมัดระวังเรื่องความปลอดภัยให้ดี" หรือ "ให้คำนึงถึงประสิทธิภาพด้วย" แล้วคิดว่านั่นคือการสื่อสารความต้องการเชิงหน้าที่ (Non-functional requirements) ครบถ้วนแล้ว ถือเป็นความผิดพลาดที่พบบ่อยที่สุด LLM จะไม่เข้าใจว่าต้องทำถึงระดับไหนจึงจะถือว่าผ่านเกณฑ์ ผลที่ตามมาคือมาตรการที่มองเห็นได้ชัดเจน (เช่น การทำ Input Escaping หรือการจัดการ Exception) อาจเพิ่มขึ้น แต่ส่วนที่มองเห็นได้ยากอย่างความสอดคล้องของการอนุญาต (Authorization consistency) หรือการเตรียมพร้อมรับมือกับปริมาณข้อมูลจะยังคงเป็นปัญหาอยู่

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

วิธีหลีกเลี่ยงคือการเปลี่ยนจาก "คำเตือน" ให้เป็น "เกณฑ์มาตรฐาน" โดยเขียนให้เป็นประโยคที่มนุษย์สามารถอ่านและตัดสินได้ว่าทำตามหรือไม่ เช่น "ให้กำหนด Tenant จาก Session" หรือ "ค่าที่จะแสดงบนหน้าจอต้องผ่านการทำ Template Escaping เสมอ" จากนั้นให้เตรียมการทดสอบหรือการตรวจสอบเพื่อประเมินประโยคเหล่านั้นไว้ 1 รายการ หากการเพิ่มความเข้มงวดด้วยคำว่า "สำคัญ" หรือ "ต้องทำ" ลงในบรรทัดหนึ่งของ CLAUDE.md แล้วยังไม่สามารถทำให้ปฏิบัติตามได้ เอกสารอย่างเป็นทางการก็ได้ระบุไว้ว่า นั่นคือสัญญาณที่บอกว่าควรเปลี่ยนไปใช้กลไกบังคับแทน

การถือว่าการผ่านการทดสอบคือการผ่านเกณฑ์ Non-functional

เมื่อ LLM รายงานว่า "ผ่านการทดสอบทั้งหมดแล้ว" เรามักจะรู้สึกว่าการเปลี่ยนแปลงทั้งหมดนั้นถูกต้อง แต่การทดสอบเหล่านั้นส่วนใหญ่เป็นเพียงการทดสอบความต้องการเชิงฟังก์ชัน (functional requirements) เท่านั้น โค้ดที่ทำงานได้ถูกต้องแต่ช้า หรือโค้ดที่ทำงานได้ถูกต้องแต่กลับดึงข้อมูลของเทนแนนท์อื่นออกมาได้ ก็สามารถผ่านการทดสอบฟังก์ชันได้เช่นกัน

ความเชื่อมั่นที่ผิดพลาดนี้ยังส่งผลให้มนุษย์มีความมั่นใจในตัวเองสูงเกินไปอีกด้วย งานวิจัยจากมหาวิทยาลัยสแตนฟอร์ดพบว่า ผู้เข้าร่วมที่ใช้ AI ช่วยแก้โจทย์มีแนวโน้มที่จะเขียนโค้ดที่ไม่ปลอดภัยมากกว่าผู้ที่ไม่ได้ใช้ และยังมีความมั่นใจสูงว่าโค้ดของตนนั้นปลอดภัย (Perry และคณะ "Do Users Write More Insecure Code with AI Assistants?") โค้ดที่ AI เขียนขึ้นมักจะดู "น่าเชื่อถือ" ในสายตาของผู้อ่าน

วิธีหลีกเลี่ยงปัญหานี้มี 2 ประการ ประการแรกคือ การให้ AI ระบุ "เกณฑ์การตัดสินที่ใช้ทดสอบ" มาในรายงานความสำเร็จด้วย หากให้ AI แนบคำสั่งที่ใช้รันและผลลัพธ์ที่ได้มาด้วย เราจะสังเกตเห็นได้ทันทีว่าการทดสอบแบบไม่ใช่เชิงฟังก์ชัน (non-functional tests) ยังไม่ได้ถูกดำเนินการ ประการที่สองคือ การนำการทดสอบแบบไม่ใช่เชิงฟังก์ชันไปรวมไว้ใน CI เพื่อให้แน่ใจว่าหากไม่ผ่านเกณฑ์จะไม่สามารถรวมโค้ด (merge) ได้ วิธีนี้จะช่วยให้มนุษย์ไม่ต้องคอยตัดสินใจทุกครั้งว่าควรเชื่อรายงานของ AI หรือไม่ สำหรับภาพรวมที่ครอบคลุมถึงการออกแบบสิทธิ์ การรีวิว และการหยุดการทำงาน สามารถดูได้ที่ รายการตรวจสอบการดำเนินงานการ์ดเรลของ AI Agent

คำถามที่พบบ่อยเกี่ยวกับ Non-functional Requirements ในการเขียนโค้ดด้วย AI

คำถามที่พบบ่อยเกี่ยวกับ Non-functional Requirements ในการเขียนโค้ดด้วย AI

ตอบ 3 คำถามที่พบบ่อยเมื่อมอบหมายการเขียนโค้ด (Implementation) ให้ AI

Q1: มนุษย์จำเป็นต้องตรวจสอบความปลอดภัยของโค้ดที่ AI เขียนทั้งหมดหรือไม่?

ไม่จำเป็นครับ คุณไม่จำเป็นต้องตรวจสอบทุกส่วนด้วยความละเอียดเท่ากัน ในส่วนที่สามารถป้องกันได้ด้วย Type (เช่น การประกอบคำสั่ง SQL, การใช้งานไลบรารีเข้ารหัส) ให้ปล่อยเป็นหน้าที่ของ SAST และการตรวจสอบ Dependency ส่วนตัวคุณให้โฟกัสไปที่จุดที่มีการตัดสินใจเรื่อง Tenant หรือสิทธิ์การเข้าถึง และจุดที่มีการแสดงผลข้อมูลจากผู้ใช้งานบนหน้าจอหรือใน Log เท่านั้นครับ

Q2: สามารถให้ AI เขียนนิยามของ Non-functional Requirements เองได้หรือไม่?

มันมีประโยชน์ในฐานะร่างแรก หากคุณระบุประเภทของระบบ ขนาดของผู้ใช้งาน และประเภทของข้อมูลที่จะจัดการ AI จะช่วยลิสต์มุมมองที่ควรตรวจสอบและตัวเลขสมมติให้ตามหัวข้อต่างๆ เช่น เกรดของความต้องการที่ไม่ใช่หน้าที่หลัก (Non-functional Requirements) บางครั้งมันยังช่วยชี้ให้เห็นมุมมองที่เราอาจนึกไม่ถึง เช่น ปริมาณข้อมูลที่จะย้ายหรือจำนวนรุ่นของข้อมูลสำรอง (Backup)

อย่างไรก็ตาม การตัดสินใจว่าจะใช้ตัวเลขเหล่านั้นเลยหรือไม่นั้นเป็นอีกเรื่องหนึ่ง ตัวอย่างเช่น หากกำหนดเป้าหมายระยะเวลาการกู้คืน (Recovery Time Objective) ไว้ที่ "ภายใน 1 ชั่วโมง" หรือ "ภายในวันทำการถัดไป" โครงสร้างที่จำเป็นและค่าใช้จ่ายในการดำเนินงานจะแตกต่างกันอย่างมาก การจะตัดสินว่าแบบไหนถูกต้องนั้น จำเป็นต้องเปรียบเทียบระหว่างสิ่งที่ต้องสูญเสียหากระบบหยุดทำงานกับงบประมาณที่สามารถจ่ายได้ ดังนั้น ควรใช้ข้อเสนอของ AI เพื่อตรวจสอบความครบถ้วน และให้ผู้รับผิดชอบด้านธุรกิจและการดำเนินงานซึ่งสามารถอธิบายการเปรียบเทียบดังกล่าวเป็นผู้กำหนดตัวเลขที่แน่นอนครับ

Q3: หากโมเดลมีการอัปเดตใหม่ จะสามารถมอบหมายงานด้าน Non-functional Requirements ให้ AI ทั้งหมดได้หรือไม่?

ในส่วนของการนำไปใช้งาน ขอบเขตที่สามารถมอบหมายให้ทำได้นั้นน่าจะกว้างขึ้นเรื่อยๆ แม้แต่ในการอัปเดตล่าสุดของ Veracode อัตราการผ่านโดยเฉลี่ยก็เพิ่มสูงขึ้น และมีการปรับปรุงแก้ไขข้อบกพร่องประเภทที่สามารถป้องกันได้ด้วยการกำหนด "Type" อย่างต่อเนื่อง ในทางกลับกัน จุดสมดุลว่าจะต้องรวดเร็วแค่ไหน ราคาถูกเพียงใด และต้องปกป้องถึงระดับใดนั้น ไม่ได้ถูกกำหนดโดยประสิทธิภาพของโมเดล แต่ถูกกำหนดโดยเงื่อนไขทางธุรกิจ ดังนั้นแม้จะมีการเปลี่ยนโมเดลไป แต่หน้าที่นี้ก็จะยังคงอยู่กับฝั่งคนทำงาน หากเราสามารถเก็บรักษาเกณฑ์การตัดสินไว้ในรูปแบบที่สามารถนำมาประเมินผลได้ ก็จะยังคงใช้งานได้ทันทีแม้ในวันที่เปลี่ยนไปใช้โมเดลใหม่ก็ตาม

บทสรุป

บทสรุป

LLM ที่สามารถนำไปใช้งานตามความต้องการเชิงฟังก์ชัน (Functional Requirements) ได้ค่อนข้างดีนั้น เป็นเพราะสามารถตรวจสอบคำตอบที่ถูกต้องได้จากอินพุตและเอาต์พุต อีกทั้งยังมักจะแก้ไขได้ด้วยรูปแบบที่เป็นที่รู้จักกันดี ส่วนเหตุผลที่ความต้องการที่ไม่ใช่เชิงฟังก์ชัน (Non-functional Requirements) ยังคงต้องอาศัยมือมนุษย์อยู่นั้น เป็นเพราะคำตอบที่ถูกต้องไม่ได้อยู่ในโค้ด แต่อยู่ในเงื่อนไขทางธุรกิจและการดำเนินงาน อีกทั้งข้อบกพร่องยังอาจกระจายอยู่ข้ามกระแสข้อมูลและจะปรากฏให้เห็นก็ต่อเมื่อใช้ข้อมูลขนาดใหญ่หรือในสถานการณ์การใช้งานจริงเท่านั้น

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

สิ่งที่มนุษย์ต้องรับผิดชอบไม่ใช่การเฝ้าดูรายละเอียดการใช้งานทั้งหมด แต่คือการกำหนดจุดที่ยอมรับได้ด้วยตัวเลขและข้อห้ามต่างๆ ทำให้มันอยู่ในรูปแบบที่สามารถตัดสินได้ และบังคับใช้ส่วนที่ไม่สามารถยกเว้นได้ด้วย hook หรือ CI หากเตรียมการได้ถึงขั้นนี้ ขอบเขตที่สามารถมอบหมายให้ LLM จัดการการใช้งานความต้องการที่ไม่ใช่เชิงฟังก์ชันได้ก็จะกว้างขึ้น เช่นเดียวกับความต้องการเชิงฟังก์ชัน เริ่มต้นจากการเลือกหัวข้อที่คุณต้องชี้แนะซ้ำๆ ในการรีวิวโค้ดสัก 1 หัวข้อ แล้วเปลี่ยนให้เป็นประโยคเกณฑ์มาตรฐานและการทดสอบเพื่อตัดสิน แนวคิดในการป้องกันความล้มเหลวของ AI Agent ด้วยกลไกนั้น ได้อธิบายไว้อย่างละเอียดใน Harness Engineering คืออะไร?

บริษัทของเราให้การสนับสนุนทั้งวิธีการพัฒนาโดยใช้ Coding Agent และการสร้างเกณฑ์สำหรับความต้องการที่ไม่ใช่เชิงฟังก์ชัน หากต้องการปรึกษา สามารถติดต่อได้ที่ บริการสนับสนุน AI/DX

ผู้เขียน・ผู้ตรวจสอบ

Yusuke Ishihara

Yusuke Ishihara

เริ่มเขียนโปรแกรมตั้งแต่อายุ 13 ปี ด้วย MSX หลังจบการศึกษาจากมหาวิทยาลัย Musashi ได้ทำงานพัฒนาระบบขนาดใหญ่ รวมถึงระบบหลักของสายการบิน และโครงสร้าง Windows Server Hosting/VPS แห่งแรกของญี่ปุ่น ร่วมก่อตั้ง Site Engine Inc. ในปี 2008 ก่อตั้ง Unimon Inc. ในปี 2010 และ Enison Inc. ในปี 2025 นำทีมพัฒนาระบบธุรกิจ การประมวลผลภาษาธรรมชาติ และแพลตฟอร์ม ปัจจุบันมุ่งเน้นการพัฒนาผลิตภัณฑ์และการส่งเสริม AI/DX โดยใช้ generative AI และ Large Language Models (LLM)