Code Quality

miles990/claude-software-skills/software-engineering/code-quality

作者 miles99073632f218e5b3e9d29c87b00c7955964f354833d無授權條款收錄於 2026年10月9日更新於 2026年10月9日

Clean code principles, SOLID, and code review practices

AI 產生的概覽

提供整潔程式碼、SOLID、程式碼審查與 lint 規範,並附上範本和驗證規則。

功能
此技能提供撰寫可維護程式碼的指引:命名、函式設計、註解、五項 SOLID 原則、程式碼審查清單與回饋方式,以及圈複雜度等程式碼度量。它也列出常見陷阱(過度工程、深層巢狀、神奇數字、大泥球)以及針對 console.log、any 型別、參數過多、無追蹤 TODO 和深層相對匯入等問題的驗證規則。技能附帶設定範本,包括 ESLint 設定、Prettier 設定、editorconfig 和 README。
適用情境
適用於審查或重構程式碼、制定團隊編碼規範,或設定 lint 與格式化規則時。也適合講解 SOLID 設計,或診斷深層巢狀、命名不清等可維護性問題。
執行需求
以代理指令形式執行,附帶範本檔案,其中包括可執行的 ESLint 設定指令碼。使用 lint 與格式化範本需要 JavaScript/TypeScript 專案並已安裝 ESLint 和 Prettier。

Code Quality

Overview

Principles and practices for writing maintainable, readable, and reliable code.


Clean Code Principles

Meaningful Names

typescript
// ❌ Cryptic namesconst d = new Date();const u = getU();const arr = data.filter(x => x.s === 'a');
// ✅ Descriptive namesconst currentDate = new Date();const currentUser = getCurrentUser();const activeUsers = users.filter(user => user.status === 'active');
// ❌ Hungarian notation (outdated)const strName = 'John';const arrItems = [];const bIsActive = true;
// ✅ Let the type system handle typesconst name = 'John';const items: Item[] = [];const isActive = true;

Functions

typescript
// ❌ Does too muchfunction processUserData(userId: string) {  const user = db.findUser(userId);  const orders = db.findOrders(userId);  const total = orders.reduce((sum, o) => sum + o.amount, 0);  sendEmail(user.email, `Your total: ${total}`);  updateAnalytics(userId, total);  return { user, orders, total };}
// ✅ Single responsibilityfunction getUser(userId: string): User {  return db.findUser(userId);}
function getUserOrders(userId: string): Order[] {  return db.findOrders(userId);}
function calculateTotal(orders: Order[]): number {  return orders.reduce((sum, o) => sum + o.amount, 0);}
function sendOrderSummary(user: User, total: number): void {  sendEmail(user.email, `Your total: ${total}`);}
// ❌ Too many parametersfunction createUser(name, email, age, role, department, manager, startDate) {}
// ✅ Use object parameterinterface CreateUserParams {  name: string;  email: string;  age?: number;  role: Role;  department: string;  managerId?: string;  startDate: Date;}
function createUser(params: CreateUserParams): User {}

Comments

typescript
// ❌ Redundant comment// Increment counter by 1counter++;
// ❌ Outdated comment (code changed, comment didn't)// Returns the user's full namefunction getUserEmail(user: User) {  return user.email;}
// ✅ Explains WHY, not WHAT// Use binary search because the list is sorted and can have 100k+ itemsconst index = binarySearch(sortedItems, target);
// ✅ Warns about non-obvious behavior// IMPORTANT: This function mutates the input array for performance reasonsfunction quickSort(arr: number[]): number[] {  // ...}
// ✅ TODO with context// TODO(john): Remove after migration completes - tracking in JIRA-1234const legacyAdapter = new LegacyAdapter();

SOLID Principles

Single Responsibility Principle

typescript
// ❌ Multiple responsibilitiesclass UserManager {  createUser(data: UserData) { /* DB logic */ }  validateEmail(email: string) { /* Validation logic */ }  sendWelcomeEmail(user: User) { /* Email logic */ }  generateReport(users: User[]) { /* Report logic */ }}
// ✅ Single responsibility eachclass UserRepository {  create(data: UserData): User { /* DB logic */ }  findById(id: string): User | null { /* DB logic */ }}
class UserValidator {  validateEmail(email: string): boolean { /* Validation */ }  validatePassword(password: string): ValidationResult { /* Validation */ }}
class EmailService {  sendWelcomeEmail(user: User): void { /* Email logic */ }}
class UserReportGenerator {  generate(users: User[]): Report { /* Report logic */ }}

Open/Closed Principle

typescript
// ❌ Must modify to add new payment methodsclass PaymentProcessor {  process(payment: Payment) {    if (payment.type === 'credit') {      // Credit card logic    } else if (payment.type === 'paypal') {      // PayPal logic    } else if (payment.type === 'crypto') {      // Crypto logic - had to modify existing code!    }  }}
// ✅ Open for extension, closed for modificationinterface PaymentMethod {  process(amount: number): Promise<PaymentResult>;}
class CreditCardPayment implements PaymentMethod {  async process(amount: number): Promise<PaymentResult> { /* ... */ }}
class PayPalPayment implements PaymentMethod {  async process(amount: number): Promise<PaymentResult> { /* ... */ }}
// New payment method - no modification to existing codeclass CryptoPayment implements PaymentMethod {  async process(amount: number): Promise<PaymentResult> { /* ... */ }}
class PaymentProcessor {  constructor(private method: PaymentMethod) {}
  async process(amount: number): Promise<PaymentResult> {    return this.method.process(amount);  }}

Liskov Substitution Principle

typescript
// ❌ Violates LSP - Square breaks Rectangle contractclass Rectangle {  constructor(public width: number, public height: number) {}
  setWidth(w: number) { this.width = w; }  setHeight(h: number) { this.height = h; }  getArea() { return this.width * this.height; }}
class Square extends Rectangle {  setWidth(w: number) {    this.width = w;    this.height = w; // Unexpected side effect!  }  setHeight(h: number) {    this.width = h;    this.height = h; // Unexpected side effect!  }}
// ✅ Proper abstractioninterface Shape {  getArea(): number;}
class Rectangle implements Shape {  constructor(private width: number, private height: number) {}  getArea() { return this.width * this.height; }}
class Square implements Shape {  constructor(private side: number) {}  getArea() { return this.side * this.side; }}

Interface Segregation Principle

typescript
// ❌ Fat interfaceinterface Worker {  work(): void;  eat(): void;  sleep(): void;  attendMeeting(): void;  writeReport(): void;}
// Robot can't eat or sleep!class Robot implements Worker {  work() { /* ... */ }  eat() { throw new Error('Robots do not eat'); }  // Forced to implement  sleep() { throw new Error('Robots do not sleep'); }  // ...}
// ✅ Segregated interfacesinterface Workable {  work(): void;}
interface Eatable {  eat(): void;}
interface Sleepable {  sleep(): void;}
class Human implements Workable, Eatable, Sleepable {  work() { /* ... */ }  eat() { /* ... */ }  sleep() { /* ... */ }}
class Robot implements Workable {  work() { /* ... */ }}

Dependency Inversion Principle

typescript
// ❌ High-level depends on low-levelclass OrderService {  private db = new MySQLDatabase();  // Concrete dependency  private mailer = new SendGridMailer();  // Concrete dependency
  createOrder(data: OrderData) {    const order = this.db.insert('orders', data);    this.mailer.send(data.email, 'Order confirmed');    return order;  }}
// ✅ Depend on abstractionsinterface Database {  insert(table: string, data: unknown): unknown;  find(table: string, query: unknown): unknown[];}
interface Mailer {  send(to: string, message: string): void;}
class OrderService {  constructor(    private db: Database,    private mailer: Mailer  ) {}
  createOrder(data: OrderData) {    const order = this.db.insert('orders', data);    this.mailer.send(data.email, 'Order confirmed');    return order;  }}
// Now we can inject any implementationconst service = new OrderService(  new PostgresDatabase(),  new SESMailer());

Code Review Best Practices

What to Look For

markdown
## Code Review Checklist
### Correctness- [ ] Logic is correct and handles edge cases- [ ] Error handling is appropriate- [ ] No obvious bugs or regressions
### Design- [ ] Code is at the right abstraction level- [ ] No unnecessary complexity- [ ] Follows existing patterns in codebase
### Readability- [ ] Clear naming and intent- [ ] Comments explain "why" not "what"- [ ] Code is self-documenting where possible
### Testing- [ ] Adequate test coverage- [ ] Tests are meaningful (not just coverage padding)- [ ] Edge cases are tested
### Performance- [ ] No obvious N+1 queries or inefficiencies- [ ] Appropriate data structures used- [ ] Caching considered if needed
### Security- [ ] Input validation present- [ ] No secrets in code- [ ] Authentication/authorization correct

Giving Feedback

markdown
# Good Review Comments
## ✅ Specific and actionable"This loop has O(n²) complexity. Consider using a Map for O(n) lookup."
## ✅ Explain the why"Let's extract this to a separate function - it makes the logic easierto test and the main function more readable."
## ✅ Offer alternatives"Instead of mutating the array, consider using `filter()` which returnsa new array: `const active = items.filter(i => i.active)`"
## ✅ Distinguish severity- "nit: " - Minor style issue, optional- "suggestion: " - Good to have, not blocking- "blocking: " - Must fix before merge
# Avoid
## ❌ Vague criticism"This code is confusing"
## ❌ Personal attacks"You always make this mistake"
## ❌ No explanation"Use a different approach"

Code Metrics

Cyclomatic Complexity

typescript
// High complexity (10+) - hard to test and maintainfunction processOrder(order: Order): Result {  if (order.status === 'pending') {           // +1    if (order.paymentMethod === 'card') {     // +1      if (order.amount > 1000) {              // +1        // ...      } else if (order.amount > 100) {        // +1        // ...      } else {        // ...      }    } else if (order.paymentMethod === 'cash') {  // +1      // ...    }  } else if (order.status === 'processing') {  // +1    // ...  }  // ... more branches}
// Lower complexity - extract conditionsfunction processOrder(order: Order): Result {  const processor = getProcessor(order.paymentMethod);  const tier = getPricingTier(order.amount);  return processor.process(order, tier);}

Metrics to Track

MetricTargetWhy
Cyclomatic Complexity< 10 per functionTestability
Function Length< 50 linesReadability
File Length< 400 linesMaintainability
Test Coverage> 80%Confidence
Duplication< 3%DRY principle

Linting & Formatting

ESLint Configuration

javascript
// .eslintrc.jsmodule.exports = {  extends: [    'eslint:recommended',    'plugin:@typescript-eslint/recommended',  ],  rules: {    // Prevent bugs    'no-unused-vars': 'error',    '@typescript-eslint/no-floating-promises': 'error',    '@typescript-eslint/no-misused-promises': 'error',
    // Code quality    'complexity': ['warn', 10],    'max-lines-per-function': ['warn', 50],    'max-depth': ['warn', 3],
    // Consistency    'prefer-const': 'error',    'no-var': 'error',  }};

Pre-commit Hooks

json
// package.json{  "husky": {    "hooks": {      "pre-commit": "lint-staged"    }  },  "lint-staged": {    "*.{ts,tsx}": [      "eslint --fix",      "prettier --write"    ],    "*.{json,md}": [      "prettier --write"    ]  }}

Related Skills

  • [[refactoring]] - Improving existing code
  • [[testing-strategies]] - Ensuring quality through tests
  • [[design-patterns]] - Proven solutions

Sharp Edges(常見陷阱)

這些是程式碼品質中最常見且代價最高的錯誤

SE-1: 過度工程 (Over-engineering)

  • 嚴重度: high
  • 情境: 為了「未來可能需要」而增加不必要的抽象和複雜度
  • 原因: YAGNI 原則被忽視、設計模式濫用、「萬一以後要...」的心態
  • 症狀:
    • 簡單功能需要改動 10+ 個檔案
    • 新人看不懂架構
    • 程式碼比需求複雜 10 倍
  • 檢測: Factory.*Factory|Abstract.*Abstract|interface.*\{.*\}(?=.*interface.*\{.*\})|Strategy.*Strategy
  • 解法: YAGNI(You Aren't Gonna Need It)、先寫最簡單的實作、需要時再重構

SE-2: 命名不一致

  • 嚴重度: medium
  • 情境: 同一個概念在不同地方用不同名稱,或不同概念用相似名稱
  • 原因: 沒有統一術語、多人開發沒有對齊、複製貼上沒改名
  • 症狀:
    • user, customer, client, account 指同一件事
    • 搜尋不到相關程式碼
    • 新人經常問「這個和那個有什麼差別?」
  • 檢測: (user|customer|client|account).*=.*find|(get|fetch|retrieve|load).*User
  • 解法: 建立術語表(Ubiquitous Language)、Code Review 時檢查命名、重構統一命名

SE-3: 深層巢狀 (Deep Nesting)

  • 嚴重度: medium
  • 情境: if-else 或 callback 巢狀超過 3-4 層,難以閱讀
  • 原因: 沒有提早 return、沒有抽取函數、callback hell
  • 症狀:
    • 需要橫向捲動才能看到程式碼
    • 很難追蹤哪個 } 對應哪個 {
    • Cyclomatic complexity 超高
  • 檢測: \{.*\{.*\{.*\{|if.*if.*if.*if|\.then\(.*\.then\(.*\.then\(
  • 解法: Guard clause(提早 return)、抽取函數、使用 async/await 取代 callback

SE-4: 神奇數字/字串 (Magic Numbers)

  • 嚴重度: medium
  • 情境: 程式碼中直接使用意義不明的數字或字串
  • 原因: 懶得定義常數、「只用一次不用抽出來」
  • 症狀:
    • 看到 86400 不知道是什麼(一天的秒數)
    • 修改時需要全域搜尋 replace
    • 同一個數字在不同地方意義不同但值相同
  • 檢測: \b(86400|3600|1000|60000|1024|65535)\b|status\s*===?\s*['"][^'"]+['"]
  • 解法: 抽取為有意義名稱的常數、使用 enum、集中管理設定值

SE-5: 大泥球 (Big Ball of Mud)

  • 嚴重度: critical
  • 情境: 程式碼沒有明確架構,所有東西混在一起,到處都有依賴
  • 原因: 沒有模組化設計、趕時間「先 work 再說」、缺乏重構
  • 症狀:
    • 改 A 會壞 B
    • 單一檔案 1000+ 行
    • 所有東西都 import 所有東西
  • 檢測: import.*from.*\.\.\/\.\.\/\.\.\/|require\(.*\.\..*\.\..*\.\.\)|lines.*>\s*1000
  • 解法: 分層架構、明確的模組邊界、定期重構、嚴格的 import 規則

Validations

V-1: 禁止 console.log (生產代碼)

  • 類型: regex
  • 嚴重度: medium
  • 模式: console\.(log|debug|info)\(
  • 訊息: console.log should not be in production code
  • 修復建議: Use a proper logger (winston, pino) or remove debug statements
  • 適用: *.ts, *.js

V-2: 禁止 any 類型

  • 類型: ast
  • 嚴重度: high
  • 模式: TSAnyKeyword
  • 訊息: 'any' type defeats TypeScript's type safety
  • 修復建議: Use specific type, 'unknown', or generic type parameter
  • 適用: *.ts, *.tsx

V-3: 函數參數過多

  • 類型: regex
  • 嚴重度: medium
  • 模式: function\s+\w+\s*\([^)]*,\s*[^)]*,\s*[^)]*,\s*[^)]*,\s*[^)]*\)|=>\s*\([^)]*,\s*[^)]*,\s*[^)]*,\s*[^)]*,\s*[^)]*\)
  • 訊息: Function has more than 4 parameters - consider using an object
  • 修復建議: Replace multiple params with single options object: function(options: Options)
  • 適用: *.ts, *.js

V-4: TODO 無追蹤

  • 類型: regex
  • 嚴重度: low
  • 模式: //\s*TODO(?!.*#\d|.*JIRA|.*\w+-\d+)
  • 訊息: TODO comment without tracking reference
  • 修復建議: Add issue reference: // TODO(#123): description or create ticket
  • 適用: *.ts, *.js, *.tsx, *.jsx

V-5: 深層 import 路徑

  • 類型: regex
  • 嚴重度: medium
  • 模式: import.*from\s+['"]\.\.\/\.\.\/\.\.\/|require\s*\(\s*['"]\.\.\/\.\.\/\.\.\/
  • 訊息: Deep relative imports indicate poor module boundaries
  • 修復建議: Use path aliases: import { X } from '@/modules/x'
  • 適用: *.ts, *.js, *.tsx, *.jsx

來源與署名

來源:miles990/claude-software-skills位於software-engineering/code-quality提交73632f2

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架