Skip to main content
โšก Calmops

Code Quality Metrics: Measuring and Improving Code

Code quality metrics help measure and improve software maintainability. This comprehensive guide covers essential metrics and tools.

Why Code Quality Matters

flowchart TD
    A[High Quality Code] --> B[Easy to maintain]
    A --> C[Fewer bugs]
    A --> D[Faster development]
    A --> E[Better team collaboration]
    
    F[Low Quality Code] --> G[Technical debt]
    F --> H[Frequent bugs]
    F --> I[Slow development]
    F --> J[Frustrated team]

Complexity Metrics

Cyclomatic Complexity

// Simple function - complexity 1
function isActive(user) {
  return user.status === 'active';
}

// More complex - complexity 4
function canEdit(user, resource) {
  if (user.isAdmin) {      // 1
    return true;
  } else if (user.isOwner(resource)) {  // 2
    return true;
  } else if (user.hasPermission('edit')) {  // 3
    return resource.status === 'published';
  }
  return false;
}

// Even worse - consider refactoring
function processOrder(order) {
  if (order.status === 'pending') {    // 1
    if (order.payment) {                 // 2
      if (order.payment.status === 'paid') {  // 3
        return processPayment(order);
      } else if (order.payment.status === 'failed') {  // 4
        return notifyFailure(order);
      }
    } else if (order.type === 'subscription') {  // 5
      return processSubscription(order);
    }
  } else if (order.status === 'completed') {  // 6
    return ship(order);
  }
  return null;
}

Cognitive Complexity

// Low cognitive complexity
function getStatus(user) {
  return user.isActive ? 'Active' : 'Inactive';
}

// High cognitive complexity - hard to understand
function calculateTotal(order) {
  let total = 0;
  if (order.items) {                      // Nesting
    for (const item of order.items) {    // Loop + nesting
      if (item.price) {                   // Nesting
        if (item.quantity > 10) {        // Deep nesting
          total += item.price * item.quantity * 0.9;
        } else {
          total += item.price * item.quantity;
        }
      }
    }
  }
  if (order.discount) {                  // More nesting
    total -= total * order.discount;
  }
  return total;                            // Hard to trace
}

Lines of Code

// Bad: Long function
function processUserData(user) {  // 50 lines
  // validation
  // transformation
  // storage
  // notification
  // logging
}

// Good: Short, focused functions
function processUserData(user) {
  validateUser(user);
  transformUser(user);
  saveUser(user);
  notifyUser(user);
  logUserAction(user);
}

Maintainability Index

SonarQube Metrics

# sonarqube.yml analysis
sonar.projectKey=my-project
sonar.sources=src
sonar.tests=test
sonar.coverage.jacoco.xmlReportPath=target/jacoco.xml
sonar.issuesReport.html.enable=true

Quality Gates

# Quality gate thresholds
quality_gate:
  bugs:
    - critical: 0
    - major: 0
  security:
    - vulnerabilities: 0
  maintainability:
    - rating: A
    - technical_debt: < 5%
  coverage:
    - line_coverage: > 80%

Linting

ESLint Configuration

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended'
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: { jsx: true },
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: ['react', '@typescript-eslint'],
  rules: {
    'no-console': 'warn',
    'no-unused-vars': 'error',
    'prefer-const': 'error',
    'max-len': ['error', { code: 100 }],
    'complexity': ['error', 10]
  }
};

Pre-commit Hooks

# .husky/pre-commit
npm run lint
npm run test
npm run typecheck
# .husky/pre-commit
- name: Lint
  command: npm run lint
- name: Test
  command: npm test
- name: Check types
  command: npm run typecheck

Static Analysis

TypeScript

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Snyk (Security)

# .snyk
version: 1
exclude:
  - '*.spec.js'
  - 'test/**'
policy:
  critical:
    - '*'
# Run snyk
snyk test
snyk monitor

Code Coverage

Jest Coverage

// jest.config.js
module.exports = {
  collectCoverage: true,
  coverageThreshold: {
    'global': {
      branches: 70,
      functions: 70,
      lines: 70,
      statements: 70
    },
    './src/utils/': {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  coverageReporters: ['text', 'lcov', 'html'],
  coverageDirectory: 'coverage'
};

Coverage Reports

# Generate coverage
npm test -- --coverage

# View in CI
- name: Upload coverage
  uses: codecov/codecov-action@v3
  with:
    files: ./coverage/lcov.info
    flags: unittests

Code Review Metrics

What to Look For

## Code Review Checklist

### Readability
- [ ] Variable names are descriptive
- [ ] Functions are small and focused
- [ ] No magic numbers
- [ ] Complex logic is documented

### Maintainability
- [ ] DRY - no duplication
- [ ] Functions follow single responsibility
- [ ] Dependencies are clear
- [ ] Error handling is consistent

### Performance
- [ ] No unnecessary loops
- [ ] Proper data structures used
- [ ] Database queries are optimized

### Security
- [ ] Input validation
- [ ] No sensitive data exposed
- [ ] Proper authentication
- [ ] SQL injection prevention

Review Metrics

# Track review metrics
review_metrics:
  avg_review_time: 2.5 hours
  review_comments_per_pr: 8
  approval_time_p80: 4 hours
  re_revision_rate: 15%

Technical Debt

Tracking Debt

// TODO comments with context
// TODO(chore): Refactor to use Result pattern - 2024-01
// TODO(performance): Add caching layer - 2 days
// TODO(security): Implement rate limiting - 4 hours

Debt Ratio

# SonarQube
technical_debt:
  ratio: 5%
  effort_to_remediation: 2 days
  
# Acceptable thresholds
- Maintainability rating: A
- Technical debt ratio: < 5%
- Debt remediation effort: < 1 sprint

Automated Quality Gates

GitHub Actions Gate

name: Quality Gate

on:
  pull_request:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Lint
        run: npm run lint
        
      - name: Type Check
        run: npm run typecheck
        
      - name: Test with Coverage
        run: npm test -- --coverage
        
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          
      - name: Check Coverage
        run: |
          COVERAGE=$(npm run test:coverage -- --json | jq '.total.lines.pct')
          if [ "$COVERAGE" -lt 80 ]; then
            echo "Coverage $COVERAGE% is below 80%"
            exit 1
          fi

Best Practices

Do

// DO: Keep functions small
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// DO: Use clear naming
function calculateOrderTotalWithDiscount(orderItems, discountCode) { }

// DO: Write tests
describe('calculateTotal', () => {
  it('should sum all item prices', () => {
    expect(calculateTotal([{price: 10}, {price: 20}])).toBe(30);
  });
});

Don’t

// DON'T: Use magic numbers
if (status === 2) { }  // What is 2?

// DON'T: Create God objects
class UserManagerOrderManagerPaymentManager { }

// DON'T: Ignore warnings
// linter: disable-next-line - Why disabled?

Tools Comparison

Tool Purpose Type
ESLint JavaScript linting Static
Prettier Code formatting Static
TypeScript Type checking Static
SonarQube Quality analysis Static + Dynamic
Snyk Security scanning Static
Coveralls Coverage tracking Dynamic

External Resources

Conclusion

Code quality metrics help teams:

  • Identify problems early
  • Maintain consistent standards
  • Reduce technical debt
  • Improve maintainability

Key metrics to track:

  • Complexity (cyclomatic, cognitive)
  • Code coverage
  • Security vulnerabilities
  • Maintainability index

Automate quality gates to catch issues before they reach production.

Comments