บทเรียนนี้เน้นการเปลี่ยนมุมมองจากการเขียนโปรแกรมแบบ "ให้รันได้" ไปสู่การเขียนโปรแกรมแบบ "พร้อมดูแลและขยายต่อได้" ผู้เรียนจะเห็นว่า Bug ไม่ได้เกิดจากการเขียนโค้ดผิดเพียงอย่างเดียว แต่อาจเกิดตั้งแต่การเข้าใจความต้องการผิด การออกแบบไม่ชัดเจน การทดสอบไม่ครอบคลุม หรือการแก้ไขภายหลังโดยไม่มีระบบป้องกันผลกระทบ
คำสำคัญของบทเรียน
การเขียนโปรแกรมขั้นสูง (Advanced Programming) คือการพัฒนาโปรแกรมโดยคำนึงถึงคุณภาพของระบบทั้งวงจร ไม่ใช่เพียงเขียนคำสั่งให้ได้ผลลัพธ์เฉพาะหน้า โปรแกรมที่ดีควรมีคุณสมบัติดังนี้
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#83a598',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
mindmap
root((Software Quality
คุณภาพซอฟต์แวร์))
Correctness
ความถูกต้อง
Expected result
ผลลัพธ์ตรงโจทย์
Requirement match
ตรงตามข้อกำหนด
Reliability
ความน่าเชื่อถือ
Stable behavior
ทำงานสม่ำเสมอ
Error tolerance
รับมือข้อผิดพลาด
Maintainability
ดูแลรักษาได้
Readable code
อ่านง่าย
Clear modules
แบ่งส่วนชัดเจน
Security
ความปลอดภัย
Input validation
ตรวจข้อมูลเข้า
Protect data
ป้องกันข้อมูลสำคัญ
Scalability
ขยายต่อได้
Add features
เพิ่มฟีเจอร์
More data
รองรับข้อมูลเพิ่ม
| ประเด็น | โค้ดที่รันได้เท่านั้น | โค้ดที่มีคุณภาพ |
|---|---|---|
| การตั้งชื่อ | ใช้ชื่อสั้น เช่น x, tmp, a1 |
ใช้ชื่อสื่อความหมาย เช่น totalPrice, studentCount |
| การตรวจสอบข้อมูล | เชื่อว่าผู้ใช้กรอกถูกเสมอ | ตรวจข้อมูลว่าง ผิดชนิด และเกินขอบเขตก่อนประมวลผล |
| โครงสร้าง | รวมทุกอย่างในฟังก์ชันเดียว | แยกฟังก์ชันตามหน้าที่ |
| การแก้ไข | แก้จุดหนึ่งกระทบหลายจุด | แก้ไขเฉพาะส่วนได้ง่าย |
| การทดสอบ | ทดลองรันด้วยตนเองแบบไม่เป็นระบบ | มี test case ที่ตรวจซ้ำได้ |
การป้องกัน Bug พัฒนามาพร้อมกับวิธีการสร้างซอฟต์แวร์ ตั้งแต่ยุคที่เน้นให้โปรแกรมทำงานได้ ไปสู่ยุคที่เน้นคุณภาพ กระบวนการ เครื่องมืออัตโนมัติ และการทำงานร่วมกันของทีม
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#8ec07c',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart LR
subgraph E1[Early Programming Era
ยุคเริ่มต้นการเขียนโปรแกรม]
A[Run the program
เน้นให้โปรแกรมรันได้]
end
subgraph E2[Structured Era
ยุคโครงสร้างโปรแกรม]
B[Modular design
แบ่งโปรแกรมเป็นส่วน]
end
subgraph E3[Quality Era
ยุคคุณภาพซอฟต์แวร์]
C[Testing and review
ทดสอบและตรวจโค้ด]
end
subgraph E4[Modern DevOps Era
ยุคเครื่องมืออัตโนมัติ]
D[Static analysis and CI
ตรวจอัตโนมัติและส่งมอบต่อเนื่อง]
end
A --> B --> C --> D
SDLC (Software Development Life Cycle) คือวงจรการพัฒนาซอฟต์แวร์ตั้งแต่การวิเคราะห์ความต้องการจนถึงการบำรุงรักษา Bug สามารถเกิดขึ้นได้ทุกช่วง หากเกิดตั้งแต่ต้นทาง เช่น เข้าใจ requirement ผิด ต้นทุนการแก้ไขจะสูงขึ้นเพราะความผิดพลาดจะส่งผลต่อการออกแบบ โค้ด และการทดสอบ
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#fe8019',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart LR
A[Requirement Analysis
วิเคราะห์ความต้องการ] --> B[Design
ออกแบบระบบ]
B --> C[Implementation
เขียนโปรแกรม]
C --> D[Testing
ทดสอบ]
D --> E[Deployment
ส่งมอบ]
E --> F[Maintenance
บำรุงรักษา]
A -. Risk .-> A1[Wrong requirement
เข้าใจโจทย์ผิด]
B -. Risk .-> B1[Poor design
ออกแบบไม่เหมาะสม]
C -. Risk .-> C1[Logic error
ตรรกะผิด]
D -. Risk .-> D1[Missing test case
ทดสอบไม่ครอบคลุม]
F -. Risk .-> F1[Regression bug
แก้แล้วกระทบของเดิม]
| ขั้นตอน | ตัวอย่าง Bug | แนวทางป้องกัน |
|---|---|---|
| Requirement | ตีความเงื่อนไขส่วนลดไม่ครบ | เขียนตัวอย่าง input/output และยืนยัน requirement |
| Design | เลือกโครงสร้างข้อมูลไม่เหมาะ | วาด flow และโครงสร้างข้อมูลก่อนเขียนโค้ด |
| Implementation | เงื่อนไข if ไม่ครอบคลุมค่าขอบเขต |
ตรวจ boundary case และใช้ code review |
| Testing | ทดสอบเฉพาะข้อมูลปกติ | เพิ่ม normal, edge และ invalid case |
| Maintenance | แก้โค้ดแล้วฟีเจอร์เดิมเสีย | ใช้ regression test และ version control |
แนวคิดสำคัญของ Bug Prevention คือ ยิ่งพบ Bug ช้า ต้นทุนการแก้ไขยิ่งสูง สามารถอธิบายเชิงแนวคิดด้วยสมการต่อไปนี้
คำอธิบายตัวแปร:
ตัวอย่างการตีความ: หากพบ Bug ตั้งแต่ช่วงเขียนโค้ด อาจแก้ได้เร็ว แต่หากพบหลังส่งมอบแล้ว อาจต้องตรวจ requirement ใหม่ แก้โค้ด ทดสอบซ้ำ ทำเอกสารใหม่ และแจ้งผู้ใช้ จึงมีต้นทุนสูงกว่า
การแยกประเภทของ Bug ช่วยให้เลือกวิธีตรวจสอบได้เหมาะสม ข้อผิดพลาดที่พบบ่อยแบ่งได้ดังนี้
| ประเภท | ความหมาย | ตัวอย่าง | วิธีตรวจ |
|---|---|---|---|
| Syntax Error | เขียนคำสั่งผิดตามกฎภาษา | ลืมวงเล็บ ใช้ keyword ผิด | Compiler, IDE |
| Runtime Error | โปรแกรมรันแล้วเกิดข้อผิดพลาด | หารด้วยศูนย์ เปิดไฟล์ไม่เจอ | Debugger, Exception Handling |
| Logic Error | โปรแกรมรันได้แต่ผลลัพธ์ผิด | เงื่อนไขผิด สูตรคำนวณผิด | Test Case, Code Review |
| Requirement Error | โปรแกรมไม่ตรงความต้องการ | ทำฟีเจอร์ครบแต่ผิดกติกาธุรกิจ | Requirement Review, Prototype |
ตัวอย่างต่อไปนี้เป็นฟังก์ชันคำนวณส่วนลดจากราคา สถานการณ์สมมติคือร้านค้ากำหนดว่า ซื้อครบ 1,000 บาทขึ้นไป ได้ส่วนลด 5%
#include <iostream>
using namespace std;
// คำนวณส่วนลดจากราคาสินค้า
// Requirement: ถ้าราคา >= 1000 ต้องได้ส่วนลด 5%
double calculateDiscount(double price) {
// ตรวจสอบข้อมูลเข้าที่ไม่สมเหตุสมผล
if (price < 0) {
return 0;
}
// ใช้ >= เพื่อให้ราคา 1000 ได้รับส่วนลดตาม requirement
if (price >= 1000) {
return price * 0.05;
}
return 0;
}
int main() {
// ตัวอย่างการใช้งาน
double price1 = 1500;
double price2 = 1000;
double price3 = 999;
cout << "Discount for 1500 = " << calculateDiscount(price1) << endl;
cout << "Discount for 1000 = " << calculateDiscount(price2) << endl;
cout << "Discount for 999 = " << calculateDiscount(price3) << endl;
return 0;
}
ผลลัพธ์ที่คาดหวัง:
| Input | Expected Discount | เหตุผล |
|---|---|---|
| 1500 | 75 | มากกว่า 1000 ได้ส่วนลด 5% |
| 1000 | 50 | ครบ 1000 จึงได้ส่วนลด |
| 999 | 0 | ยังไม่ถึงเงื่อนไข |
| -100 | 0 | ข้อมูลไม่สมเหตุสมผล |
Bug Prevention Pipeline คือแนวทางป้องกันข้อผิดพลาดตั้งแต่ก่อนเริ่มเขียนโค้ดจนถึงหลังปรับปรุงโปรแกรม
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#b8bb26',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart TD
A[Clear Requirement
ความต้องการชัดเจน] --> B[Design First
ออกแบบก่อนเขียน]
B --> C[Coding Standard
มาตรฐานโค้ด]
C --> D[Static Analysis
ตรวจโค้ดอัตโนมัติ]
D --> E[Code Review
ตรวจโดยเพื่อนร่วมทีม]
E --> F[Test Case
ชุดทดสอบ]
F --> G[Refactor
ปรับโครงสร้าง]
G --> H[Better Software
ซอฟต์แวร์คุณภาพดีขึ้น]
แนวทางหลัก:
Coding Standard
กำหนดกติกาการเขียนโค้ด เช่น การตั้งชื่อ การจัดย่อหน้า การแยกไฟล์ และรูปแบบ comment เพื่อให้โค้ดอ่านง่ายและตรวจสอบง่าย
Static Analysis
ใช้เครื่องมือช่วยตรวจ warning, unused variable, possible null pointer, memory leak pattern หรือ style ที่ไม่ตรงมาตรฐาน โดยไม่ต้องรันโปรแกรม
Code Review
ให้ผู้อื่นช่วยอ่านโค้ดเพื่อตรวจ logic, requirement, input validation และความซับซ้อนที่ผู้เขียนอาจมองข้าม
Test Case Design
ออกแบบกรณีทดสอบให้ครอบคลุม normal case, edge case และ invalid case
Modular Design
แยกโปรแกรมเป็นฟังก์ชันหรือโมดูลที่มีหน้าที่ชัดเจน เพื่อลดผลกระทบเมื่อแก้ไข
| รายการตรวจ | คำถามที่ต้องตอบ |
|---|---|
| Requirement | โปรแกรมทำงานตรงโจทย์ทุกเงื่อนไขหรือไม่ |
| Input Validation | ตรวจข้อมูลว่าง ผิดชนิด หรือนอกขอบเขตแล้วหรือไม่ |
| Naming | ชื่อตัวแปร ฟังก์ชัน และไฟล์สื่อความหมายหรือไม่ |
| Function Size | ฟังก์ชันยาวเกินไปหรือทำหลายหน้าที่หรือไม่ |
| Duplication | มีโค้ดซ้ำที่ควรแยกเป็นฟังก์ชันหรือไม่ |
| Error Handling | มีการจัดการกรณีผิดพลาดที่คาดเดาได้หรือไม่ |
| Test Case | มี normal, edge และ invalid case หรือไม่ |
ให้นักศึกษาวิเคราะห์โปรแกรมตัวอย่างที่มีข้อผิดพลาด แล้วดำเนินการตามลำดับ:
calculateDiscountรายงานสั้น 1-2 หน้าในรูปแบบ Markdown ประกอบด้วย:
1000 ในตัวอย่างส่วนลดจึงสำคัญ