🎯 代码质量标准
代码质量是 lzt 项目的核心要求,本文档定义了代码质量标准、检查工具和持续改进流程。
📊 质量指标
核心质量指标
质量基准
- 测试覆盖率:
pkg/包必须达到 85% 以上 - 圈复杂度: 单个函数不超过 10
- 函数长度: 不超过 50 行
- 文件长度: 不超过 500 行
- import 深度: 不超过 3 层
🔍 静态代码分析
Go 工具链
bash
# 格式化检查
go fmt ./...
goimports -w .
gofumpt -w .
# 代码检查
go vet ./...
golint ./...
golangci-lint run
# 安全检查
gosec ./...
# 依赖检查
go mod verify
go mod tidy配置文件
yaml
# .golangci.yml
run:
timeout: 5m
tests: true
modules-download-mode: readonly
linters-settings:
gocyclo:
min-complexity: 10
goconst:
min-len: 2
min-occurrences: 2
dupl:
threshold: 100
funlen:
lines: 50
statements: 40
lll:
line-length: 120
linters:
enable:
- gofmt
- goimports
- golint
- govet
- ineffassign
- misspell
- gocyclo
- goconst
- dupl
- funlen
- lll
- unconvert
- unparam
- unused📝 代码规范检查
命名规范验证
go
// ✅ 良好的命名
type TransactionService struct {
repository TransactionRepository
validator TransactionValidator
idGen IDGenerator
}
func (s *TransactionService) CreateTransaction(ctx context.Context, req *CreateTransactionRequest) (*Transaction, error) {
if err := s.validator.Validate(req); err != nil {
return nil, fmt.Errorf("validation failed: %w", err)
}
tx := &Transaction{
ID: s.idGen.Generate(),
LedgerID: req.LedgerID,
Type: req.Type,
Amount: req.Amount,
Description: req.Description,
CreatedAt: time.Now(),
}
if err := s.repository.Create(ctx, tx); err != nil {
return nil, fmt.Errorf("create transaction failed: %w", err)
}
return tx, nil
}
// ❌ 需要改进的命名
type TxSvc struct {
repo Repository // 不够具体
val Validator // 缩写不清晰
gen Generator // 含义模糊
}
func (s *TxSvc) Create(ctx context.Context, req interface{}) (interface{}, error) {
// 参数类型过于通用
// 函数名不够描述性
}复杂度控制
go
// ✅ 简单易懂的函数
func ValidateTransactionRequest(req *CreateTransactionRequest) error {
if req == nil {
return errors.New("request cannot be nil")
}
if req.LedgerID == "" {
return errors.New("ledger_id is required")
}
if req.Amount.Amount <= 0 {
return errors.New("amount must be positive")
}
if req.Amount.Currency == "" {
return errors.New("currency is required")
}
return nil
}
// ❌ 复杂度过高的函数
func ProcessComplexTransaction(req *ComplexRequest) (*ComplexResponse, error) {
// 大量的 if/else 嵌套
// 多个循环和条件判断
// 圈复杂度 > 10
// 需要重构为多个小函数
}🧪 测试质量标准
测试覆盖率要求
bash
# 检查覆盖率
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
# pkg 包必须达到 85% 覆盖率
go test -cover ./pkg/...
# 生成详细报告
go tool cover -html=coverage.out -o coverage.html测试质量评估
go
// ✅ 高质量测试
func TestCreateTransaction_ValidRequest_Success(t *testing.T) {
// Arrange
mockRepo := &MockTransactionRepository{}
service := NewTransactionService(mockRepo)
req := &CreateTransactionRequest{
LedgerID: "ledger-123",
Type: TransactionTypeExpense,
Amount: Money{Amount: 1000, Currency: "CNY"},
}
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*Transaction")).Return(nil)
// Act
result, err := service.CreateTransaction(context.Background(), req)
// Assert
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, req.LedgerID, result.LedgerID)
assert.Equal(t, req.Type, result.Type)
assert.Equal(t, req.Amount, result.Amount)
assert.NotEmpty(t, result.ID)
mockRepo.AssertExpectations(t)
}
// 测试边界条件
func TestCreateTransaction_InvalidAmount_ReturnsError(t *testing.T) {
service := NewTransactionService(nil)
req := &CreateTransactionRequest{
LedgerID: "ledger-123",
Type: TransactionTypeExpense,
Amount: Money{Amount: -1000, Currency: "CNY"}, // 负数金额
}
result, err := service.CreateTransaction(context.Background(), req)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "amount must be positive")
}📚 文档质量标准
代码注释规范
go
// ✅ 良好的包级注释
// Package ledger 提供账本管理的核心功能。
//
// 主要功能包括:
// - 交易记录管理
// - 预算控制
// - 数据统计分析
//
// 使用示例:
// service := ledger.NewTransactionService(repo)
// tx, err := service.CreateTransaction(ctx, req)
package ledger
// ✅ 良好的函数注释
// CreateTransaction 创建新的交易记录。
//
// 该方法验证请求参数,检查账本存在性,然后创建并保存交易记录。
// 如果验证失败或账本不存在,将返回详细的错误信息。
//
// 参数:
// ctx: 请求上下文,用于超时控制和取消
// req: 创建交易的请求参数,包含账本ID、类型、金额等
//
// 返回值:
// *Transaction: 创建成功的交易记录
// error: 创建失败时的错误信息
func (s *TransactionService) CreateTransaction(ctx context.Context, req *CreateTransactionRequest) (*Transaction, error) {
// 实现细节...
}
// ✅ 良好的类型注释
// Transaction 表示一笔财务交易记录。
//
// 交易一旦创建,核心字段(ID、金额、类型)不可修改,
// 只能修改描述、标签等辅助信息。
type Transaction struct {
ID string `json:"id" gorm:"primarykey"` // 交易唯一标识
LedgerID string `json:"ledger_id" gorm:"index"` // 所属账本ID
Type string `json:"type" gorm:"not null"` // 交易类型
Amount Money `json:"amount" gorm:"embedded"` // 交易金额
Description string `json:"description"` // 交易描述
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` // 创建时间
}API 文档标准
go
// swagger:route POST /api/v1/transactions transactions createTransaction
//
// 创建新交易
//
// 创建一笔新的财务交易记录。支持收入、支出和转账三种类型。
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Security:
// api_key:
//
// Responses:
// 201: transactionResponse
// 400: errorResponse
// 401: errorResponse
// 500: errorResponse
func (h *Handler) CreateTransaction(w http.ResponseWriter, r *http.Request) {
// 实现...
}🚀 性能质量标准
性能基准
go
// 基准测试要求
func BenchmarkCreateTransaction(b *testing.B) {
service := setupBenchmarkService()
req := &CreateTransactionRequest{
LedgerID: "bench-ledger",
Type: TransactionTypeExpense,
Amount: Money{Amount: 1000, Currency: "CNY"},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := service.CreateTransaction(context.Background(), req)
if err != nil {
b.Fatal(err)
}
}
}
// 性能目标
const (
MaxCreateTransactionLatency = 50 * time.Millisecond // 最大延迟
MaxMemoryPerOperation = 1 * 1024 // 最大内存使用 1KB
MaxAllocationsPerOperation = 10 // 最大分配次数
)内存管理
go
// ✅ 良好的内存管理
func ProcessLargeDataset(items []string) []string {
const batchSize = 1000
results := make([]string, 0, len(items)) // 预分配容量
for i := 0; i < len(items); i += batchSize {
end := i + batchSize
if end > len(items) {
end = len(items)
}
batch := items[i:end]
processed := processBatch(batch)
results = append(results, processed...)
// 显式回收大对象
if len(batch) > 500 {
runtime.GC()
}
}
return results
}
// ❌ 内存使用不当
func ProcessLargeDatasetBadly(items []string) []string {
var results []string // 没有预分配
for _, item := range items {
// 每次都创建新的临时对象
temp := make([]string, 1000)
temp[0] = processItem(item)
results = append(results, temp[0])
// temp 会在每次迭代中被丢弃,造成GC压力
}
return results
}🔄 持续改进流程
代码审查清单
markdown
## 代码审查清单
### 功能性
- [ ] 功能是否按需求正确实现
- [ ] 边界条件是否正确处理
- [ ] 错误处理是否完善
- [ ] 并发安全性是否考虑
### 可读性
- [ ] 命名是否清晰表达意图
- [ ] 代码结构是否逻辑清晰
- [ ] 注释是否必要且准确
- [ ] 复杂逻辑是否有解释
### 可维护性
- [ ] 函数是否单一职责
- [ ] 依赖是否合理
- [ ] 代码是否遵循现有模式
- [ ] 是否容易扩展
### 测试
- [ ] 单元测试是否充分
- [ ] 测试用例是否覆盖主要场景
- [ ] 测试是否独立且可重复
- [ ] 测试名称是否描述性
### 性能
- [ ] 算法复杂度是否合理
- [ ] 内存使用是否优化
- [ ] 是否有明显的性能瓶颈
- [ ] 并发处理是否高效质量门禁
yaml
# .github/workflows/quality-gate.yml
name: Quality Gate
on:
pull_request:
branches: [ main ]
jobs:
quality-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.24.3'
- name: Run tests with coverage
run: |
go test -coverprofile=coverage.out ./...
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
echo "Coverage: $COVERAGE%"
if (( $(echo "$COVERAGE < 85" | bc -l) )); then
echo "Coverage $COVERAGE% is below required 85%"
exit 1
fi
- name: Run linter
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout=5m
- name: Security scan
uses: securecodewarrior/github-action-add-sarif@v1
with:
sarif-file: 'gosec-report.sarif'📊 质量监控
质量指标仪表板
go
// 质量指标收集
type QualityMetrics struct {
TestCoverage float64 `json:"test_coverage"`
CyclomaticComplexity float64 `json:"cyclomatic_complexity"`
LinesOfCode int `json:"lines_of_code"`
TechnicalDebt int `json:"technical_debt_hours"`
BugDensity float64 `json:"bugs_per_kloc"`
}
func CollectQualityMetrics() *QualityMetrics {
return &QualityMetrics{
TestCoverage: calculateTestCoverage(),
CyclomaticComplexity: calculateAverageComplexity(),
LinesOfCode: countLinesOfCode(),
TechnicalDebt: estimateTechnicalDebt(),
BugDensity: calculateBugDensity(),
}
}📚 相关资源
项目标准文档
外部参考
- Clean Code - Robert C. Martin
- Effective Go - Go 官方指南
- Code Complete - Steve McConnell
💡 质量建议: 代码质量不是一次性达成的,而是需要持续改进的过程。建立质量门禁,定期进行代码审查,关注质量指标变化趋势。