ข้ามไปยังเนื้อหาหลัก
ProgrammerThailand

AI & Vibe Coding

Debug กับ AI อย่างเป็นระบบ: จาก Error ถึงสาเหตุ

Debug กับ AI อย่างเป็นระบบ: จาก Error ถึงสาเหตุ

หลายคนใช้ AI debug แบบนี้: คัดลอก error บรรทัดเดียวไปวาง ได้คำแนะนำมา ลองแก้ ไม่หาย วางใหม่ ได้คำแนะนำใหม่ แก้ซ้อนไปเรื่อย ๆ จนโค้ดเละกว่าเดิม ปัญหาไม่ได้อยู่ที่ AI ไม่เก่ง แต่อยู่ที่กระบวนการไม่เป็นระบบ บทความนี้จะเสนอวิธี debug กับ AI แบบนักพัฒนามืออาชีพ คือส่ง error เต็มพร้อมบริบท ขอให้ AI เสนอสมมติฐานหลายข้อแทนคำตอบเดียว แล้วทดลองพิสูจน์ทีละข้อ พร้อมตัวอย่างการอ่าน stack trace ของ PHP/Yii2 จริง

ทำไมการวาง error บรรทัดเดียวถึงไม่พอ

Error message บอกแค่ "อาการ" ไม่ได้บอก "สาเหตุ" ข้อความเดียวกันอย่าง Trying to access array offset on value of type null อาจเกิดจากข้อมูลในฐานข้อมูลไม่มี query ผิดเงื่อนไข API ภายนอกตอบกลับไม่ครบ หรือ session หมดอายุ เมื่อ AI ได้ข้อมูลน้อย มันจะเดาสาเหตุที่พบบ่อยที่สุดในโลก ซึ่งอาจไม่ใช่ของระบบเรา และมักตอบด้วยความมั่นใจสูงเสมอ

อาการที่บอกว่ากำลัง debug ผิดทาง:

  • แก้ไปแล้วสามรอบ error เปลี่ยนข้อความไปเรื่อย ๆ แต่ไม่หาย
  • ใส่ ?? null หรือ @ กลบ error จนหายไป แต่ไม่รู้ว่าทำไมค่าถึงเป็น null ตั้งแต่แรก
  • แก้หลายจุดพร้อมกันจนไม่รู้ว่าจุดไหนที่ได้ผล

ขั้นที่ 1: รวบรวมข้อมูลให้ครบก่อนถาม

ก่อนเปิดแชตกับ AI ให้เตรียมข้อมูลเหล่านี้:

  • Error เต็มพร้อม stack trace ไม่ตัดทอน ใน Yii2 ดูได้จาก runtime/logs/app.log หรือหน้า debug ในโหมด dev
  • โค้ดที่เกี่ยวข้อง อย่างน้อยไฟล์และบรรทัดที่อยู่ใน trace ส่วนที่เป็นโค้ดของเรา ไม่ใช่โค้ดใน vendor/
  • ขั้นตอนทำให้เกิดซ้ำ URL, input, ผู้ใช้ประเภทไหน เกิดทุกครั้งหรือบางครั้ง
  • สภาพแวดล้อม เวอร์ชัน PHP, Yii2, PostgreSQL และเกิดที่ dev หรือ production
  • สิ่งที่เปลี่ยนล่าสุด ดูจาก git log ว่าก่อนหน้านี้เคยทำงานได้หรือไม่ และ deploy อะไรไปล่าสุด
  • สิ่งที่ลองแล้ว เพื่อไม่ให้ AI เสนอสิ่งเดิมซ้ำ

ข้อควรระวัง: ก่อนวาง log ให้ลบข้อมูลอ่อนไหวออก เช่น รหัสผ่าน token ข้อมูลส่วนบุคคลของลูกค้า และ connection string ที่มี credential

ตัวอย่าง: อ่าน Stack Trace ของ PHP

สมมติเจอ error นี้ใน log ของ Yii2:

PHP Warning 'yii\base\ErrorException' with message
'Trying to access array offset on value of type null'

in /var/www/app/services/InvoiceService.php:42

Stack trace:
#0 /var/www/app/services/InvoiceService.php(42): yii\base\ErrorHandler->handleError(2, 'Trying to acces...', '/var/www/app/se...', 42)
#1 /var/www/app/controllers/InvoiceController.php(58): app\services\InvoiceService->buildSummary(1532)
#2 [internal function]: app\controllers\InvoiceController->actionView('1532')
#3 /var/www/app/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#4 /var/www/app/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams(Array)
...

วิธีอ่าน:

  1. บรรทัด in ...InvoiceService.php:42 คือจุดที่ error เกิดขึ้นจริง
  2. stack trace อ่านจากบนลงล่างคือจาก "ลึกสุด" ไปหา "ต้นทาง" frame #0 คือที่เกิด frame ถัดไปคือผู้เรียก
  3. frame #1 บอกว่า InvoiceController บรรทัด 58 เรียก buildSummary(1532) และ frame #2 บอกว่ามาจาก actionView('1532') ทำให้เรารู้ว่า invoice id 1532 คือกรณีที่ทำให้เกิดปัญหา
  4. frame ที่อยู่ใน vendor/ เป็นกลไกของ framework มักไม่ใช่ต้นเหตุ ให้โฟกัสที่โค้ดของเราก่อน

และนี่คือโค้ดบรรทัดที่เกี่ยวข้อง:

public function buildSummary(int $invoiceId): array
{
    $row = (new \yii\db\Query())
        ->from('invoice i')
        ->leftJoin('customer c', 'c.id = i.customer_id')
        ->select(['i.*', 'c.name AS customer_name', 'c.address'])
        ->where(['i.id' => $invoiceId])
        ->one();

    $address = json_decode($row['address'], true);
    return [
        'customer' => $row['customer_name'],
        'province' => $address['province'],   // บรรทัด 42
    ];
}

ขั้นที่ 2: ขอสมมติฐานหลายข้อ ไม่ใช่คำตอบเดียว

แทนที่จะถามว่า "แก้ยังไง" ให้ขอรายการสมมติฐานที่เป็นไปได้ พร้อมวิธีพิสูจน์แต่ละข้อ วิธีนี้บังคับให้ AI คิดกว้างขึ้น และให้เราเป็นคนตัดสินจากหลักฐาน

ไม่ดี:
Trying to access array offset on value of type null แก้ยังไง

ดี:
ฉันเจอ error นี้ใน Yii2 (PHP 8.x, PostgreSQL) เฉพาะบาง invoice เช่น id 1532
invoice อื่นส่วนใหญ่แสดงผลได้ปกติ เริ่มเกิดหลัง deploy เมื่อวาน
[แนบ stack trace เต็ม]
[แนบโค้ด buildSummary]
สิ่งที่ลองแล้ว: เปิดหน้า invoice อื่น ๆ ได้ปกติ

ขอ:
1. สมมติฐานที่เป็นไปได้ 3-5 ข้อ เรียงตามความน่าจะเป็น
2. แต่ละข้อ บอกวิธีพิสูจน์ที่เร็วที่สุด (query, log, var_dump) โดยยังไม่ต้องแก้โค้ด
3. ยังไม่ต้องเสนอวิธีแก้ จนกว่าเราจะรู้สาเหตุ

คำตอบที่ได้อาจเป็นประมาณนี้:

  1. c.address เป็น NULL สำหรับลูกค้ารายนี้ ทำให้ json_decode ได้ null (ตรวจด้วย query ดูข้อมูล)
  2. ค่า address เป็น JSON ที่ไม่ถูกต้อง json_decode จึงคืน null (ตรวจด้วย json_last_error_msg())
  3. invoice ไม่มี customer ที่ตรงกัน เพราะใช้ leftJoin ทำให้ทุกคอลัมน์ของ customer เป็น null
  4. หลัง deploy มี migration เปลี่ยนรูปแบบ address เช่น ย้าย province ไปอยู่ใน key อื่น

ขั้นที่ 3: ทดลองพิสูจน์ทีละข้อ

ตอนนี้เรามีรายการสิ่งที่ต้องตรวจ ให้เริ่มจากข้อที่ตรวจได้เร็วและถูกที่สุด และอย่าแก้โค้ดจนกว่าจะพิสูจน์ได้

-- ตรวจสมมติฐานข้อ 1 และ 3 ในคำสั่งเดียว
SELECT i.id, i.customer_id, c.id AS c_id, c.address
FROM invoice i
LEFT JOIN customer c ON c.id = i.customer_id
WHERE i.id = 1532;

สมมติผลออกมาว่า c_id เป็น NULL แปลว่าลูกค้ารายนี้ถูกลบไปแล้ว (สมมติฐานข้อ 3) ขั้นต่อไปคือถามว่า "ทำไม" ต่อ เช่น ตรวจ git log ว่า deploy เมื่อวานมีการเปลี่ยนโค้ดลบลูกค้าหรือไม่ แล้วนับว่ามี invoice ที่ customer หายไปกี่รายการ:

SELECT count(*)
FROM invoice i
LEFT JOIN customer c ON c.id = i.customer_id
WHERE c.id IS NULL;

กติกาของการทดลอง:

  • เปลี่ยนตัวแปรทีละอย่าง แล้วดูผล ถ้าแก้สองจุดพร้อมกันจะไม่รู้ว่าจุดไหนได้ผล
  • จดผลการทดลองแต่ละข้อ แล้วส่งกลับไปให้ AI เป็นบริบทในรอบถัดไป
  • ถ้าทุกสมมติฐานตกไปหมด ให้ขอสมมติฐานชุดใหม่พร้อมแนบผลการทดลองทั้งหมด
  • ถ้ารู้ว่าเวอร์ชันก่อนหน้าทำงานได้ git bisect ช่วยหา commit ที่ทำให้พังได้อย่างเป็นระบบ

ขั้นที่ 4: แก้ที่สาเหตุ แล้วเขียนเทสต์กันกลับมา

เมื่อรู้สาเหตุแล้ว ค่อยขอให้ AI เสนอวิธีแก้ ซึ่งควรแก้ทั้งต้นเหตุและอาการ เช่น ป้องกันการลบลูกค้าที่ยังมี invoice (ด้วย foreign key หรือ soft delete) และทำให้ buildSummary รับมือกับข้อมูลที่ไม่ครบได้อย่างชัดเจน:

$row = (new \yii\db\Query())
    // ... เหมือนเดิม
    ->one();

if ($row === false) {
    throw new \yii\web\NotFoundHttpException('ไม่พบใบแจ้งหนี้');
}

$address = $row['address'] !== null
    ? json_decode($row['address'], true, 512, JSON_THROW_ON_ERROR)
    : [];

return [
    'customer' => $row['customer_name'] ?? '(ไม่พบข้อมูลลูกค้า)',
    'province' => $address['province'] ?? '',
];

สังเกตว่าเราใช้ ?? หลังจากรู้แล้วว่าข้อมูลขาดได้จริงและตัดสินใจว่าควรแสดงอะไร ไม่ใช่ใช้กลบ error ตั้งแต่แรก และอย่าลืมใช้ Html::encode() เมื่อนำค่าเหล่านี้ไปแสดงใน view

สุดท้าย เขียนเทสต์สำหรับกรณีที่เพิ่งเจอ (invoice ที่ไม่มี customer และ address เป็น null) เพื่อไม่ให้บั๊กเดิมกลับมาอีก

สรุป

  • error message คืออาการ ต้องส่ง stack trace เต็ม โค้ด ขั้นตอนทำซ้ำ และสิ่งที่เปลี่ยนล่าสุดให้ AI
  • อ่าน stack trace จาก frame ที่เกิด error ย้อนไปหาผู้เรียก และโฟกัสที่โค้ดของเราก่อน vendor
  • ขอสมมติฐานหลายข้อพร้อมวิธีพิสูจน์ แทนการขอคำตอบเดียว
  • ทดลองทีละข้อ เปลี่ยนทีละอย่าง จดผล และอย่าแก้โค้ดจนกว่าจะรู้สาเหตุ
  • แก้ที่ต้นเหตุ ไม่ใช่กลบอาการ แล้วเขียนเทสต์กันบั๊กกลับมา

ลองทำ

นึกถึงบั๊กล่าสุดที่คุณเจอในโปรเจกต์ (หรือสร้างขึ้นมาเองโดยลบข้อมูลที่ถูกอ้างอิงในฐานข้อมูล dev) เตรียมข้อมูลตามรายการในขั้นที่ 1 ให้ครบ แล้วใช้ prompt แบบ "ดี" เพื่อขอสมมติฐาน 3-5 ข้อ ทดลองพิสูจน์ทีละข้อ แล้วจดลงตารางสามคอลัมน์: สมมติฐาน วิธีพิสูจน์ ผลลัพธ์ เมื่อพบสาเหตุแล้วให้เปรียบเทียบว่าสมมติฐานอันดับแรกของ AI ถูกหรือไม่ และขั้นไหนของกระบวนการที่ช่วยคุณได้มากที่สุด

ติดปัญหาตอนทำตาม? ถามในชุมชน

ความคิดเห็น (0)

ยังไม่มีความคิดเห็น — เริ่มคุยเป็นคนแรก ถามสิ่งที่สงสัย หรือแชร์ประสบการณ์ของคุณ