开发
软件开发相关知识
Spec驱动开发(SDD)实战:用OpenSpec和Cursor Rules告别AI编程混乱
为什么"氛围编程"不够用?
项目越大,AI编程的问题越明显: - 每次请求孤立,AI不知道系统整体设计 - 两次生成的代码风格、模式完全不同 - "改了这里,坏了那里"的副作用频繁
SDD的核心:先写规范,再让AI生成实现。
一、OpenSpec YAML定义
# specs/auth.spec.yaml
name: UserAuthentication
version: "1.0"
constraints:
- "密码必须使用bcrypt(cost=12),禁止明文或MD5"
- "JWT Token:Access 15分钟,Refresh 7天"
- "登录失败5次后锁定30分钟"
- "禁止在日志中输出password、token等敏感信息"
endpoints:
- id: login
path: "/api/v1/auth/login"
method: POST
request:
body:
email: {type: string, format: email, required: true}
password: {type: string, minLength: 8, required: true}
response:
success:
status: 200
body:
access_token: {type: string}
expires_in: {type: integer, unit: seconds}
errors:
- status: 401
code: "INVALID_CREDENTIALS"
- status: 429
code: "ACCOUNT_LOCKED"
test_cases:
- name: "正常登录"
given: {email: "test@example.com", password: "ValidPass123"}
expect: {status: 200, has_fields: ["access_token"]}
- name: "错误密码"
given: {email: "test@example.com", password: "WrongPass"}
expect: {status: 401, body.code: "INVALID_CREDENTIALS"}
- name: "账号锁定"
setup: "登录失败5次"
expect: {status: 429}
# 基于Spec生成代码
openspec generate --spec specs/auth.spec.yaml --framework fastapi --output src/auth/
# 自动生成API+测试文件
二、Cursor Rules项目级上下文
<!-- .cursor/rules/auth.mdc -->
---
globs: src/auth/**
alwaysApply: true
---
## 强制约束
- 密码:passlib.hash.bcrypt.hash(password, rounds=12)
- JWT:python-jose,算法固定HS256
- 认证失败返回通用错误,不透露是用户名还是密码错
## 当前实现
- Access Token: 15分钟 | Refresh Token: 7天
- 账号锁定:5次失败→Redis存储30分钟锁定
## 目录规范
- models.py:只放Pydantic模型
- service.py:业务逻辑+依赖注入
- router.py:只处理HTTP层
## 禁止操作
- 禁止在日志输出password/token/session_id
- 禁止直接导入config.py中的密钥(用DI)
三、SDD与TDD结合(自动生成测试)
# OpenSpec自动生成的测试套件
class TestUserAuthentication:
async def test_login_success(self, client, create_user):
user = await create_user(email="test@example.com", password="ValidPass123")
resp = await client.post("/api/v1/auth/login",
json={"email":"test@example.com","password":"ValidPass123"})
assert resp.status_code == 200
assert "access_token" in resp.json()
async def test_login_wrong_password(self, client, create_user):
await create_user(email="test@example.com", password="ValidPass123")
resp = await client.post("/api/v1/auth/login",
json={"email":"test@example.com","password":"WrongPassword"})
assert resp.status_code == 401
assert resp.json()["code"] == "INVALID_CREDENTIALS"
# 验证不透露具体失败原因
assert "密码" not in resp.json()["message"]
四、SDD的实际收益
实测数据(认证模块重构项目): - AI生成代码格式一致性:73% → 97% - 因AI改A坏B导致的回归bug:减少60% - 新人理解系统设计的时间:减少40%
建议从最复杂、最容易出错的核心模块开始引入SDD,积累经验再推广。