Code Quality Code Review DevOps

Code Quality & Review — เขียน Code ดีๆ ที่ทีมอยากอ่าน 🔍

By Anirach Mingkhwan DevOps & Vibe Coding 2026
Code Quality — scientist dog inspecting code

จากโพสต์ที่แล้ว เราเรียน GitHub Actions ที่ช่วย automate CI/CD — แต่ pipeline ดีแค่ไหน ถ้า code ข้างในห่วยก็ไม่ช่วย!

วันนี้มาเรียนรู้ Code Quality — ทำยังไงให้ code อ่านง่าย, maintain ได้, bug น้อย และทีมทำงานร่วมกันได้ดี 🔍🐕


🤔 ทำไม Code Quality สำคัญ?

ลองนึกภาพว่าคุณต้องกลับมาแก้ code ที่เขียนเมื่อ 6 เดือนก่อน — ถ้า code อ่านไม่รู้เรื่อง ไม่มี test ไม่มี documentation คุณจะเสียเวลาทำความเข้าใจมากกว่าเวลาแก้จริง! Code Quality ไม่ใช่ "เรื่องสวยงาม" แต่คือ ต้นทุนจริง — code คุณภาพต่ำ = bug เยอะ + แก้ช้า + deploy กลัว + developer ใหม่สับสน

Code ดี 😊Code แย่ 😰
อ่านแล้วเข้าใจใน 5 นาทีอ่าน 1 ชั่วโมงยังงง
แก้ bug ง่าย → แก้จุดเดียวแก้ 1 จุด พัง 10 จุด
คนใหม่ onboard ได้เร็วคนเดิมลาออก = ตาย
Deploy มั่นใจDeploy แล้วสวดมนต์
Technical debt ต่ำTechnical debt สะสมจนล้มละลาย
💡 "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." — Martin Fowler

🧹 Linting & Formatting — บังคับให้เขียนสวย

Linting คือการใช้เครื่องมือตรวจสอบ code อัตโนมัติเพื่อหา bugs, bad practices, และ style issues ส่วน Formatting คือการจัด code ให้สวยเหมือนกันทั้งทีม (indentation, quotes, semicolons) — ไม่ต้องเถียงกันอีกว่า tab หรือ space! เมื่อ setup แล้ว ทุกคนในทีมจะเขียน code style เดียวกันอัตโนมัติ

ESLint (JavaScript/TypeScript)

ESLint เป็น linter ยอดนิยมที่สุดสำหรับ JavaScript/TypeScript — สามารถ customize rules ได้ตามความต้องการของทีม ตั้งแต่ห้ามใช้ any type ไปจนถึงบังคับ naming conventions ตัวอย่างด้านล่างแสดง config ที่แนะนำ:

# ติดตั้ง
npm install --save-dev eslint @eslint/js typescript-eslint

# eslint.config.js
import js from '@eslint/js';
import tseslint from 'typescript-eslint';

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      'no-console': 'warn',             // console.log = warning
      'no-unused-vars': 'error',         // ตัวแปรไม่ใช้ = error
      'no-var': 'error',                 // ห้ามใช้ var
      'prefer-const': 'error',           // ใช้ const ถ้าไม่ reassign
      'eqeqeq': 'error',               // ห้าม == ต้อง ===
      '@typescript-eslint/no-explicit-any': 'warn',
    }
  }
];

# รัน
npx eslint .                            # ตรวจ
npx eslint . --fix                      # ตรวจ + แก้อัตโนมัติ

Prettier (Auto-format)

Prettier ทำงานร่วมกับ ESLint — ESLint ดูแล "ถูก-ผิด" ส่วน Prettier ดูแล "สวย-ไม่สวย" แค่ตั้งค่าครั้งเดียว Prettier จะ format code ให้สวยทุกครั้งที่ save — ไม่ต้องเสียเวลาจัด whitespace เอง:

# ติดตั้ง
npm install --save-dev prettier

# .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80
}

# รัน
npx prettier --write .                  # format ทุกไฟล์
npx prettier --check .                  # ตรวจ (ใน CI)

Python — Ruff (เร็วที่สุด!)

Ruff เป็น Python linter + formatter ตัวใหม่ที่เขียนด้วย Rust — เร็วกว่า flake8 + isort + black รวมกัน 10-100 เท่า! แทนที่จะต้องตั้งค่า 3 tools แยกกัน Ruff ทำได้ทุกอย่างในตัวเดียว:

# ติดตั้ง
pip install ruff

# pyproject.toml
[tool.ruff]
line-length = 88
select = ["E", "F", "W", "I", "N", "UP"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"

# รัน
ruff check .                            # lint
ruff check . --fix                      # lint + fix
ruff format .                           # format (แทน black)

Pre-commit Hooks — บังคับก่อน commit

ปัญหาของ linting/formatting คือ developer อาจลืมรัน! Pre-commit hooks แก้ปัญหานี้โดยบังคับให้ lint + format อัตโนมัติก่อนทุก commit — ถ้า code ไม่ผ่าน commit จะ fail ไม่ต้องพึ่ง "วินัย" อีกต่อไป เพราะระบบบังคับให้:

# ติดตั้ง
npm install --save-dev husky lint-staged
npx husky init

# .husky/pre-commit
npx lint-staged

# package.json
{
  "lint-staged": {
    "*.{js,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,yml}": [
      "prettier --write"
    ]
  }
}

# ตอนนี้ทุกครั้งที่ git commit → auto lint + format!
# ❌ code ไม่ผ่าน lint = commit ไม่ได้

🔍 Code Review — ศิลปะการ review code

เครื่องมืออัตโนมัติจับ bugs ได้ แต่ไม่สามารถบอกได้ว่า "approach นี้ถูกต้องไหม?" หรือ "มี design ที่ดีกว่านี้ไหม?" นั่นคือหน้าที่ของ Code Review — กระบวนการที่ developer คนอื่นอ่านและให้ feedback กับ code ของเรา ไม่ใช่แค่หา bugs แต่เป็นการแชร์ความรู้ สร้างมาตรฐาน และป้องกันปัญหาที่เครื่องมือมองไม่เห็น

Code Review Flow

กระบวนการ Code Review ที่ดีควรมี flow ที่ชัดเจน — ตั้งแต่ developer เปิด PR ไปจนถึงการ merge เข้า main branch สิ่งสำคัญคือต้อง review ภายใน 24 ชั่วโมง เพื่อไม่ให้ block workflow ของทีม:

📝 PR สร้าง
Dev เปิด Pull Request
🤖 CI ตรวจ
Lint, Test, Build
👀 Human Review
Logic, Design, Style
✅ Approve
Merge to main

Reviewer — ดูอะไรบ้าง?

Reviewer ที่ดีไม่ใช่แค่ดู syntax — แต่ต้องดูทั้ง correctness, design, readability, performance, security และ test coverage หลักการสำคัญคือ "Could a new team member understand this code?" ถ้าตอบว่าไม่ แสดงว่า code ต้องปรับปรุง:

หมวดดูอะไรตัวอย่าง
🐛 Correctnessทำงานถูกมั้ยEdge cases, off-by-one, null checks
📖 Readabilityอ่านรู้เรื่องมั้ยชื่อตัวแปร, comments, structure
🏗️ Designออกแบบดีมั้ยSOLID, DRY, separation of concerns
⚡ Performanceเร็วพอมั้ยN+1 queries, unnecessary loops
🔒 Securityปลอดภัยมั้ยSQL injection, XSS, hardcoded secrets
🧪 Testsมี test ครอบคลุมมั้ยHappy path + edge cases + error cases

ตัวอย่าง Review Comment ที่ดี

Review comment ที่ดีต้อง อธิบาย "ทำไม" ไม่ใช่แค่บอกว่า "แก้ตรงนี้" — เพื่อให้ developer เรียนรู้และไม่ทำผิดซ้ำ ควรแนะนำวิธีแก้ พร้อม code ตัวอย่าง และใช้ tone ที่ constructive ไม่ใช่ criticize:

// ❌ Review comment ที่แย่: // "This is wrong" → ไม่บอกว่าผิดยังไง // "Fix this" → ไม่บอกว่าแก้ยังไง
// ✅ Review comment ที่ดี: // "This could throw if `user` is null. // Consider adding a null check: // `if (!user) return res.status(404).json(...)`"
// ✅ อีกตัวอย่าง: // "Nit: prefer `const` here since `items` // is never reassigned."
🐕 Review Culture: Review code ไม่ใช่ review คน — "code นี้น่าจะ..." ไม่ใช่ "คุณเขียนแย่..." | ใช้ "Nit:" สำหรับ nitpick เล็กๆ | ชมเมื่อเห็น code ดี 👍

🧠 SOLID Principles — หลักการ code ที่ดี

SOLID คือ 5 หลักการออกแบบ software ที่ช่วยให้ code flexible, maintainable, และ extensible คิดค้นโดย Robert C. Martin (Uncle Bob) แม้ชื่อจะดูน่ากลัว แต่แก่นแท้คือ — แยกส่วนให้ชัดเจน, แต่ละส่วนทำหน้าที่เดียว, เปลี่ยนแปลงได้ง่าย มาดูแต่ละตัว:

S
Single Responsibility
Class/function ทำแค่อย่างเดียว
O
Open/Closed
เปิดให้ extend ปิดไม่ให้แก้
L
Liskov Substitution
Subclass แทน parent ได้
I
Interface Segregation
แยก interface เล็กๆ ดีกว่า 1 อันใหญ่
D
Dependency Inversion
พึ่ง abstraction ไม่พึ่ง implementation

ตัวอย่าง: Single Responsibility

Single Responsibility Principle (SRP) บอกว่า "แต่ละ class/function ควรมีเหตุผลในการเปลี่ยนแปลงแค่อันเดียว" ถ้า function ทำทั้ง validate, save, send email → แก้ email logic อาจพัง save logic! ตัวอย่างด้านล่างเปรียบเทียบ code ที่ละเมิด SRP กับ code ที่ทำตาม:

// ❌ แย่: function ทำหลายอย่าง - function handleOrder(order) { - validateOrder(order); // validate - calculateTotal(order); // calculate - chargePayment(order); // payment - sendEmail(order); // notification - updateInventory(order); // inventory - logAnalytics(order); // analytics - }
// ✅ ดี: แยก responsibility ชัดเจน + class OrderService { + constructor(validator, payment, notifier, inventory) { + this.validator = validator; + this.payment = payment; + this.notifier = notifier; + this.inventory = inventory; + } + async process(order) { + this.validator.validate(order); + await this.payment.charge(order); + await this.inventory.update(order); + await this.notifier.send(order); + } + }

🚨 Code Smells — กลิ่นของ code ที่ต้องแก้

Code Smells คือ "สัญญาณ" ว่า code อาจมีปัญหา — ไม่ใช่ bugs โดยตรง แต่เป็น patterns ที่บ่งบอกว่า design ไม่ดี ถ้าปล่อยไว้จะกลายเป็น technical debt ที่แก้ยากขึ้นเรื่อยๆ เหมือน "กลิ่น" — ไม่ได้ทำร้ายทันที แต่บอกว่ามีอะไรเน่าอยู่!

Code Smellปัญหาวิธีแก้
🔢 Magic Numbersif (status === 3)ใช้ constant: ORDER_STATUS.COMPLETED
📏 Long FunctionFunction 200+ บรรทัดแยกเป็น functions เล็กๆ
📦 God ClassClass ทำทุกอย่างแยกตาม responsibility (SRP)
🔄 Duplicated CodeCopy-paste เหมือนกันExtract เป็น shared function
🏗️ Deep Nestingif ซ้อน 5+ ชั้นEarly return, guard clauses
📝 Dead CodeCode ที่ไม่ถูกเรียกใช้ลบทิ้ง (Git เก็บ history ให้)
🔗 Feature Envyใช้ data ของ class อื่นเยอะย้าย method ไป class นั้น
💬 Bad Naminglet x, tmp, data2ชื่อบอกความหมาย

ตัวอย่าง: Deep Nesting → Early Return

หนึ่งใน code smells ที่พบบ่อยที่สุดคือ Deep Nesting — if ซ้อน if ซ้อน if จนอ่านไม่รู้เรื่อง วิธีแก้คือ Early Return Pattern — เช็ค error cases ก่อนแล้ว return ออกเลย ทำให้ "happy path" อยู่ด้านนอกสุดและอ่านง่าย:

// ❌ แย่: Deep nesting - function getDiscount(user) { - if (user) { - if (user.isPremium) { - if (user.orders > 10) { - return 0.2; - } else { - return 0.1; - } - } else { - return 0; - } - } else { - return 0; - } - }
// ✅ ดี: Early return (flat) + function getDiscount(user) { + if (!user) return 0; + if (!user.isPremium) return 0; + if (user.orders > 10) return 0.2; + return 0.1; + }

📊 Static Analysis — ให้เครื่องตรวจ

นอกจาก linter แล้ว ยังมี Static Analysis tools ที่วิเคราะห์ code เชิงลึกกว่า — หา code smells อัตโนมัติ, วัด complexity, ตรวจจับ security vulnerabilities, และ track technical debt ตลอดเวลา เครื่องมือเหล่านี้เป็นเหมือน "ตาที่สาม" ที่ช่วย reviewer

SonarQube / SonarCloud

SonarQube (self-hosted) หรือ SonarCloud (managed) เป็น platform วิเคราะห์ code ที่ครบเครื่องที่สุด — ใช้กับ GitHub Actions ได้ สแกนทุก PR และแสดงผลลัพธ์เป็น dashboard พร้อม metrics ต่างๆ:

# GitHub Actions + SonarCloud
name: Quality Gate

on: [push, pull_request]

jobs:
  sonarcloud:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0                 # full history for blame

      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: >
            -Dsonar.projectKey=myorg_myapp
            -Dsonar.organization=myorg
            -Dsonar.sources=src
            -Dsonar.tests=tests
            -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

SonarQube วัดอะไรบ้าง?

SonarQube วัด code quality ใน 5 มิติหลัก — Bugs, Vulnerabilities, Code Smells, Coverage, และ Duplications แต่ละมิติมีเกรด (A-E) ที่ช่วยให้เห็นภาพรวมได้เร็ว:

Metricวัดอะไรเป้าหมาย
🐛 BugsCode ที่น่าจะผิด0 (A rating)
🔓 VulnerabilitiesSecurity issues0 (A rating)
💩 Code SmellsMaintainability issuesน้อยที่สุด
📋 CoverageTest coverage %> 80%
🔄 DuplicationsDuplicated code %< 3%
🏗️ ComplexityCyclomatic complexity< 15 per function
📏 DebtTechnical debt เวลาแก้Trend ลดลง

Quality Gate — ผ่านหรือไม่ผ่าน

Quality Gate คือ "ด่าน" ที่กำหนดว่า code ต้องผ่านเกณฑ์ขั้นต่ำก่อน merge ได้ — เช่น coverage ต้อง > 80%, ห้ามมี Critical bugs, duplications < 3% ถ้าไม่ผ่าน PR จะ fail อัตโนมัติ:

# Quality Gate Conditions (SonarQube default)
# ──────────────────────────────────────────
# ✅ PASS ถ้า:
#   - New code coverage > 80%
#   - New code duplications < 3%
#   - New bugs = 0 (A rating)
#   - New vulnerabilities = 0 (A rating)
#   - New code smells ≤ A rating
#   - New security hotspots reviewed 100%
#
# ❌ FAIL → PR ไม่สามารถ merge ได้!
# → บังคับคุณภาพอัตโนมัติ ไม่ต้องพึ่ง reviewer

🛠️ Tools รวม — ครบชุด

สรุปเครื่องมือทั้งหมดที่ใช้ในการรักษา Code Quality — แบ่งตาม category เพื่อให้เลือกใช้ได้ง่าย ไม่จำเป็นต้องใช้ทุกตัว เริ่มจาก linter + formatter ก่อน แล้วค่อยเพิ่มตามความพร้อม:

หมวดJavaScript/TSPythonGeneral
LinterESLint, BiomeRuff, Pylint, Flake8
FormatterPrettier, BiomeRuff format, Black
Type CheckTypeScriptmypy, Pyright
Static AnalysisSonarCloudSonarCloud, BanditSonarQube
ComplexityESLint rulesradon, xenonCodeClimate
Dead Codets-prune, knipvulture
Pre-commitHusky + lint-stagedpre-commit
AI ReviewGitHub Copilot, CodeRabbit, Sourcery

📋 CI/CD Quality Pipeline

ถึงเวลารวมทุกเครื่องมือเข้าด้วยกันใน GitHub Actions! Pipeline ด้านล่างแสดง quality checks ที่ควรรันทุก PR เรียงจากเร็วไปช้า — format check (วินาที) → lint (วินาที) → type check (นาที) → tests + coverage (นาที) → SonarCloud (นาที):

# .github/workflows/quality.yml
name: Code Quality

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      # Step 1: Format check
      - name: Check formatting
        run: npx prettier --check .

      # Step 2: Lint
      - name: Lint
        run: npx eslint .

      # Step 3: Type check
      - name: Type check
        run: npx tsc --noEmit

      # Step 4: Tests + coverage
      - name: Tests
        run: npm test -- --coverage

      # Step 5: Coverage threshold
      - name: Check coverage threshold
        run: |
          COVERAGE=$(jq '.total.lines.pct' coverage/coverage-summary.json)
          echo "Coverage: $COVERAGE%"
          if (( $(echo "$COVERAGE < 80" | bc -l) )); then
            echo "❌ Coverage below 80%!"
            exit 1
          fi

      # Step 6: SonarCloud
      - uses: SonarSource/sonarcloud-github-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

Pipeline ข้างต้นจะแสดงการเรียงลำดับขั้นตอนการตรวจสอบจากเร็วไปช้า และจากพื้นฐานไปซับซ้อน การเริ่มด้วย formatting check จะให้ feedback เร็วที่สุดหาก code ไม่เป็นไปตามมาตรฐาน ตามด้วย linting ที่จะตรวจสอบ syntax และ basic rules

Type checking จะช่วยจับข้อผิดพลาดเรื่อง type safety ซึ่งสำคัญมากใน TypeScript การรัน tests พร้อมกับ coverage จะยืนยันว่า functionality ยังทำงานถูกต้อง และมี test coverage ที่เพียงพอ ขั้นตอนสุดท้ายคือ SonarCloud ที่จะทำการวิเคราะห์เชิงลึกและรวบรวม metrics ทั้งหมด


📝 Clean Code Checklist

หลังจากที่เราได้เรียนรู้เครื่องมือและกระบวนการต่างๆ มาแล้ว สิ่งสำคัญคือการนำหลักการและแนวปฏิบัติเหล่านี้มาใช้ในการเขียน code ในชีวิตประจำวัน Clean Code Checklist ต่อไปนี้จะเป็นแนวทางที่นักพัฒนาสามารถใช้เป็น guideline ในการเขียนและ review code

Checklist นี้เป็นการรวบรวมหลักการสำคัญที่ทำให้ code มีคุณภาพ โดยแต่ละข้อจะเน้นในด้านต่างๆ ตั้งแต่การตั้งชื่อ, การออกแบบ function, ไปจนถึงกระบวนการ development ที่ดี การปฏิบัติตาม checklist นี้จะช่วยยกระดับคุณภาพ code อย่างมีระบบ:

  1. Naming: ชื่อตัวแปร/function บอกความหมาย — getUserById ไม่ใช่ getData
  2. Functions: สั้น ทำอย่างเดียว ไม่เกิน 20 บรรทัด
  3. DRY: Don't Repeat Yourself — extract shared logic
  4. KISS: Keep It Simple, Stupid — อย่า over-engineer
  5. Comments: อธิบาย "ทำไม" ไม่ใช่ "อะไร" — code บอก what, comment บอก why
  6. Error Handling: handle errors explicitly ไม่ใช่ catch แล้วเงียบ
  7. No Magic: ใช้ constants แทน magic numbers/strings
  8. Consistent: ทั้ง codebase ใช้ style เดียวกัน (enforce ด้วย linter)
  9. Tests: ทุก function สำคัญต้องมี test
  10. Small PRs: PR เล็กๆ review ง่าย — ไม่เกิน 400 lines changed

Clean Code Checklist ข้างต้นครอบคลุมหลักการสำคัญทุกด้านของการเขียน code ที่มีคุณภาพ ตั้งแต่ technical aspects อย่าง naming conventions และ error handling ไปจนถึง process aspects อย่าง code review และการจัดการ PR ขนาดเล็ก การปฏิบัติตามทุกข้ออาจดูเยอะ แต่เมื่อกลายเป็นนิสัยแล้วจะทำได้โดยอัตโนมัติ

💡 กฎ Boy Scout: "Leave the code better than you found it" — เจอ code smell ก็แก้ทีละนิด ไม่ต้องรอ refactor ใหญ่

กฎ Boy Scout เป็นหลักการง่ายๆ แต่มีพลังมาก การแก้ไข code smell เล็กๆ น้อยๆ ทีละนิดจะช่วยป้องกันไม่ให้ technical debt สะสมจนกลายเป็นปัญหาใหญ่ แนวทางนี้ยั่งยืนกว่าการรอให้มีปัญหาใหญ่แล้วค่อย refactor ทั้งหมด และยังช่วยให้ทีมมีความรู้สึก ownership กับ codebase มากขึ้น


สรุป

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

สิ่งสำคัญที่สุดคือการสร้าง culture ที่ให้ความสำคัญกับคุณภาพ code ในทีม เครื่องมือต่างๆ เป็นเพียงตัวช่วย แต่ใจสำคัญอยู่ที่ mindset และวัฒนธรรมของทีมที่ต้องการเขียน code ที่ดี ช่วยเหลือกัน และเรียนรู้จากกัน นี่คือจุดเริ่มต้นของ sustainable software development:

การนำแนวปฏิบัติเหล่านี้มาใช้ไม่จำเป็นต้องทำทุกอย่างพร้อมกัน สามารถเริ่มจากเครื่องมือพื้นฐานอย่าง linter และ formatter ก่อน แล้วค่อยๆ เพิ่ม static analysis, code review process และ quality gates ตามความพร้อมของทีม สิ่งสำคัญคือความสม่ำเสมอและการปรับปรุงอย่างต่อเนื่อง เพราะ code quality ไม่ใช่จุดหมาย แต่เป็นการเดินทางที่ไม่มีวันสิ้นสุด 🔍🐕

บทความจากซีรีส์ DevOps & Vibe Coding 2026
← Previous
GitHub Actions — Automate ทุกอย่างใน CI/CD Pipeline
Next →
Automated Testing with GitHub — ทดสอบอัตโนมัติทุก Push