# ปฏิบัติการ: Code Review (ใช้ Library-Management-v2)

## 📑 สารบัญ (Table of Contents)

1. [วัตถุประสงค์ของปฏิบัติการ](#วัตถุประสงค์ของปฏิบัติการ)
2. [การเตรียมการก่อนปฏิบัติการ](#การเตรียมการก่อนปฏิบัติการ)
3. [Exercise 1: ตั้งค่าเครื่องมือวิเคราะห์แบบสแตติก](#exercise-1)
4. [Exercise 2: การฝึกการทบทวนโค้ด](#exercise-2)
5. [Exercise 3: วิเคราะห์แบบสแตติกอัตโนมัติ](#exercise-3)
6. [Exercise 4: การประชุมตรวจสอบเสมือน](#exercise-4)
7. [Exercise 5: สร้างรายงานการตรวจสอบ](#exercise-5)
8. [ส่งมอบจากปฏิบัติการ](#ส่งมอบจากปฏิบัติการ)
9. [ปัญหาทั่วไปและการแก้ไขปัญหา](#ปัญหาทั่วไปและการแก้ไขปัญหา)
10. [ท้าทายเพิ่มเติม](#ท้าทายเพิ่มเติม)
11. [ทรัพยากรเพิ่มเติม](#ทรัพยากรเพิ่มเติม)
12. [รายการตรวจสอบ](#รายการตรวจสอบ)
13. [ประเด็นหลักจากปฏิบัติการนี้](#ประเด็นหลักจากปฏิบัติการนี้)

---

## วัตถุประสงค์ของปฏิบัติการ (Lab Objectives)

1. ทำ การทบทวนโค้ด (Code Review) บน Library Management System v2
2. ติดตั้ง (Setup) และใช้ ESLint + Prettier
3. วิเคราะห์ ปัญหาคุณภาพโค้ด (code quality issues)
4. จัด การประชุมตรวจสอบเสมือน (Mock Inspection Meeting)
5. สร้าง รายงานการตรวจสอบ (Inspection Report)

---

## การเตรียมการก่อนปฏิบัติการ (Pre-lab Preparation) {#การเตรียมการก่อนปฏิบัติการ}

### ตรวจสอบว่าคุณมี

- โค้ด Library Management System v2
- Node.js และ npm installed
- VS Code with ESLint extension
- Git for version control

---

## Exercise 1: ตั้งค่าเครื่องมือวิเคราะห์แบบสแตติก (Setup Static Analysis Tools) {#exercise-1}

### Step 1: Install ESLint

```bash
cd Library-Management-v2

# Install ESLint
npm install --save-dev eslint

# Initialize configuration
npx eslint --init
```

**เลือก options:**

```
? How would you like to use ESLint?
  ❯ To check syntax, find problems, and enforce code style

? What type of modules does your project use?
  ❯ JavaScript modules (import/export)

? Which framework does your project use?
  ❯ None of these

? Does your project use TypeScript?
  ❯ No

? Where does your code run?
  ❯ Node

? How would you like to define a style for your project?
  ❯ Use a popular style guide

? Which style guide do you want to follow?
  ❯ Airbnb

? What format do you want your config file to be in?
  ❯ JSON
```

### Step 2: Install Prettier

```bash
# Install Prettier
npm install --save-dev prettier

# Install integration
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
```

### Step 3: สร้างไฟล์ config (Create Configuration Files)

**Create `.eslintrc.json`:**

```json
{
  "env": {
    "node": true,
    "es2021": true,
    "jest": true
  },
  "extends": ["airbnb-base", "plugin:prettier/recommended"],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "rules": {
    "no-console": "warn",
    "no-unused-vars": "error",
    "prefer-const": "error",
    "no-var": "error",
    "eqeqeq": ["error", "always"],
    "curly": ["error", "all"],
    "complexity": ["warn", 10],
    "max-lines-per-function": ["warn", 50],
    "max-depth": ["warn", 3]
  }
}
```

**Create `.prettierrc.json`:**

```json
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "arrowParens": "always"
}
```

**Create `.eslintignore`:**

```
node_modules/
dist/
build/
coverage/
*.min.js
database/
```

### Step 4: เพิ่มสคริปต์ npm (Add npm Scripts)

**Update `package.json`:**

```json
{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}
```

### Step 5: รันการวิเคราะห์เริ่มต้น (Run Initial Analysis)

```bash
# Check for lint errors
npm run lint

# Format all files
npm run format

# Fix auto-fixable issues
npm run lint:fix
```

**📝 Task:** Screenshot ผลลัพธ์จาก `npm run lint`

---

## Exercise 2: การฝึกการทบทวนโค้ด (Code Review Practice) {#exercise-2}

### ตัวอย่าง โค้ดที่มีปัญหา (สำหรับ Review)

**File: `src/controllers/BorrowingController.js` (Problematic Version)**

```javascript
// ⚠️ This code has MANY issues - Find them!

const Borrowing = require("../models/Borrowing");
const Book = require("../models/Book");
const Member = require("../models/Member");

// Helper function with issues
var calculateFine = function (dueDate, returnDate) {
  var due = new Date(dueDate);
  var returned = new Date(returnDate);
  var daysOverdue = Math.floor((returned - due) / (1000 * 60 * 60 * 24));

  if (daysOverdue > 0) {
    var feePerDay = 10;
    return daysOverdue * feePerDay;
  }
  return 0;
};

var BorrowingController = {
  // Async function issues
  findById: function (req, res) {
    try {
      var borrowId = req.params.borrowId;
      var record = Borrowing.findById(borrowId);
      if (!record) {
        return res.status(404).json({ error: "Borrowing record not found" });
      }
      res.json(record);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  borrow: function (req, res) {
    try {
      var memberId = req.body.memberId;
      var bookId = req.body.bookId;
      var borrowDate = req.body.borrowDate;
      var dueDate = req.body.dueDate;

      // No input validation
      // Check if member exists
      var member = Member.findById(memberId);
      if (!member) {
        return res.status(404).json({ error: "Member not found" });
      }

      // Check if book exists
      var book = Book.findById(bookId);
      if (!book) {
        return res.status(404).json({ error: "Book not found" });
      }

      // Loose equality
      if (book.available_copies == 0) {
        return res.status(400).json({ error: "Book is not available" });
      }

      // Check member borrowing limit inconsistently
      if (member.status != "active") {
        return res.status(400).json({ error: "Member is not active" });
      }

      // Missing validation for dates
      var borrowRecord = Borrowing.create(
        memberId,
        bookId,
        borrowDate,
        dueDate,
      );

      // Missing error handling possibility
      Book.updateAvailableCopies(bookId, book.available_copies - 1);

      console.log("Borrow record created for member " + memberId);
      res.status(201).json({
        success: true,
        borrow_id: borrowRecord.lastID,
      });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  returnBook: function (req, res) {
    try {
      var borrowId = req.params.borrowId;
      var returnDate = req.body.returnDate;

      // Missing parameter validation
      var borrow = Borrowing.findById(borrowId);
      if (!borrow) {
        return res.status(404).json({ error: "Borrowing record not found" });
      }

      // Loose equality
      if (borrow.status == "returned") {
        return res.status(400).json({ error: "Book already returned" });
      }

      // No input sanitization
      var fine = calculateFine(borrow.due_date, returnDate);

      // Potential race condition
      Borrowing.update(borrowId, returnDate, fine);
      var book = Book.findById(borrow.book_id);
      Book.updateAvailableCopies(borrow.book_id, book.available_copies + 1);

      console.log("Book returned: " + borrowId + ", Fine: " + fine);
      res.json({
        success: true,
        fine: fine,
      });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  getOverdue: function (req, res) {
    try {
      var records = Borrowing.getOverdue();
      // No null check
      res.json(records);
    } catch (error) {
      console.log("Error in getOverdue: " + error);
      res.status(500).json({ error: error.message });
    }
  },

  getMemberBorrows: function (req, res) {
    try {
      var memberId = req.params.memberId;
      var records = Borrowing.getByMember(memberId);

      // Inconsistent response format
      if (records != null && records.length > 0) {
        res.json(records);
      } else {
        res.json({ result: [] }); // Different format!
      }
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },
};

module.exports = BorrowingController;
```

### ให้นิสิต: ทบทวนโค้ดโดยตัวเอง

**ใช้ รายการตรวจสอบการทบทวนโค้ด ด้านล่าง:**

#### รายการตรวจสอบการทบทวนโค้ด (Code Review Checklist)

**1. คุณภาพและความอ่านได้ (Code Quality & Readability)**

- [ ] Variable names ชัดเจน สื่อความหมาย
- [ ] Function names บอกได้ว่าทำอะไร
- [ ] การจัดรูปแบบสอดคล้อง (Code formatting consistent)
- [ ] ความเห็น (Comments) ที่จำเป็น (ไม่มากเกินไป - ไม่เยอะเกินไป)
- [ ] Indentation ถูกต้อง

**2. ฟังก์ชันและตรรกะ (Functionality & Logic)**

- [ ] Logic ถูกต้อง ครบถ้วน
- [ ] กรณีพิเศษ (Handle edge cases) (null, empty, invalid input)
- [ ] Error handling เพียงพอ
- [ ] ไม่มีลูปอนันต์ (No infinite loops)
- [ ] Return values ถูกต้อง
- [ ] Async/await ใช้ถูกต้อง

**3. มาตรฐานโค้ด (Code Standards)**

- [ ] ใช้ `const`/`let` แทน `var`
- [ ] ใช้ `===` แทน `==`
- [ ] Arrow functions เมื่อเหมาะสม
- [ ] Modern JavaScript features
- [ ] ชื่อสัญญาคงสม่ำเสมอ (Consistent naming convention)

**4. ประสิทธิภาพ (Performance)**

- [ ] ประสิทธิภาพของอัลกอริทึม (Algorithm efficiency)
- [ ] ไม่มีลูปที่ไม่จำเป็น (No unnecessary loops)
- [ ] โครงสร้างข้อมูลที่เหมาะสม (Proper data structures)
- [ ] ไม่มีการรั่วไหลของหน่วยความจำ (No memory leaks)

**5. ความปลอดภัย (Security)**

- [ ] การตรวจสอบอินพุต (Input validation)
- [ ] ไม่มีช่องโหว่ SQL injection (No SQL injection vulnerabilities)
- [ ] ไม่มีข้อมูลที่ละเอียดอ่อนในบันทึก (No sensitive data in logs)
- [ ] การตรวจสอบการตรวจสอบสิทธิ์ที่เหมาะสม (Proper authentication checks)

**6. ความสามารถในการบำรุงรักษา (Maintainability)**

- [ ] Function ไม่ยาวเกินไป (<50 lines)
- [ ] Complexity ไม่สูงเกินไป
- [ ] DRY principle (Don't Repeat Yourself)
- [ ] Single Responsibility Principle
- [ ] ง่ายต่อการปรับแต่ง/ขยาย (Easy to modify/extend)

**7. การทดสอบ (Testing)**

- [ ] ง่ายต่อการเขียน tests
- [ ] ไม่มีการเชื่อมโยงที่แน่นหนา (No tight coupling)
- [ ] ความพึ่งพาที่สามารถเลียนแบบได้ (Mockable dependencies)

---

### 📋 เทมเพลตสำหรับบันทึกข้อบกพร่อง (Defect Log Template)

สร้างตาราง defects ที่เจอ:

| #   | Line | Severity | Type      | Description                    | Suggestion                  |
| --- | ---- | -------- | --------- | ------------------------------ | --------------------------- |
| 1   | 5    | Minor    | Standards | Using `var` instead of `const` | Use `const` for module      |
| 2   | 6    | Major    | Standards | Function expression with `var` | Use `const` with arrow func |
| 3   | 12   | Major    | Standards | Using `==` instead of `===`    | Use strict equality `===`   |
| ... |      |          |           |                                |                             |

**Defect Types:**

- Standards: การละเมิดมาตรฐานการเขียนโค้ด (Coding standard violations)
- Logic: ข้อผิดพลาดทางตรรกะหรือข้อบกพร่องที่อาจเกิด (Logical errors or potential bugs)
- Security: ช่องโหว่ความปลอดภัย (Security vulnerabilities)
- Performance: ปัญหาประสิทธิภาพ (Performance issues)
- Maintainability: ยากต่อการบำรุงรักษา/ทำความเข้าใจ (Hard to maintain/understand)
- Async: ปัญหา async/await (Async/await issues)

**📝 Task:** หา defects อย่างน้อย 15 issues

---

## Exercise 3: วิเคราะห์แบบสแตติกอัตโนมัติ (Automated Static Analysis)

### Step 1: Run ESLint on the Problematic Code

```bash
# Run ESLint on BorrowingController
npx eslint src/controllers/BorrowingController.js

# Save output
npx eslint src/controllers/BorrowingController.js > eslint-report.txt
```

### Step 2: วิเคราะห์ผลลัพธ์ (Analyze Results)

**Questions to Answer:**

1. กี่ errors และ warnings ที่ ESLint หาเจอ?
2. Top 3 ประเภทของ issues ที่พบคืออะไร?
3. Issues ไหนที่ auto-fix ได้?
4. Issues ไหนที่ต้องแก้ manual?

### Step 3: แก้ไขปัญหา (Fix Issues)

```bash
# Auto-fix ที่ทำได้
npx eslint src/controllers/BorrowingController.js --fix

# Format code
npx prettier --write src/controllers/BorrowingController.js
```

### Step 4: Refactored Code (Example) (โค้ดที่ปรับปรุงแล้ว)

```javascript
// REFACTORED VERSION
const Borrowing = require("../models/Borrowing");
const Book = require("../models/Book");
const Member = require("../models/Member");

const DAILY_FEE = 10;

// Helper function with proper syntax
const calculateFine = (dueDate, returnDate) => {
  const due = new Date(dueDate);
  const returned = new Date(returnDate);
  const daysOverdue = Math.floor((returned - due) / (1000 * 60 * 60 * 24));

  if (daysOverdue > 0) {
    return daysOverdue * DAILY_FEE;
  }
  return 0;
};

const BorrowingController = {
  async findById(req, res) {
    try {
      const { borrowId } = req.params;

      if (!borrowId) {
        return res.status(400).json({ error: "Borrow ID is required" });
      }

      const record = await Borrowing.findById(borrowId);

      if (!record) {
        return res.status(404).json({ error: "Borrowing record not found" });
      }

      res.json(record);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  async borrow(req, res) {
    try {
      const { memberId, bookId, borrowDate, dueDate } = req.body;

      // Input validation
      if (!memberId || !bookId || !borrowDate || !dueDate) {
        return res.status(400).json({
          error: "Member ID, book ID, borrow date, and due date are required",
        });
      }

      // Validate dates
      const borrow = new Date(borrowDate);
      const due = new Date(dueDate);
      if (borrow >= due) {
        return res.status(400).json({
          error: "Borrow date must be before due date",
        });
      }

      // Check if member exists and is active
      const member = await Member.findById(memberId);
      if (!member) {
        return res.status(404).json({ error: "Member not found" });
      }

      if (member.status !== "active") {
        return res.status(400).json({ error: "Member is not active" });
      }

      // Check member borrowing limit
      const borrowCount = await Member.getBorrowingCount(memberId);
      if (borrowCount >= member.max_books) {
        return res.status(400).json({
          error: `Member has reached maximum borrowing limit (${member.max_books})`,
        });
      }

      // Check if book exists
      const book = await Book.findById(bookId);
      if (!book) {
        return res.status(404).json({ error: "Book not found" });
      }

      // Check availability
      if (book.available_copies <= 0) {
        return res.status(400).json({ error: "Book is not available" });
      }

      // Check if already borrowed
      const currentBorrows = await Borrowing.getMemberCurrentBorrows(memberId);
      if (currentBorrows.some((b) => b.book_id === bookId)) {
        return res
          .status(400)
          .json({ error: "Member already borrowed this book" });
      }

      // Create borrow record
      const result = await Borrowing.create(
        memberId,
        bookId,
        borrowDate,
        dueDate,
      );

      // Update available copies
      await Book.updateAvailableCopies(bookId, book.available_copies - 1);

      res.status(201).json({
        success: true,
        borrow_id: result.lastID,
      });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  async returnBook(req, res) {
    try {
      const { borrowId } = req.params;
      const { returnDate } = req.body;

      // Input validation
      if (!borrowId || !returnDate) {
        return res.status(400).json({
          error: "Borrow ID and return date are required",
        });
      }

      const borrow = await Borrowing.findById(borrowId);
      if (!borrow) {
        return res.status(404).json({ error: "Borrowing record not found" });
      }

      if (borrow.status === "returned") {
        return res.status(400).json({ error: "Book already returned" });
      }

      // Calculate fine
      const fine = calculateFine(borrow.due_date, returnDate);

      // Update borrowing record
      await Borrowing.update(borrowId, returnDate, fine);

      // Update available copies
      const book = await Book.findById(borrow.book_id);
      if (book) {
        await Book.updateAvailableCopies(
          borrow.book_id,
          book.available_copies + 1,
        );
      }

      res.json({
        success: true,
        fine,
      });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  async getOverdue(req, res) {
    try {
      const records = await Borrowing.getOverdue();

      if (!records) {
        return res.json([]);
      }

      res.json(records);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },

  async getMemberBorrows(req, res) {
    try {
      const { memberId } = req.params;

      if (!memberId) {
        return res.status(400).json({ error: "Member ID is required" });
      }

      const records = await Borrowing.getByMember(memberId);

      // Consistent response format
      res.json(records || []);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  },
};

module.exports = BorrowingController;
```

**Task:** เปรียบเทียบ Before/After และสรุปการปรับปรุง

---

## Exercise 4: การประชุมตรวจสอบเสมือน (Mock Inspection Meeting)

### การมอบหมายบทบาท (Roles Assignment) (กลุ่มละ 4-5 คน)

- **ผู้ประสานงาน (Moderator)** (1 คน): จัดการ process
- **ผู้เขียน (Author)** (1 คน): เจ้าของ code, ตอบคำถาม
- **ผู้อ่าน (Reader)** (1 คน): อ่าน code
- **ผู้ทบทวน (Reviewers)** (2-3 คน): หา defects
- **ผู้จดบันทึก (Scribe)** (1 คน): บันทึก defects

### การจำลองการประชุม (Meeting Simulation) (10 minutes)

**Agenda:**

1. **บทนำ (Introduction)** (1 min)
   - Moderator แนะนำ scope และ process
   - Code: BorrowingController.js

2. **การอ่านโค้ด (Code Reading)** (7 min)
   - Reader อ่าน code ทีละ function
   - Reviewers ชี้ defects
   - Scribe บันทึก

3. **สรุป (Summary)** (2 min)
   - Count defects by severity
   - Action items

**Task:**

- บันทึก meeting minutes
- จัดหมวดหมู่ defects ที่เจอ
- กำหนด action items

---

## Exercise 5: สร้างรายงานการตรวจสอบ (Create Inspection Report)

### แม่แบบรายงาน (Inspection Report Template)

```markdown
# รายงานการตรวจสอบโค้ด (Code Inspection Report)

## รายละเอียดการตรวจสอบ (Inspection Details)

- **Date:** [date]
- **Code Reviewed:** BorrowingController.js (Library-Management-v2)
- **Lines Reviewed:** [number]
- **Reviewers:** [names]
- **Duration:** 60 minutes (10 min meeting)

## สรุปการตรวจสอบ (Inspection Summary)

- **Total Defects Found:** [number]
  - Critical: [number]
  - Major: [number]
  - Minor: [number]
  - Suggestions: [number]

## การกระจายข้อบกพร่องตามประเภท (Defect Distribution by Type)

- Standards Violations: [number]
- Logic Issues: [number]
- Async Issues: [number]
- Maintainability: [number]
- Security: [number]

## ปัญหาที่สำคัญที่สุดที่พบ (Top Issues Found)

### 1. Use of `var` instead of `const`/`let`

- **Severity:** Minor
- **Location:** ตำแหน่งหลาย ๆ แห่ง (Multiple locations)
- **Description:** Old JavaScript syntax used
- **Recommendation:** Replace with modern syntax

### 2. Missing async/await

- **Severity:** Major
- **Location:** findById, borrow, returnBook methods
- **Description:** Methods should be async but not defined as such
- **Recommendation:** Add async keyword to all async methods

### 3. Using `==` instead of `===`

- **Severity:** Major
- **Location:** Lines with status comparison
- **Description:** Loose equality can cause bugs
- **Recommendation:** Always use strict equality

### 4. ขาดการตรวจสอบอินพุต (Missing Input Validation)

- **Severity:** Major
- **Location:** Multiple functions
- **Description:** No validation for null/undefined inputs
- **Recommendation:** Add validation at function entry

## เมตริกซ์ (Metrics)

- **Defect Density:** [number] defects per 100 LOC
- **Inspection Rate:** [number] LOC/hour (preparation)
- **Meeting Rate:** [number] LOC/minute

## รายการดำเนินการ (Action Items)

1. [ ] Author: Fix all major defects by [date]
2. [ ] Author: Apply ESLint auto-fix
3. [ ] Author: Add input validation
4. [ ] Author: Convert to async/await properly
5. [ ] Moderator: ยืนยันการแก้ไข (Verify fixes)
6. [ ] Team: Update coding standards document

## บทเรียนที่ได้เรียนรู้ (Lessons Learned)

- ESLint caught [%] of standards violations
- Manual review found logic issues ESLint missed
- Preparation phase was crucial for meeting effectiveness
- Async/await patterns need to be consistent

## ความเห็นของผู้ทบทวน (Reviewer Comments)

[Space for additional comments]
```

**Task:** สร้าง Inspection Report สำหรับ BorrowingController.js

---

## Lab Deliverables {#ส่งมอบจากปฏิบัติการ}

ให้นิสิตส่งสิ่งเหล่านี้

1. **ไฟล์ config (ESLint Configuration Files)**
   - `.eslintrc.json`
   - `.prettierrc.json`

2. **ESLint Report**
   - `eslint-report.txt`
   - Screenshot of ESLint output

3. **บันทึกข้อบกพร่อง (Defect Log)**
   - Table of all defects found
   - จัดประเภทตามความร้ายแรงและประเภท (Classified by severity and type)

4. **โค้ดที่ปรับปรุงแล้ว (Refactored Code)**
   - Before/After comparison
   - คำอธิบายของการเปลี่ยนแปลง (Explanation of changes)

5. **รายงานการตรวจสอบ (Inspection Report)**
   - Complete report following template
   - Metrics and analysis

6. **การสะท้อน (Reflection)** (1 หน้า)
   - อะไรที่เรียนรู้จาก static analysis ของ v2 code?
   - Manual review vs Automated tools - ข้อดีข้อเสียของแต่ละอย่าง?
   - ความแตกต่างระหว่างการ review v1 และ v2 คืออะไร?
   - จะนำไปใช้ใน project ต่อไปอย่างไร?

---

## ปัญหาทั่วไปและการแก้ไขปัญหา (Common Issues & Troubleshooting) {#ปัญหาทั่วไปและการแก้ไขปัญหา}

### Issue 1: ESLint shows too many errors (ESLint แสดง errors มากเกินไป)

**Solution:**

```bash
# Fix step by step
# 1. Fix formatting first
npx prettier --write .

# 2. Auto-fix ESLint issues
npx eslint . --fix

# 3. Manually fix remaining issues
```

### Issue 2: กฎที่ขัดแย้งกัน (Conflicting rules between ESLint and Prettier)

**Solution:**

```bash
# Install integration
npm install --save-dev eslint-config-prettier

# Update .eslintrc.json
"extends": [
  "airbnb-base",
  "plugin:prettier/recommended"  // Must be last!
]
```

### Issue 3: เตือนมากเกินไป (Too many warnings, hard to focus)

**Solution:**

```bash
# Show only errors
npx eslint . --quiet

# Or update specific rules
// .eslintrc.json
"rules": {
  "no-console": "off"  // Disable warning
}
```

### Issue 4: v2 has async/await but ESLint complains

**Solution:**

```bash
# Update parserOptions to support async/await
// .eslintrc.json
"parserOptions": {
  "ecmaVersion": 2020,  // or "latest"
  "sourceType": "module"
}
```

---

## Bonus Challenges (ถ้ามีเวลาเหลือ) {#ท้าทายเพิ่มเติม}

### Challenge 1: Custom ESLint Rule

สร้าง custom rule ที่ห้ามใช้ `console.log` ใน production code:

```javascript
// .eslintrc.json
"rules": {
  "no-console": ["error", { "allow": ["warn", "error"] }]
}
```

### Challenge 2: Pre-commit Hook

Setup Git hook ที่รัน ESLint + Prettier ก่อน commit:

```bash
# Install husky
npm install --save-dev husky lint-staged

# Setup
npx husky install

# Add pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
```

```json
// package.json
"lint-staged": {
  "*.js": [
    "eslint --fix",
    "prettier --write"
  ]
}
```

### Challenge 3: VS Code Integration

ติดตั้ง VS Code extensions:

- ESLint
- Prettier
- Error Lens

**Settings:**

```json
// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.validate": ["javascript"]
}
```

### Challenge 4: Compare v1 vs v2 Code Quality

เปรียบเทียบข้อบกพร่องที่พบในการ review BorrowingController.js v2 กับ BookService.js v1:

- โครงการ v2 มีปัญหาอะไรน้อยลง?
- v2 ใช้ async/await ถูกต้องหรือไม่?
- มีการปรับปรุงเรื่องอื่น ๆ หรือไม่?

---

## Additional Resources {#ทรัพยากรเพิ่มเติม}

### Documentation

- [ESLint Rules](https://eslint.org/docs/rules/)
- [Prettier Options](https://prettier.io/docs/en/options.html)
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
- [Async/Await Best Practices](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)

### Tools

- [AST Explorer](https://astexplorer.net/) - Visualize JavaScript AST
- [ESLint Demo](https://eslint.org/demo) - Try ESLint online

### การอ่าน (Reading)

- "Code Complete" by Steve McConnell
- "Clean Code" by Robert C. Martin
- "The Art of Readable Code" by Boswell & Foucher
- "Node.js Best Practices" by Goldberger et al.

---

## Lab Checklist

ก่อนส่งงาน ตรวจสอบว่า:

- [ ] ติดตั้ง ESLint และ Prettier สำเร็จ
- [ ] Config files ครบถ้วน
- [ ] รัน `npm run lint` ได้โดยไม่ error
- [ ] สร้าง Defect Log อย่างน้อย 15 items
- [ ] ทำ Code Review ตาม checklist
- [ ] Refactor code แล้ว
- [ ] ทำ Mock Inspection Meeting
- [ ] เขียน Inspection Report สมบูรณ์
- [ ] เขียน Reflection พร้อมการเปรียบเทียบ v1 vs v2
- [ ] Commit code to Git
- [ ] Push to GitHub repository

---

## ประเด็นหลักจากปฏิบัติการนี้ (Key Takeaways from This Lab) {#ประเด็นหลักจากปฏิบัติการนี้}

1. **ตั้งค่าการวิเคราะห์แบบสแตติกอัตโนมัติ (Setup automated static analysis)** ด้วย ESLint และ Prettier
2. **ทำการทบทวนโค้ดด้วยตนเอง (Perform manual code review)** อย่างเป็นระบบ
3. **ระบุปัญหาคุณภาพโค้ดทั่วไป (Identify common code quality issues)** ทั้ง manual และ automated
4. **เข้าใจความสำคัญ async/await (Understand async/await patterns)** ใน Express controllers
5. **ดำเนินการประชุมตรวจสอบอย่างเป็นทางการ (Conduct formal inspection meeting)** ตาม process ที่ถูกต้อง
6. **จัดทำเอกสารข้อค้นพบ (Document findings)** ใน Inspection Report
7. **ปรับโครงสร้างโค้ดใหม่ (Refactor code)** ให้มีคุณภาพดีขึ้น

**สิ่งที่ต้องจำ:**

- ช่วยประหยัดเงินโดยค้นหา bugs เร็ว (Static testing saves money by finding bugs early)
- ผสมวิธีกาแมนนวลและอัตโนมัติ (Combine manual and automated approaches)
- ใช้เครื่องมือสำหรับหาปัญหาง่ายๆ ใช้คนสำหรับปัญหาซับซ้อน (Use tools to find simple issues, humans for complex ones)
- ทบทวนโค้ด ปรับปรุงไม่เพียงเฉพาะโค้ดแต่ทักษะทีมด้วย (Code reviews improve not just code, but also team skills)
- การปรับปรุงอย่างต่อเนื่องผ่านเมตริกซ์ (Continuous improvement through metrics)
- เรียนรู้จากโปรเจ็กต์ที่มีเวอร์ชันต่างกัน (Learn from different project versions and compare quality)
