AI 结对编程工作流:我和 Claude 配合开发了一个 SaaS 产品
过去 3 个月,我用 AI 结对编程的方式开发了一个完整的 SaaS 产品。从需求分析到上线,AI 参与了 80% 的代码编写。
这篇文章分享我的工作流和实战经验。
产品概述
一个 AI 写作助手 SaaS,功能包括:
- 用户注册/登录
- AI 文章生成
- 文章管理和编辑
- 团队协作
- 付费订阅
技术栈:Next.js + TypeScript + Supabase + Stripe
工作流设计
角色分工
| 角色 | 职责 |
|------|------|
| 我(人类) | 需求分析、架构设计、代码审查、部署 |
| AI(Claude) | 代码实现、测试编写、文档生成、Bug 修复 |
日常流程
1. 需求分析(我)
↓
2. 技术方案(我 + AI 讨论)
↓
3. 代码实现(AI 写,我审查)
↓
4. 测试(AI 写测试,我验证)
↓
5. 部署(我操作)实战案例
案例 1:用户认证模块
我的输入:
实现用户认证模块,要求:
1. 使用 Supabase Auth
2. 支持邮箱和 Google 登录
3. 登录后跳转到 dashboard
4. 未登录访问受保护页面时重定向到登录页AI 生成的代码:
// src/lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// src/middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const supabase = createMiddlewareClient({ req, res });
const { data: { session } } = await supabase.auth.getSession();
// 受保护路由
if (req.nextUrl.pathname.startsWith('/dashboard')) {
if (!session) {
return NextResponse.redirect(new URL('/login', req.url));
}
}
// 已登录用户访问登录页,重定向到 dashboard
if (req.nextUrl.pathname === '/login' && session) {
return NextResponse.redirect(new URL('/dashboard', req.url));
}
return res;
}
export const config = {
matcher: ['/dashboard/:path*', '/login']
};我的审查意见:
- 代码结构清晰
- 但缺少错误处理
- 需要添加 loading 状态
AI 修改后:
// 添加了错误边界和 loading 状态
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
setUser(session?.user ?? null);
setLoading(false);
}
);
return () => subscription.unsubscribe();
}, []);
if (loading) {
return <LoadingSpinner />;
}
return (
<AuthContext.Provider value={{ user, loading }}>
{children}
</AuthContext.Provider>
);
}案例 2:AI 文章生成
我的输入:
实现 AI 文章生成功能:
1. 用户输入主题和关键词
2. 调用 OpenAI API 生成文章
3. 支持流式输出
4. 生成完成后保存到数据库AI 生成的代码:
// src/app/api/generate/route.ts
import { NextRequest } from 'next/server';
import OpenAI from 'openai';
import { supabase } from '@/lib/supabase';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function POST(req: NextRequest) {
const { topic, keywords, userId } = await req.json();
// 创建流式响应
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
try {
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: '你是一个专业的文章写作助手。'
},
{
role: 'user',
content: `请根据以下信息写一篇文章:
主题:${topic}
关键词:${keywords.join(', ')}
`
}
],
stream: true,
});
let fullContent = '';
for await (const chunk of completion) {
const content = chunk.choices[0]?.delta?.content || '';
fullContent += content;
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\n\n`));
}
// 保存到数据库
const { data, error } = await supabase
.from('articles')
.insert({
user_id: userId,
topic,
keywords,
content: fullContent,
status: 'draft'
})
.select()
.single();
if (error) throw error;
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
done: true,
articleId: data.id
})}\n\n`));
} catch (error) {
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
error: '生成失败'
})}\n\n`));
}
controller.close();
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}// src/components/ArticleGenerator.tsx
'use client';
import { useState } from 'react';
export function ArticleGenerator() {
const [topic, setTopic] = useState('');
const [keywords, setKeywords] = useState('');
const [content, setContent] = useState('');
const [generating, setGenerating] = useState(false);
const handleGenerate = async () => {
setGenerating(true);
setContent('');
const response = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
topic,
keywords: keywords.split(',').map(k => k.trim()),
userId: 'current-user-id'
}),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\n').filter(line => line.startsWith('data: '));
for (const line of lines) {
const data = JSON.parse(line.slice(6));
if (data.content) {
setContent(prev => prev + data.content);
}
if (data.done) {
setGenerating(false);
}
if (data.error) {
alert(data.error);
setGenerating(false);
}
}
}
};
return (
<div className="space-y-4">
<input
value={topic}
onChange={e => setTopic(e.target.value)}
placeholder="输入文章主题"
className="w-full p-2 border rounded"
/>
<input
value={keywords}
onChange={e => setKeywords(e.target.value)}
placeholder="输入关键词,用逗号分隔"
className="w-full p-2 border rounded"
/>
<button
onClick={handleGenerate}
disabled={generating || !topic}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
>
{generating ? '生成中...' : '生成文章'}
</button>
{content && (
<div className="prose max-w-none">
{content}
</div>
)}
</div>
);
}案例 3:Stripe 支付集成
我的输入:
集成 Stripe 订阅支付:
1. 月付 $19,年付 $190
2. 创建 checkout session
3. 处理 webhook 事件
4. 更新用户订阅状态AI 生成的代码:
// src/app/api/checkout/route.ts
import Stripe from 'stripe';
import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '@/lib/supabase';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-06-20',
});
const PRICES = {
monthly: 'price_monthly_xxx',
yearly: 'price_yearly_xxx',
};
export async function POST(req: NextRequest) {
const { plan, userId } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: PRICES[plan as keyof typeof PRICES],
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=true`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing?canceled=true`,
metadata: { userId },
});
return NextResponse.json({ url: session.url });
}
// src/app/api/webhook/route.ts
export async function POST(req: NextRequest) {
const body = await req.text();
const sig = req.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return NextResponse.json({ error: 'Webhook Error' }, { status: 400 });
}
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object as Stripe.Checkout.Session;
await supabase
.from('subscriptions')
.insert({
user_id: session.metadata?.userId,
stripe_subscription_id: session.subscription as string,
status: 'active',
});
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
await supabase
.from('subscriptions')
.update({ status: 'canceled' })
.eq('stripe_subscription_id', subscription.id);
break;
}
}
return NextResponse.json({ received: true });
}效率数据
3 个月的开发数据:
| 指标 | 数值 |
|------|------|
| 总代码行数 | 28,500 行 |
| AI 生成比例 | 82% |
| 人工修改比例 | 18% |
| 开发周期 | 12 周 |
| 单人开发 | 是 |
如果没有 AI,估计需要 6-8 个月。
经验总结
有效的做法
1. 明确需求:给 AI 清晰的指令
2. 分步实现:大功能拆成小任务
3. 及时审查:每段代码都要审查
4. 持续测试:边写边测
需要避免的
1. 盲目信任:AI 生成的代码不一定正确
2. 一次太多:一次让 AI 写太多容易出错
3. 忽略边界:AI 经常忽略边界条件
4. 跳过测试:一定要写测试
总结
AI 结对编程可以大幅提升开发效率,但需要正确的工作流。
关键原则:
1. 人类负责决策和审查
2. AI 负责实现和执行
3. 保持紧密的反馈循环
4. 不要跳过任何审查步骤
开发时间:2026年4月-7月
产品状态:已上线
用户数:500+
#AI编程 #结对编程 #SaaS #开发效率
读者评论 2