code-quality-gates
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCode Quality Gates — Expert Decisions
代码质量门——专业决策
Expert decision frameworks for quality gate choices. Claude knows linting tools — this skill provides judgment calls for threshold calibration and rule selection.
针对质量门选择的专业决策框架。Claude熟悉代码检查工具——本技能为阈值校准和规则选择提供专业判断建议。
Decision Trees
决策树
Which Quality Gates for Your Project
为你的项目选择合适的质量门
Project stage?
├─ Greenfield (new project)
│ └─ Enable ALL gates from day 1
│ • SwiftLint (strict)
│ • SwiftFormat
│ • SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
│ • Coverage > 80%
│
├─ Brownfield (legacy, no gates)
│ └─ Adopt incrementally:
│ 1. SwiftLint with --baseline (ignore existing)
│ 2. Format new files only
│ 3. Gradually increase thresholds
│
└─ Existing project with some gates
└─ Team size?
├─ Solo/Small (1-3) → Lint + Format sufficient
└─ Medium+ (4+) → Full pipeline
• Add coverage gates
• Add static analysis
• Add security scanningProject stage?
├─ Greenfield (new project)
│ └─ Enable ALL gates from day 1
│ • SwiftLint (strict)
│ • SwiftFormat
│ • SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
│ • Coverage > 80%
│
├─ Brownfield (legacy, no gates)
│ └─ Adopt incrementally:
│ 1. SwiftLint with --baseline (ignore existing)
│ 2. Format new files only
│ 3. Gradually increase thresholds
│
└─ Existing project with some gates
└─ Team size?
├─ Solo/Small (1-3) → Lint + Format sufficient
└─ Medium+ (4+) → Full pipeline
• Add coverage gates
• Add static analysis
• Add security scanningCoverage Threshold Selection
覆盖率阈值选择
What type of code?
├─ Domain/Business Logic
│ └─ 90%+ coverage required
│ • Business rules must be tested
│ • Silent bugs are expensive
│
├─ Data Layer (Repositories, Mappers)
│ └─ 85% coverage
│ • Test mapping edge cases
│ • Test error handling
│
├─ Presentation (ViewModels)
│ └─ 70-80% coverage
│ • Test state transitions
│ • Skip trivial bindings
│
└─ Views (SwiftUI)
└─ Don't measure coverage
• Snapshot tests or manual QA
• Unit testing views has poor ROIWhat type of code?
├─ Domain/Business Logic
│ └─ 90%+ coverage required
│ • Business rules must be tested
│ • Silent bugs are expensive
│
├─ Data Layer (Repositories, Mappers)
│ └─ 85% coverage
│ • Test mapping edge cases
│ • Test error handling
│
├─ Presentation (ViewModels)
│ └─ 70-80% coverage
│ • Test state transitions
│ • Skip trivial bindings
│
└─ Views (SwiftUI)
└─ Don't measure coverage
• Snapshot tests or manual QA
• Unit testing views has poor ROISwiftLint Rule Strategy
SwiftLint规则策略
Starting fresh?
├─ YES → Enable opt_in_rules aggressively
│ └─ Easier to disable than enable later
│
└─ NO → Adopting on existing codebase
└─ Use baseline approach:
1. Run: swiftlint lint --reporter json > baseline.json
2. Configure: baseline_path: baseline.json
3. New violations fail, existing ignored
4. Chip away at baseline over timeStarting fresh?
├─ YES → Enable opt_in_rules aggressively
│ └─ Easier to disable than enable later
│
└─ NO → Adopting on existing codebase
└─ Use baseline approach:
1. Run: swiftlint lint --reporter json > baseline.json
2. Configure: baseline_path: baseline.json
3. New violations fail, existing ignored
4. Chip away at baseline over timeNEVER Do
绝对不要做的事
Threshold Anti-Patterns
阈值反模式
NEVER set coverage to 100%:
yaml
undefined绝对不要将覆盖率设置为100%:
yaml
undefined❌ Blocks legitimate PRs, encourages gaming
❌ Blocks legitimate PRs, encourages gaming
MIN_COVERAGE: 100
MIN_COVERAGE: 100
✅ Realistic threshold with room for edge cases
✅ Realistic threshold with room for edge cases
MIN_COVERAGE: 80
**NEVER** use zero-tolerance for warnings initially on legacy code:
```bashMIN_COVERAGE: 80
**绝对不要**在遗留项目初期对警告采取零容忍策略:
```bash❌ 500 warnings = blocked pipeline forever
❌ 500 warnings = blocked pipeline forever
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES # On day 1 of legacy project
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES # On day 1 of legacy project
✅ Incremental adoption
✅ Incremental adoption
1. First: just report warnings
1. First: just report warnings
SWIFT_TREAT_WARNINGS_AS_ERRORS = NO
SWIFT_TREAT_WARNINGS_AS_ERRORS = NO
2. Then: require no NEW warnings
2. Then: require no NEW warnings
swiftlint lint --baseline existing-violations.json
swiftlint lint --baseline existing-violations.json
3. Finally: zero tolerance (after cleanup)
3. Finally: zero tolerance (after cleanup)
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
**NEVER** skip gates on "urgent" PRs:
```yamlSWIFT_TREAT_WARNINGS_AS_ERRORS = YES
**绝对不要**在“紧急”PR中跳过质量门:
```yaml❌ Creates precedent, gates become meaningless
❌ Creates precedent, gates become meaningless
if: github.event.pull_request.labels.contains('urgent')
run: echo "Skipping quality gates"
if: github.event.pull_request.labels.contains('urgent')
run: echo "Skipping quality gates"
✅ Gates are non-negotiable
✅ Gates are non-negotiable
If code can't pass gates, it shouldn't ship
If code can't pass gates, it shouldn't ship
undefinedundefinedSwiftLint Anti-Patterns
SwiftLint反模式
NEVER disable rules project-wide without documenting why:
yaml
undefined绝对不要在不记录原因的情况下全局禁用规则:
yaml
undefined❌ Mystery disabled rules
❌ Mystery disabled rules
disabled_rules:
- force_unwrapping
- force_cast
- line_length
disabled_rules:
- force_unwrapping
- force_cast
- line_length
✅ Document the reasoning
✅ Document the reasoning
disabled_rules:
line_length: Configured separately with custom thresholds
force_unwrapping: Using force_unwrapping opt-in rule instead (stricter)
**NEVER** use inline disables without explanation:
```swift
// ❌ No context
// swiftlint:disable force_unwrapping
let value = optionalValue!
// swiftlint:enable force_unwrapping
// ✅ Explain why exception is valid
// swiftlint:disable force_unwrapping
// Reason: fatalError path for corrupted bundle resources that should crash
let value = optionalValue!
// swiftlint:enable force_unwrappingNEVER silence all warnings in a file:
swift
// ❌ Nuclear option hides real issues
// swiftlint:disable all
// ✅ Disable specific rules for specific lines
// swiftlint:disable:next identifier_name
let x = calculateX() // Math conventiondisabled_rules:
line_length: Configured separately with custom thresholds
force_unwrapping: Using force_unwrapping opt-in rule instead (stricter)
**绝对不要**在不解释原因的情况下内联禁用规则:
```swift
// ❌ No context
// swiftlint:disable force_unwrapping
let value = optionalValue!
// swiftlint:enable force_unwrapping
// ✅ Explain why exception is valid
// swiftlint:disable force_unwrapping
// Reason: fatalError path for corrupted bundle resources that should crash
let value = optionalValue!
// swiftlint:enable force_unwrapping绝对不要静默文件中的所有警告:
swift
// ❌ Nuclear option hides real issues
// swiftlint:disable all
// ✅ Disable specific rules for specific lines
// swiftlint:disable:next identifier_name
let x = calculateX() // Math conventionCI Anti-Patterns
CI反模式
NEVER run expensive gates first:
yaml
undefined绝对不要先运行耗时的质量门:
yaml
undefined❌ Slow feedback — tests run even if lint fails
❌ Slow feedback — tests run even if lint fails
jobs:
test: # 10 minutes
...
lint: # 30 seconds
...
jobs:
test: # 10 minutes
...
lint: # 30 seconds
...
✅ Fast feedback — fail fast on cheap checks
✅ Fast feedback — fail fast on cheap checks
jobs:
lint:
runs-on: macos-14
steps: [swiftlint, swiftformat]
build:
needs: lint # Only if lint passes
...
test:
needs: build # Only if build passes
...
**NEVER** run quality gates only on PR:
```yamljobs:
lint:
runs-on: macos-14
steps: [swiftlint, swiftformat]
build:
needs: lint # Only if lint passes
...
test:
needs: build # Only if build passes
...
**绝对不要**仅在PR中运行质量门:
```yaml❌ Main branch can have violations
❌ Main branch can have violations
on:
pull_request:
branches: [main]
on:
pull_request:
branches: [main]
✅ Protect main branch too
✅ Protect main branch too
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
---on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
---Essential Configurations
必要配置
Minimal SwiftLint (Start Here)
基础SwiftLint配置(从这里开始)
yaml
undefinedyaml
undefined.swiftlint.yml — Essential rules only
.swiftlint.yml — Essential rules only
excluded:
- Pods
- .build
- DerivedData
excluded:
- Pods
- .build
- DerivedData
Most impactful opt-in rules
Most impactful opt-in rules
opt_in_rules:
- force_unwrapping # Catches crashes
- implicitly_unwrapped_optional
- empty_count # Performance
- first_where # Performance
- contains_over_first_not_nil
- fatal_error_message # Debugging
opt_in_rules:
- force_unwrapping # Catches crashes
- implicitly_unwrapped_optional
- empty_count # Performance
- first_where # Performance
- contains_over_first_not_nil
- fatal_error_message # Debugging
Sensible limits
Sensible limits
line_length:
warning: 120
error: 200
ignores_urls: true
ignores_function_declarations: true
function_body_length:
warning: 50
error: 80
cyclomatic_complexity:
warning: 10
error: 15
type_body_length:
warning: 300
error: 400
file_length:
warning: 500
error: 800
undefinedline_length:
warning: 120
error: 200
ignores_urls: true
ignores_function_declarations: true
function_body_length:
warning: 50
error: 80
cyclomatic_complexity:
warning: 10
error: 15
type_body_length:
warning: 300
error: 400
file_length:
warning: 500
error: 800
undefinedMinimal SwiftFormat
基础SwiftFormat配置
ini
undefinedini
undefined.swiftformat — Essentials only
.swiftformat — Essentials only
--swiftversion 5.9
--exclude Pods,.build,DerivedData
--indent 4
--maxwidth 120
--wraparguments before-first
--wrapparameters before-first
--swiftversion 5.9
--exclude Pods,.build,DerivedData
--indent 4
--maxwidth 120
--wraparguments before-first
--wrapparameters before-first
Non-controversial rules
Non-controversial rules
--enable sortedImports
--enable trailingCommas
--enable redundantSelf
--enable redundantReturn
--enable blankLinesAtEndOfScope
--enable sortedImports
--enable trailingCommas
--enable redundantSelf
--enable redundantReturn
--enable blankLinesAtEndOfScope
Disable controversial rules
Disable controversial rules
--disable acronyms
--disable wrapMultilineStatementBraces
undefined--disable acronyms
--disable wrapMultilineStatementBraces
undefinedXcode Build Settings (Quality Enforced)
Xcode构建设置(强制执行质量要求)
ini
undefinedini
undefinedQualityGates.xcconfig
QualityGates.xcconfig
// Fail on warnings
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
GCC_TREAT_WARNINGS_AS_ERRORS = YES
// Strict concurrency (Swift 6 prep)
SWIFT_STRICT_CONCURRENCY = complete
// Static analyzer
RUN_CLANG_STATIC_ANALYZER = YES
CLANG_STATIC_ANALYZER_MODE = deep
---// Fail on warnings
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
GCC_TREAT_WARNINGS_AS_ERRORS = YES
// Strict concurrency (Swift 6 prep)
SWIFT_STRICT_CONCURRENCY = complete
// Static analyzer
RUN_CLANG_STATIC_ANALYZER = YES
CLANG_STATIC_ANALYZER_MODE = deep
---CI Patterns
CI模式
GitHub Actions (Minimal Effective)
GitHub Actions(最简有效配置)
yaml
name: Quality Gates
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Fast gates first
lint:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- run: brew install swiftlint swiftformat
- run: swiftlint lint --strict --reporter github-actions-logging
- run: swiftformat . --lint
# Build only if lint passes
build:
needs: lint
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Build with strict warnings
run: |
xcodebuild build \
-scheme App \
-destination 'platform=iOS Simulator,name=iPhone 15' \
SWIFT_TREAT_WARNINGS_AS_ERRORS=YES
# Test only if build passes
test:
needs: build
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Test with coverage
run: |
xcodebuild test \
-scheme App \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-enableCodeCoverage YES \
-resultBundlePath TestResults.xcresult
- name: Check coverage threshold
run: |
COVERAGE=$(xcrun xccov view --report --json TestResults.xcresult | \
jq '.targets[] | select(.name | contains("App")) | .lineCoverage * 100')
echo "Coverage: ${COVERAGE}%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "❌ Coverage below 80%"
exit 1
fiyaml
name: Quality Gates
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Fast gates first
lint:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- run: brew install swiftlint swiftformat
- run: swiftlint lint --strict --reporter github-actions-logging
- run: swiftformat . --lint
# Build only if lint passes
build:
needs: lint
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Build with strict warnings
run: |
xcodebuild build \
-scheme App \
-destination 'platform=iOS Simulator,name=iPhone 15' \
SWIFT_TREAT_WARNINGS_AS_ERRORS=YES
# Test only if build passes
test:
needs: build
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Test with coverage
run: |
xcodebuild test \
-scheme App \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-enableCodeCoverage YES \
-resultBundlePath TestResults.xcresult
- name: Check coverage threshold
run: |
COVERAGE=$(xcrun xccov view --report --json TestResults.xcresult | \
jq '.targets[] | select(.name | contains("App")) | .lineCoverage * 100')
echo "Coverage: ${COVERAGE}%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "❌ Coverage below 80%"
exit 1
fiPre-commit Hook (Local Enforcement)
预提交钩子(本地强制执行)
bash
#!/bin/bashbash
#!/bin/bash.git/hooks/pre-commit
.git/hooks/pre-commit
echo "Running pre-commit quality gates..."
echo "Running pre-commit quality gates..."
SwiftLint (fast)
SwiftLint (fast)
if ! swiftlint lint --strict --quiet 2>/dev/null; then
echo "❌ SwiftLint failed"
exit 1
fi
if ! swiftlint lint --strict --quiet 2>/dev/null; then
echo "❌ SwiftLint failed"
exit 1
fi
SwiftFormat check (fast)
SwiftFormat check (fast)
if ! swiftformat . --lint 2>/dev/null; then
echo "❌ Code formatting issues. Run: swiftformat ."
exit 1
fi
echo "✅ Pre-commit checks passed"
---if ! swiftformat . --lint 2>/dev/null; then
echo "❌ Code formatting issues. Run: swiftformat ."
exit 1
fi
echo "✅ Pre-commit checks passed"
---Threshold Calibration
阈值校准
Finding the Right Coverage Threshold
找到合适的覆盖率阈值
Step 1: Measure current coverage
$ xcodebuild test -enableCodeCoverage YES ...
$ xcrun xccov view --report TestResults.xcresult
Step 2: Set threshold slightly below current
Current: 73% → Set threshold: 70%
Prevents regression without blocking
Step 3: Ratchet up over time
Week 1: 70%
Week 4: 75%
Week 8: 80%
Stop at: 80-85% (diminishing returns above)Step 1: Measure current coverage
$ xcodebuild test -enableCodeCoverage YES ...
$ xcrun xccov view --report TestResults.xcresult
Step 2: Set threshold slightly below current
Current: 73% → Set threshold: 70%
Prevents regression without blocking
Step 3: Ratchet up over time
Week 1: 70%
Week 4: 75%
Week 8: 80%
Stop at: 80-85% (diminishing returns above)SwiftLint Warning Budget
SwiftLint警告预算
yaml
undefinedyaml
undefinedStart permissive, tighten over time
Start permissive, tighten over time
Week 1
Week 1
MAX_WARNINGS: 100
MAX_WARNINGS: 100
Week 4
Week 4
MAX_WARNINGS: 50
MAX_WARNINGS: 50
Week 8
Week 8
MAX_WARNINGS: 20
MAX_WARNINGS: 20
Target (after cleanup sprint)
Target (after cleanup sprint)
MAX_WARNINGS: 0
---MAX_WARNINGS: 0
---Quick Reference
快速参考
Gate Priority Order
质量门优先级排序
| Priority | Gate | Time | ROI |
|---|---|---|---|
| 1 | SwiftLint | ~30s | High — catches bugs |
| 2 | SwiftFormat | ~15s | Medium — consistency |
| 3 | Build (0 warnings) | ~2-5m | High — compiler catches issues |
| 4 | Unit Tests | ~5-15m | High — catches regressions |
| 5 | Coverage Check | ~1m | Medium — enforces testing |
| 6 | Static Analysis | ~5-10m | Medium — deep issues |
| Priority | Gate | Time | ROI |
|---|---|---|---|
| 1 | SwiftLint | ~30s | High — catches bugs |
| 2 | SwiftFormat | ~15s | Medium — consistency |
| 3 | Build (0 warnings) | ~2-5m | High — compiler catches issues |
| 4 | Unit Tests | ~5-15m | High — catches regressions |
| 5 | Coverage Check | ~1m | Medium — enforces testing |
| 6 | Static Analysis | ~5-10m | Medium — deep issues |
Red Flags
危险信号
| Smell | Problem | Fix |
|---|---|---|
| Gates disabled for "urgent" PR | Culture problem | Gates are non-negotiable |
| 100% coverage requirement | Gaming metrics | 80-85% is optimal |
| All SwiftLint rules enabled | Too noisy | Curate impactful rules |
| Pre-commit takes > 30s | Devs skip it | Only fast checks locally |
| Different rules in CI vs local | Surprises | Same config everywhere |
| Smell | Problem | Fix |
|---|---|---|
| Gates disabled for "urgent" PR | Culture problem | Gates are non-negotiable |
| 100% coverage requirement | Gaming metrics | 80-85% is optimal |
| All SwiftLint rules enabled | Too noisy | Curate impactful rules |
| Pre-commit takes > 30s | Devs skip it | Only fast checks locally |
| Different rules in CI vs local | Surprises | Same config everywhere |
SwiftLint Rules Worth Enabling
值得启用的SwiftLint规则
| Rule | Why |
|---|---|
| Prevents crashes |
| Prevents crashes |
| Performance (O(1) vs O(n)) |
| Performance |
| Better crash logs |
| Prevents crashes |
| Code hygiene |
| Rule | Why |
|---|---|
| Prevents crashes |
| Prevents crashes |
| Performance (O(1) vs O(n)) |
| Performance |
| Better crash logs |
| Prevents crashes |
| Code hygiene |
SwiftLint Rules to Avoid
应避免的SwiftLint规则
| Rule | Why Avoid |
|---|---|
| Swift has inference for a reason |
| Team preference varies |
| Outdated convention |
| Rarely adds value |
| Rule | Why Avoid |
|---|---|
| Swift has inference for a reason |
| Team preference varies |
| Outdated convention |
| Rarely adds value |