辣评开发过程中的经验教训(二十八)

 

经验教训概述

在辣评项目三年多的开发历程中,我们积累了大量的经验和教训。本文总结了技术选型、架构设计、团队协作、测试策略和文档维护等方面的经验,希望能为类似项目提供参考。


技术选型建议

成功的选择

1. Go 语言作为后端

优势:

  • 高性能和并发能力满足需求
  • 编译为单一二进制文件,部署简单
  • 标准库完善,第三方库生态良好
  • 静态类型减少运行时错误

经验:

// Go 的并发特性让我们轻松处理高并发场景
func ProcessComments(comments []Comment) {
    var wg sync.WaitGroup
    semaphore := make(chan struct{}, 10) // 限制并发数

    for _, comment := range comments {
        wg.Add(1)
        go func(c Comment) {
            defer wg.Done()
            semaphore <- struct{}{}
            defer func() { <-semaphore }()

            processComment(c)
        }(comment)
    }

    wg.Wait()
}

教训:

  • 初期对 goroutine 泄漏问题认识不足,导致内存占用过高
  • 解决方案:使用 context 控制 goroutine 生命周期,添加超时机制

2. Vue 3 + Vite 作为前端

优势:

  • Composition API 提供更好的代码组织
  • Vite 开发体验极佳,热更新快速
  • TypeScript 支持良好
  • Element Plus 组件库完善

经验:

<!-- Composition API 让代码更易维护 -->
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'

// 逻辑清晰,易于复用
const authStore = useAuthStore()
const isAdmin = computed(() => authStore.hasRole('admin'))

onMounted(() => {
  loadData()
})
</script>

教训:

  • 初期过度使用 Composition API,导致组件过于复杂
  • 解决方案:合理拆分组件,提取可复用的 composables

3. SQLite 作为数据库

优势:

  • 轻量级,无需独立数据库服务
  • 部署简单,适合中小型项目
  • 性能足够满足需求
  • 备份简单(单文件)

教训:

  • 并发写入性能有限
  • 解决方案:使用连接池,优化写入策略,考虑读写分离

需要改进的选择

1. 微信机器人作为初期架构

问题:

  • 依赖第三方平台,稳定性受限
  • 交互方式受限,用户体验不佳
  • 功能扩展困难

教训:

  • 应该更早地规划 Web 平台架构
  • 微信机器人可以作为辅助入口,但不应作为主要架构

2. 前端状态管理

问题:

  • 初期使用 Vuex,迁移到 Pinia 花费了不少时间
  • 状态管理过于分散,难以维护

教训:

  • 应该从一开始就使用 Pinia(Vue 3 官方推荐)
  • 合理规划状态结构,避免过度使用全局状态

架构设计心得

成功的设计

1. 前后端分离

优势:

  • 前后端可以独立开发和部署
  • 便于团队协作
  • 技术栈选择更灵活

经验:

前端(Vue 3)→ REST API → 后端(Go)→ 数据库(SQLite)

最佳实践:

  • 定义清晰的 API 接口规范
  • 使用 TypeScript 类型定义增强类型安全
  • 前后端使用相同的数据模型定义

2. 分层架构

架构层次:

表现层(Handlers)→ 业务层(Services)→ 数据层(Models/Database)

优势:

  • 职责清晰,易于维护
  • 便于单元测试
  • 易于替换实现

示例:

// Handler 层:处理 HTTP 请求
func (h *CommentHandler) CreateComment(c *gin.Context) {
    var req CreateCommentRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    // 调用 Service 层
    comment, err := h.commentService.CreateComment(c, &req)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, comment)
}

// Service 层:业务逻辑
func (s *CommentService) CreateComment(ctx context.Context, req *CreateCommentRequest) (*Comment, error) {
    // 验证业务规则
    if err := s.validateComment(req); err != nil {
        return nil, err
    }

    // 调用 Data 层
    comment := &Comment{
        UserID:       req.UserID,
        SubmissionID: req.SubmissionID,
        Content:      req.Content,
    }

    if err := s.db.Create(comment).Error; err != nil {
        return nil, err
    }

    return comment, nil
}

3. 配置化管理

优势:

  • 便于环境切换
  • 敏感信息集中管理
  • 便于定制化

最佳实践:

// config.json
{
  "environment": "production",
  "apiPort": 8888,
  "database": {
    "type": "sqlite",
    "path": "./data/laping.db"
  },
  "jwt": {
    "secret": "your-secret",
    "expireHours": 24
  },
  "system": {
    "name": "辣评",
    "logo": "/assets/logo.svg"
  }
}

需要改进的设计

1. 缓存策略

问题:

  • 初期没有考虑缓存,导致重复查询
  • 后期添加缓存时改动较大

教训:

  • 应该在设计初期就考虑缓存策略
  • 对于频繁查询的数据(如配置、统计数据)应该使用缓存

改进方案:

// 使用内存缓存
type CacheService struct {
    cache map[string]interface{}
    mutex sync.RWMutex
    ttl   time.Duration
}

func (s *CacheService) Get(key string) (interface{}, bool) {
    s.mutex.RLock()
    defer s.mutex.RUnlock()
    value, ok := s.cache[key]
    return value, ok
}

func (s *CacheService) Set(key string, value interface{}) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    s.cache[key] = value
}

2. 错误处理

问题:

  • 初期错误处理不统一
  • 错误信息对用户不友好
  • 缺少错误码规范

教训:

  • 应该定义统一的错误处理机制
  • 区分系统错误和业务错误
  • 提供友好的错误提示

改进方案:

// 定义错误码
const (
    ErrCodeSuccess         = 0
    ErrCodeInvalidParam    = 400
    ErrCodeUnauthorized    = 401
    ErrCodeForbidden       = 403
    ErrCodeNotFound        = 404
    ErrCodeInternalError   = 500
    ErrCodeDuplicateSubmit = 1001
)

// 统一错误响应
type ErrorResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
}

func HandleError(c *gin.Context, err error) {
    var resp ErrorResponse

    switch e := err.(type) {
    case *BusinessError:
        resp = ErrorResponse{
            Code:    e.Code,
            Message: e.Message,
        }
    default:
        resp = ErrorResponse{
            Code:    ErrCodeInternalError,
            Message: "服务器内部错误",
        }
        log.Printf("Internal error: %v", err)
    }

    c.JSON(resp.Code, resp)
}

团队协作经验

成功的实践

1. Git 工作流

分支策略:

master (生产环境)
  ↑
dev (开发环境)
  ↑
feature/* (功能分支)

最佳实践:

  • 功能开发在 feature 分支
  • 完成后合并到 dev 测试
  • 测试通过后合并到 master 发布

2. 代码审查

流程:

  1. 提交 Pull Request
  2. 团队成员审查代码
  3. 讨论和修改
  4. 审查通过后合并

收益:

  • 提高代码质量
  • 知识共享
  • 发现潜在问题

3. 文档先行

实践:

  • API 设计先写文档
  • 功能开发前写设计文档
  • 重要决策记录在文档中

收益:

  • 减少沟通成本
  • 便于后期维护
  • 新成员快速上手

需要改进的地方

1. 沟通效率

问题:

  • 初期沟通主要依赖微信,信息容易丢失
  • 缺少正式的需求评审流程

改进:

  • 使用专业的项目管理工具
  • 建立定期的需求评审会议
  • 重要决策记录在文档中

2. 任务管理

问题:

  • 任务分配不够明确
  • 进度跟踪不够及时

改进:

  • 使用看板管理任务
  • 每日站会同步进度
  • 定期回顾和总结

测试策略

成功的实践

1. 单元测试

覆盖范围:

  • 核心业务逻辑
  • 工具函数
  • 数据模型

示例:

func TestCalculatePoints(t *testing.T) {
    tests := []struct {
        name     string
        comments []Comment
        want     int
    }{
        {
            name: "高质量评论",
            comments: []Comment{
                {Score: 5},
                {Score: 5},
                {Score: 4},
            },
            want: 28, // 3 + 5*3 + 10
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := CalculatePoints(tt.comments)
            if got != tt.want {
                t.Errorf("got %d, want %d", got, tt.want)
            }
        })
    }
}

2. 集成测试

测试场景:

  • API 接口测试
  • 数据库操作测试
  • 权限验证测试

工具:

  • Go: httptest
  • 前端: Vitest

需要改进的地方

1. 测试覆盖率

问题:

  • 初期测试覆盖率较低
  • 部分关键功能缺少测试

改进:

  • 设定测试覆盖率目标(如 80%)
  • 新功能必须包含测试
  • 定期检查测试覆盖率

2. E2E 测试

问题:

  • 缺少端到端测试
  • 用户流程测试不足

改进:

  • 使用 Playwright 或 Cypress
  • 测试关键用户流程
  • 自动化回归测试

文档维护建议

成功的实践

1. 代码注释

原则:

  • 复杂逻辑必须注释
  • 公共 API 必须注释
  • 注释说明”为什么”而不是”是什么”

示例:

// CalculatePoints 计算用户积分
// 积分规则:
// - 每条评论 1 分
// - 评分 >= 4 的评论额外 5 分
// - 平均评分 >= 4.5 额外 10 分
func CalculatePoints(comments []Comment) int {
    points := len(comments) // 基础分

    var totalScore int
    for _, comment := range comments {
        totalScore += comment.Score
        if comment.Score >= 4 {
            points += 5 // 高质量评论奖励
        }
    }

    avgScore := float64(totalScore) / float64(len(comments))
    if avgScore >= 4.5 {
        points += 10 // 高平均分奖励
    }

    return points
}

2. API 文档

工具:

  • Swagger/OpenAPI
  • Postman Collection

内容:

  • 接口路径和方法
  • 请求参数
  • 响应格式
  • 错误码说明

3. 开发文档

包含内容:

  • 项目架构说明
  • 开发环境搭建
  • 编码规范
  • 部署流程

需要改进的地方

1. 文档更新

问题:

  • 代码更新后文档未及时更新
  • 文档与实际代码不一致

改进:

  • 代码变更时同步更新文档
  • 定期审查文档准确性
  • 使用工具自动生成文档

2. 知识沉淀

问题:

  • 开发经验未及时记录
  • 问题解决方案容易遗忘

改进:

  • 建立知识库
  • 记录常见问题和解决方案
  • 定期分享经验

总结

关键经验

  1. 技术选型
    • 选择成熟稳定的技术栈
    • 考虑团队技术储备
    • 评估长期维护成本
  2. 架构设计
    • 分层架构,职责清晰
    • 配置化管理,便于定制
    • 预留扩展空间
  3. 团队协作
    • 规范的工作流程
    • 充分的沟通交流
    • 及时的代码审查
  4. 质量保证
    • 完善的测试覆盖
    • 统一的错误处理
    • 详细的日志记录
  5. 文档维护
    • 代码即文档
    • 及时更新文档
    • 知识持续沉淀

持续改进

  • 定期回顾和总结
  • 学习新技术和最佳实践
  • 优化开发流程
  • 提升代码质量
  • 改善用户体验

这些经验教训是辣评项目宝贵的财富,也是我们持续进步的动力。希望这些总结能够帮助到其他开发者,也期待在未来的开发中继续积累和分享经验。

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 Attribution-NonCommercial 4.0 International