
本文档系统性地阐述了从“Vibe Coding”理念出发,结合 Harness 平台与规范驱动开发(SDD,Specification-Driven Development)方法论,构建现代化全栈应用的技术路径与实践方案。
Vibe Coding 是一种以“直觉驱动 + 快速迭代”为特征的开发方式,强调开发者在编码过程中的流畅体验、即时反馈和低心智负担。其核心特征包括:
特征 | 描述 |
|---|---|
即时反馈 | 代码变更后秒级看到效果 |
极简配置 | 零配置或约定优于配置 |
热重载 | 开发过程中无需手动重启服务 |
类型安全 | 借助 TypeScript 等语言减少运行时错误 |
AI 辅助 | 利用 AI 编程助手提升编码效率 |
代表性技术栈:
规范驱动开发(SDD) 是在 Vibe Coding 基础上引入“规范先行”的理念,通过结构化的规范文档驱动代码生成和验证。
Vibe Coding → SDD
直觉驱动 → 规范先行 + 直觉辅助
个人快速原型 → 团队协作规模化
隐性知识 → 显性规范文档
手动验证 → 自动化验证Harness 是领先的持续交付与云成本管理平台,在 SDD 体系中扮演关键角色:
Harness 能力 | SDD 对应环节 |
|---|---|
Pipeline as Code | 部署规范自动执行 |
Feature Flags | 规范变更的渐进式发布 |
GitOps | 规范与基础设施同步 |
Cloud Cost Management | 规范中的成本约束执行 |
Service Reliability | 规范中的 SLO/SLI 落地 |
AI DevOps | 智能化的部署与回滚决策 |
┌─────────────────────────────────────────────────────────────────┐
│ 规范驱动开发循环 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 规范定义 │ → │ 代码生成 │ → │ 持续验证 │ → │ 部署发布 │ │
│ │ (Spec) │ │ (Gen) │ │ (Verify) │ │ (Deploy) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ ↓ ↓ ↓ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Harness 平台能力层 │ │
│ │ · Pipeline Orchestration · Feature Management │ │
│ │ · Infrastructure as Code · Cost Governance │ │
│ │ · Service Reliability · Security Governance │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘层次 | 技术选型 | SDD 规范文件格式 | Harness 集成点 |
|---|---|---|---|
规范层 | OpenAPI / AsyncAPI / Protobuf | .yaml / .proto | Harness 规范校验 |
前端 | Next.js 14+ / React 18 | component.spec.tsx | Feature Flags |
后端 | NestJS / Node.js | api.spec.ts | Pipeline 部署 |
数据库 | PostgreSQL + Prisma | schema.prisma | 迁移管理 |
测试 | Jest + Playwright | .spec.ts | 质量门禁 |
IaC | Terraform / Pulumi | .tf / .yaml | Harness GitOps |
可观测 | OpenTelemetry + Prometheus | slo.yaml | 可靠性门禁 |
📁 specs/
├── 📁 api/
│ ├── openapi.yaml # REST API 规范
│ └── asyncapi.yaml # 事件驱动规范
├── 📁 database/
│ ├── schema.prisma # 数据模型
│ └── migrations/ # 迁移规范
├── 📁 deployment/
│ ├── pipeline.yaml # Harness Pipeline 规范
│ ├── slo.yaml # 可靠性目标
│ └── cost-budget.yaml # 成本预算规范
├── 📁 frontend/
│ ├── components/ # 组件规范
│ └── design-tokens.json # 设计规范
└── 📁 tests/
├── contract/ # 契约测试规范
└── e2e/ # 端到端测试规范# specs/api/openapi.yaml
openapi: 3.1.0
info:
title: "TaskFlow API"
version: "1.0.0"
description: "任务管理系统的规范驱动 API"
paths:
/api/tasks:
get:
summary: 获取任务列表
parameters:
- name: status
in: query
schema:
$ref: "#/components/schemas/TaskStatus"
responses:
"200":
description: 成功
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
post:
summary: 创建新任务
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/TaskCreateRequest"
responses:
"201":
description: 创建成功
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
components:
schemas:
Task:
type: object
required:
- id
- title
- status
properties:
id:
type: string
format: uuid
title:
type: string
maxLength: 100
description:
type: string
status:
$ref: "#/components/schemas/TaskStatus"
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
TaskStatus:
type: string
enum:
- TODO
- IN_PROGRESS
- DONE
- ARCHIVED
TaskCreateRequest:
type: object
required:
- title
properties:
title:
type: string
maxLength: 100
description:
type: string
status:
$ref: "#/components/schemas/TaskStatus"
default: TODO使用 openapi-generator 或 @hey-api/openapi-ts 生成类型安全的 SDK:
# 生成 TypeScript 类型和客户端代码
npx @hey-api/openapi-ts \
-i specs/api/openapi.yaml \
-o src/generated \
-c axios// specs/database/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Task {
id String @id @default(cuid())
title String @db.VarChar(100)
description String? @db.Text
status TaskStatus @default(TODO)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@map("tasks")
}
enum TaskStatus {
TODO
IN_PROGRESS
DONE
ARCHIVED
}在规范骨架之上,采用 Vibe Coding 方式快速实现业务逻辑:
// src/tasks/tasks.controller.ts
import { Controller, Get, Post, Body, Query } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@ApiTags('tasks')
@Controller('api/tasks')
export class TasksController {
constructor(private readonly tasksService: TasksService) {}
@Get()
@ApiOperation({ summary: 'Get tasks filtered by status' })
@ApiResponse({ status: 200, description: 'Tasks retrieved' })
async getTasks(@Query('status') status?: string) {
return this.tasksService.getTasks(status);
}
@Post()
@ApiOperation({ summary: 'Create a new task' })
@ApiResponse({ status: 201, description: 'Task created' })
async createTask(@Body() createTaskDto: CreateTaskDto) {
return this.tasksService.createTask(createTaskDto);
}
}// src/tasks/tasks.service.ts
import { Injectable, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { TaskStatus } from '@prisma/client';
@Injectable()
export class TasksService {
constructor(private prisma: PrismaService) {}
async getTasks(status?: string) {
const where = status ? { status: status as TaskStatus } : {};
return this.prisma.task.findMany({
where,
orderBy: { createdAt: 'desc' },
});
}
async createTask(createTaskDto: CreateTaskDto) {
if (createTaskDto.title.length > 100) {
throw new BadRequestException('Title cannot exceed 100 characters');
}
return this.prisma.task.create({
data: {
title: createTaskDto.title,
description: createTaskDto.description,
status: (createTaskDto.status as TaskStatus) || TaskStatus.TODO,
},
});
}
}// app/tasks/page.tsx
'use client';
import { useState, useEffect } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Task, TaskStatus, TasksApi } from '@/generated';
export default function TaskPage() {
const queryClient = useQueryClient();
const [statusFilter, setStatusFilter] = useState<string>('');
const { data: tasks, isLoading } = useQuery({
queryKey: ['tasks', statusFilter],
queryFn: () => TasksApi.getTasks({ status: statusFilter || undefined }),
});
const createMutation = useMutation({
mutationFn: TasksApi.createTask,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['tasks'] });
},
});
return (
<div className="container mx-auto p-6">
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold">Tasks</h1>
<button
onClick={() => createMutation.mutate({ title: 'New Task' })}
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
>
Add Task
</button>
</div>
<div className="flex gap-2 mb-4">
{['', 'TODO', 'IN_PROGRESS', 'DONE', 'ARCHIVED'].map((status) => (
<button
key={status || 'all'}
onClick={() => setStatusFilter(status)}
className={`px-3 py-1 rounded-full text-sm ${
statusFilter === status
? 'bg-blue-600 text-white'
: 'bg-gray-200 text-gray-700'
}`}
>
{status || 'All'}
</button>
))}
</div>
{isLoading ? (
<div className="animate-pulse space-y-3">
{[...Array(3)].map((_, i) => (
<div key={i} className="h-20 bg-gray-200 rounded-lg"></div>
))}
</div>
) : (
<div className="space-y-3">
{tasks?.map((task) => (
<div
key={task.id}
className="p-4 border border-gray-200 rounded-lg shadow-sm"
>
<div className="flex justify-between items-start">
<div>
<h3 className="font-semibold">{task.title}</h3>
{task.description && (
<p className="text-gray-600 text-sm mt-1">
{task.description}
</p>
)}
</div>
<span
className={`px-2 py-1 rounded-full text-xs ${
task.status === 'DONE'
? 'bg-green-100 text-green-800'
: task.status === 'IN_PROGRESS'
? 'bg-yellow-100 text-yellow-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{task.status}
</span>
</div>
</div>
))}
</div>
)}
</div>
);
}// tests/contract/tasks.contract.spec.ts
import { describe, it, expect, beforeAll } from '@jest/globals';
import { TasksApi } from '@/generated';
import { setupServer } from './setup';
describe('Tasks API Contract Tests', () => {
let server: any;
beforeAll(async () => {
server = await setupServer();
});
describe('GET /api/tasks', () => {
it('should return tasks with correct schema', async () => {
const response = await TasksApi.getTasks();
expect(Array.isArray(response)).toBe(true);
response.forEach((task: any) => {
expect(task).toHaveProperty('id');
expect(task).toHaveProperty('title');
expect(task).toHaveProperty('status');
expect(['TODO', 'IN_PROGRESS', 'DONE', 'ARCHIVED']).toContain(
task.status
);
expect(task).toHaveProperty('createdAt');
expect(task).toHaveProperty('updatedAt');
});
});
it('should filter tasks by status', async () => {
const doneTasks = await TasksApi.getTasks({ status: 'DONE' });
doneTasks.forEach((task) => {
expect(task.status).toBe('DONE');
});
});
});
describe('POST /api/tasks', () => {
it('should create a task with valid data', async () => {
const newTask = await TasksApi.createTask({
title: 'Contract Test Task',
description: 'Testing via contract',
status: 'TODO',
});
expect(newTask).toHaveProperty('id');
expect(newTask.title).toBe('Contract Test Task');
expect(newTask.status).toBe('TODO');
});
it('should reject task with title > 100 chars', async () => {
await expect(
TasksApi.createTask({
title: 'A'.repeat(101),
})
).rejects.toThrow();
});
});
});# specs/deployment/pipeline.yaml
pipeline:
name: "TaskFlow Fullstack Pipeline"
identifier: "taskflow_fullstack"
projectIdentifier: "taskflow"
orgIdentifier: "default"
stages:
- stage:
name: "Build"
identifier: "build"
type: "CI"
spec:
execution:
steps:
- step:
type: "Run"
name: "Install Dependencies"
identifier: "install"
spec:
connectorRef: "docker-hub"
image: "node:20-alpine"
shell: "sh"
command: |
npm ci --cache .npm --prefer-offline
- step:
type: "Run"
name: "Type Check & Lint"
identifier: "typecheck"
spec:
connectorRef: "docker-hub"
image: "node:20-alpine"
shell: "sh"
command: |
npm run type-check
npm run lint
- step:
type: "Run"
name: "Unit & Contract Tests"
identifier: "test"
spec:
connectorRef: "docker-hub"
image: "node:20-alpine"
shell: "sh"
command: |
npm run test:ci
- step:
type: "BuildAndPushDocker"
name: "Build & Push Backend"
identifier: "build_backend"
spec:
dockerfile: "Dockerfile.backend"
context: "."
tags:
- "latest"
- "${CI_PIPELINE_ID}"
- stage:
name: "Deploy to Staging"
identifier: "deploy_staging"
type: "CD"
spec:
serviceIdentifier: "taskflow-backend"
envIdentifier: "staging"
gitOps:
type: "MANIFEST"
spec:
manifestFiles:
- "k8s/staging/*.yaml"
execution:
steps:
- step:
type: "K8sRollingDeploy"
name: "Rollout Backend"
identifier: "deploy"
spec:
skipDryRun: false
- step:
type: "Run"
name: "Run Database Migration"
identifier: "migrate"
spec:
connectorRef: "k8s-cluster"
image: "taskflow-backend:${CI_PIPELINE_ID}"
command: |
npx prisma migrate deploy
- step:
type: "Verify"
name: "Smoke Test"
identifier: "smoke_test"
spec:
type: "HTTP"
url: "https://staging.taskflow.dev/health"
responseCodes: ["200"]
- stage:
name: "Deploy to Production"
identifier: "deploy_production"
type: "CD"
spec:
serviceIdentifier: "taskflow-backend"
envIdentifier: "production"
gitOps:
type: "MANIFEST"
spec:
manifestFiles:
- "k8s/production/*.yaml"
execution:
steps:
- step:
type: "Approval"
name: "Manual Approval"
identifier: "approval"
spec:
approvers:
userGroups: ["platform-team", "tech-leads"]
timeout: "30m"
message: "Please approve production deployment"
- step:
type: "K8sRollingDeploy"
name: "Production Deployment"
identifier: "deploy"
spec:
skipDryRun: false
variables:
- name: "ENVIRONMENT"
type: "STRING"
value: "staging"
- name: "REPLICA_COUNT"
type: "NUMBER"
value: 2// src/config/feature-flags.ts
import { CfClient } from '@harnessio/ff-nodejs-server-sdk';
export class FeatureFlagService {
private client: CfClient;
constructor() {
this.client = new CfClient(
process.env.HARNESS_API_KEY!,
process.env.HARNESS_FF_ENVIRONMENT!
);
}
async initialize() {
await this.client.waitForInitialization();
}
async isEnabled(flagName: string, target: { identifier: string }) {
return this.client.boolVariation(flagName, target, false);
}
}
// Usage in task service
async getTasks(status?: string) {
const showArchived = await this.ffService.isEnabled(
'SHOW_ARCHIVED_TASKS',
{ identifier: this.currentUser.id }
);
if (!showArchived) {
where.status = { not: 'ARCHIVED' };
}
return this.prisma.task.findMany({ where });
}# specs/deployment/slo.yaml
slo:
name: "TaskFlow API SLOs"
objectives:
- name: "API Availability"
target: 99.95
window: "30d"
measurement: "availability"
service: "taskflow-backend"
- name: "API Latency (p95)"
target: 300ms
window: "7d"
measurement: "latency"
service: "taskflow-backend"
- name: "Error Rate"
target: 0.1%
window: "7d"
measurement: "error_rate"
service: "taskflow-backend"
errorBudget:
policy: "conservative"
alertThreshold: 80%
reporting:
- type: "prometheus"
endpoint: "http://prometheus.monitoring:9090"
queries:
availability: |
sum(up{service="taskflow-backend"}) / count(up{service="taskflow-backend"}) * 100
latency_p95: |
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
error_rate: |
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100┌──────────────────────────────────────────────────────────────────────┐
│ Vibe + SDD 工作流流程 │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 规范定义 │
│ └─→ 更新 OpenAPI / Schema / SLO 规范文件 │
│ │
│ 2. 代码生成 │
│ └─→ 运行代码生成器 → 生成类型/客户端/测试骨架 │
│ │
│ 3. Vibe 实现 │
│ ├─→ 实现业务逻辑 │
│ ├─→ 编写单元测试 │
│ └─→ AI 辅助编码 (Cursor/Copilot) │
│ │
│ 4. 本地验证 │
│ ├─→ 规范校验 (openapi lint) │
│ ├─→ 类型检查 (tsc) │
│ ├─→ 单元测试 (jest) │
│ └─→ 契约测试 (pact) │
│ │
│ 5. 提交 PR │
│ ├─→ 自动运行质量门禁 │
│ ├─→ 自动部署 Preview Environment │
│ └─→ 团队 Code Review │
│ │
│ 6. 合并发布 │
│ ├─→ Harness Pipeline 自动触发 │
│ ├─→ 渐进式发布 (Feature Flags) │
│ ├─→ 金丝雀部署 / 蓝绿部署 │
│ └─→ 自动验证 SLO │
│ │
└──────────────────────────────────────────────────────────────────────┘规范类型 | 版本策略 | 兼容性检查 |
|---|---|---|
OpenAPI | SemVer 2.0 | 向后兼容校验 |
数据库 Schema | 迁移版本 | 回滚可行性验证 |
SLO | 递增版本 | 目标合理性审核 |
成本预算 | 季度版本 | 实际 vs 预算偏差 |
Feature Flag | 无版本 | 生命周期管理 |
# 规范代码生成
npx @hey-api/openapi-ts -i specs/api/openapi.yaml -o src/generated
npx prisma generate
# 本地开发
npm run dev # 前端 + 后端热重载
npm run test:watch # 测试监听模式
npm run test:contract # 契约测试
# 规范验证
npx @redocly/cli lint specs/api/openapi.yaml
npx prisma validate
# 数据库迁移
npx prisma migrate dev --name add_task_priority
npx prisma migrate deploy
# 类型安全构建
npm run build
tsc --noEmit --project tsconfig.build.json# 登录 Harness
harness login --api-key <YOUR_API_KEY>
# 验证 pipeline
harness pipeline validate -f specs/deployment/pipeline.yaml
# 触发 pipeline
harness pipeline run taskflow_fullstack \
--variable ENVIRONMENT=staging \
--variable REPLICA_COUNT=3
# 查看部署状态
harness deployment list --project taskflow
# Feature Flags
harness ff create SHOW_ARCHIVED_TASKS \
--type boolean \
--default false \
--project taskflow
harness ff update SHOW_ARCHIVED_TASKS \
--environment staging \
--value true关注点 | 规范要求 | Harness 集成 |
|---|---|---|
密钥管理 | 所有密钥通过 Harness Secrets 管理 | ${secrets.getValue("DB_PASSWORD")} |
镜像扫描 | 镜像必须经过安全扫描 | Harness Security Testing |
权限最小化 | K8s RBAC 规范定义 | Harness Governance |
合规检查 | 合规策略即代码 | Harness Policy as Code |
审计日志 | 所有操作可追溯 | Harness Audit Trail |
质量门禁检查清单:
✅ 单元测试覆盖率 ≥ 80%
✅ 契约测试全部通过
✅ 规范文件格式校验通过
✅ 代码安全性扫描无高危漏洞
✅ 镜像大小 ≤ 500MB
✅ 启动时间 ≤ 10s
✅ SLO 预算消耗 ≤ 70%
✅ 金丝雀阶段错误率 ≤ 1%# specs/deployment/cost-budget.yaml
costBudget:
monthly:
compute: "$2,500"
storage: "$500"
network: "$200"
total: "$3,200"
alerts:
- threshold: 80%
action: slack_notify
- threshold: 95%
action: auto_scale_down
optimization:
autoScaling: true
rightSizing: true
idleResources: cleanup
spotInstances: allowed_for_nonprod症状:类型检查失败或运行时类型错误
解决方案:
# 重新生成代码
npm run generate:api
# 对比规范差异
npx openapi-diff specs/api/openapi.yaml src/generated/schema.ts
# 启用严格模式
strict: true in tsconfig.json错误类型 | 常见原因 | 解决方法 |
|---|---|---|
镜像拉取失败 | 凭证过期 | 更新 Harness Secret |
数据库迁移失败 | Schema 冲突 | 检查迁移顺序 |
金丝雀验证失败 | 健康检查超时 | 调整 readinessProbe |
审批超时 | 审批人未响应 | 配置备份审批人 |
// 检查点
1. Flag 是否在 Harness 控制台启用
2. 目标用户是否在目标规则中
3. 环境变量是否正确设置
4. SDK 是否成功初始化
5. 缓存是否过期Vibe Coding → 平台化 SDD → 自治式开发
个人体验 团队协作 系统自优化
手动迭代 规范驱动 智能演进
工具链分散 统一平台 全链路自动方向 | 技术 | Harness 支持 |
|---|---|---|
内部开发者平台 | Backstage | Harness Backstage 插件 |
可观测性 | Grafana + Tempo | 指标接入 Harness 仪表板 |
安全左移 | Snyk + Trivy | Harness STO 集成 |
成本优化 | CloudHealth | Harness CCM 原生态 |
GitOps 进阶 | ArgoCD + Crossplane | Harness GitOps 增强 |
□ 1. 定义 OpenAPI / AsyncAPI 规范
□ 2. 配置 Prisma Schema 并生成客户端
□ 3. 使用生成器产出类型安全的 SDK
□ 4. 实现业务逻辑(Vibe Coding)
□ 5. 编写契约测试和单元测试
□ 6. 配置 Harness Pipeline(CI + CD)
□ 7. 设置 Feature Flags
□ 8. 定义 SLO 目标
□ 9. 配置成本预算和告警
□ 10. 部署到预览环境验证
□ 11. 执行金丝雀部署到生产
□ 12. 监控 SLO 并优化工具 | 用途 |
|---|---|
OpenAPI Generator | 从 OpenAPI 生成代码 |
Prisma | ORM + Schema 管理 |
Pact | 契约测试框架 |
Redocly | OpenAPI 文档与校验 |
AsyncAPI Generator | 事件驱动代码生成 |
OpenTelemetry | 可观测性规范 |
# .harness/harness.config.yaml
version: 1
project: taskflow
org: default
connectors:
- name: docker-hub
type: docker
spec:
url: https://registry.hub.docker.com
credentials: harness-secret
- name: k8s-cluster
type: kubernetes
spec:
masterUrl: https://k8s-api.prod.internal
auth:
type: serviceAccount
serviceAccountToken: ${secrets.getValue("K8S_TOKEN")}
secrets:
- name: DB_PASSWORD
type: encrypted
value: ${secrets.getValue("database/password")}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。