【实战+爆笑】我和AI斗智斗勇30天:一个网站背后的50+次bug修复记录

摘要

本文记录了使用Claude AI开发MCP Hub网站的真实经历,包括架构设计、无数bug修复、性能优化等环节。通过Git提交记录还原开发过程中的"崩溃瞬间",展示AI编程的真实一面——既有惊喜,也有"惊吓"。

关键词:AI编程、Claude、React、SSR地狱、Bug修复、全栈开发

1. 项目背景:一个美好的开始

6月的某天,我突然想开发一个mcp导航平台,聚合github上所有的mcp服务,并带收藏和投票功能,为了避免中途放弃,破釜沉舟,直接花了80大洋注册了域名magicmcp.net,又充值了当今最火爆的ai编程助手claude code。起初,我天真地以为有了AI助手,开发会一帆风顺。

然而,当我看到Git历史中密密麻麻的 fix: 开头的提交时,我意识到:AI也会挖坑,而且挖得很深

1.1 技术选型

经过与Claude讨论,确定技术栈:

{
  "frontend": {
    "framework": "React 19",
    "language": "TypeScript",
    "styling": "Tailwind CSS",
    "state": "Zustand",
    "data-fetching": "TanStack Query"
  },
  "backend": {
    "database": "Supabase (PostgreSQL)",
    "auth": "Clerk",
    "hosting": "Vercel"
  }
}

2. 开发初期:AI的"热情"让我措手不及

2.1 需求分析:AI比产品经理还"贪心"

与Claude的第一次对话就让我意识到,这个AI有点"过分热情":

我:需要一个简单的MCP服务发现平台
Claude:简单?不存在的!我建议加入:
1. 实时搜索(带防抖)
2. 多维度筛选
3. 7种语言国际化
4. 深色模式
5. 响应式设计
6. 粒子特效背景
7. 无限滚动
8. 骨架屏加载
9. ...(还有20多项)

我:等等,我只是想要个列表页面...
Claude:Trust me, you'll need all of these! 😎

2.2 架构设计:看起来很美

Claude给出的架构设计确实很专业:

// 项目结构
src/
├── components/          # UI组件
│   ├── Home/           # 首页组件
│   ├── Layout/         # 布局组件
│   └── Common/         # 通用组件
├── hooks/              # 自定义Hooks
├── store/              # Zustand状态管理
├── services/           # 业务逻辑
├── types/              # TypeScript类型
└── utils/              # 工具函数

但是,作为一个只会jquery的后端程序员,这是不是有点强人所难了....

3. 核心功能实现:噩梦的开始

3.1 第一个坑:SSR地狱

Claude信心满满地说:"我们需要SSR来提升SEO!"

然后,我的Git历史变成了这样:

c8784a4 fix(ssr): 修复中文页面SSR完整解决方案
34a8277 fix(ssr): 修复服务器端与客户端缓存键不匹配导致/servers页面无数据问题
9cd66c2 fix(ssr): 修复URL重建时locale重复的问题
086bd3f fix: 修复Vercel部署中SSR入口文件动态发现问题
03f58b2 fix: 修复 Vercel 部署中的 SSR 模块加载问题
eb0d714 fix(ssr): 修复SSR模板加载和slug提取问题
6dd1382 debug: 添加服务器详情页面SSR路由的详细调试日志

最痛苦的Bug:中文页面SSR问题,足足花了3天才解决:

// Claude的第一版代码
function needsSSR(pathname) {
  return pathname.includes('/servers'); // 看起来没问题?
}

// 实际运行:/zh-CN/servers?locale=zh-CN
// 结果:包含查询参数,判断失败,中文页面全部变成CSR

// 修复了5次后的最终版本
function needsSSR(pathname, searchParams) {
  // 1. 处理Vercel的URL重写
  if (searchParams.get('locale') && searchParams.get('slug')) {
    const locale = searchParams.get('locale');
    const slug = searchParams.get('slug');
    pathname = `/${locale}/servers/${slug}`;
  }
  
  // 2. 移除查询参数再判断
  const cleanPath = pathname.split('?')[0];
  
  // 3. 检查是否需要SSR
  return SSR_ROUTES.some(route => cleanPath.includes(route));
}

我:Claude,你不是说SSR很简单吗?
Claude:呃...我没想到Vercel会这样处理URL重写...

3.2 数据层设计:看似完美的陷阱

// hooks/useUnifiedData.ts
export const useServersPaginated = (
  page: number,
  limit: number,
  sortBy: string,
  sortOrder: 'asc' | 'desc',
  filters?: SearchFilters
) => {
  return useQuery({
    queryKey: ['servers', page, limit, sortBy, sortOrder, filters],
    queryFn: async () => {
      const supabase = createClient();
      let query = supabase.from('servers').select('*', { count: 'exact' });
      
      // 应用筛选
      if (filters?.category) {
        query = query.eq('category_id', filters.category);
      }
      
      // 分页
      const start = (page - 1) * limit;
      query = query.range(start, start + limit - 1);
      
      // 排序
      query = query.order(sortBy, { ascending: sortOrder === 'asc' });
      
      const { data, error, count } = await query;
      
      if (error) throw error;
      
      return {
        servers: data || [],
        totalCount: count || 0,
        currentPage: page,
        totalPages: Math.ceil((count || 0) / limit)
      };
    }
  });
};

3.3 第二个坑:批量查询引发的"无限循环地狱"

Claude:"我们来优化一下性能,用批量查询!"

结果:

// Claude的"优化"代码
const VoteButton = ({ serverId }) => {
  const { data } = useQuery({
    queryKey: ['votes', serverId],
    queryFn: () => batchQueryVotes([serverId]) // 批量查询单个??
  });
};

// 页面上有20个VoteButton
// 结果:每个按钮都触发一次批量查询
// 20个组件 × 批量查询 = 疯狂的数据库请求

Git记录再次沦陷:

384f1a4 fix(投票): 清理遗留的用户投票查询并优化批量查询
d6364d3 fix(批量查询): 修复批量查询逻辑避免重复请求
964f00e feat(投票组件): 添加调试日志以跟踪投票状态
9479ab4 fix(voting): 在getUserVote中添加调试日志

最终解决方案(在崩溃边缘想出来的):

// 使用单例模式管理批量查询
class BatchQueryManager {
  private queue: Set<string> = new Set();
  private timer: NodeJS.Timeout | null = null;
  
  addToQueue(serverId: string) {
    this.queue.add(serverId);
    this.scheduleFlush();
  }
  
  private scheduleFlush() {
    if (this.timer) return;
    
    this.timer = setTimeout(() => {
      const ids = Array.from(this.queue);
      this.queue.clear();
      this.timer = null;
      
      // 真正的批量查询
      batchQueryVotes(ids);
    }, 50); // 50ms内的请求合并
  }
}

3.4 第三个坑:认证状态的"薛定谔的猫"

用户登录状态检测本应该很简单,但是...

// Claude的第一版
const useAuth = () => {
  const { user } = useUser(); // Clerk hook
  return { isAuthenticated: !!user };
};

// 问题:用户已登录,但user在首次渲染时是null
// 结果:闪烁问题,用户看到"请登录"然后突然变成"已登录"

相关的Git提交:

c3184a9 fix(auth): 重构认证状态检测逻辑,移除受限状态
bc5ce10 feat(认证): 添加安全组件和认证状态检测
167dec3 fix(hooks): 使useFavoritesSync在没有ClerkProvider时安全使用

最搞笑的Bug:收藏同步导致的"幽灵收藏"

// 场景:用户未登录时收藏了5个项目,然后登录
// Claude的同步逻辑
useEffect(() => {
  if (user && localFavorites.length > 0) {
    // 同步本地收藏到云端
    syncToCloud(localFavorites);
    clearLocalFavorites(); // 清空本地
  }
}, [user]);

// 问题:如果同步失败怎么办?
// 结果:用户的收藏消失了!"我的收藏呢???"

修复后的版本(加了一堆保护逻辑):

const syncFavorites = async () => {
  try {
    // 1. 先备份
    const backup = [...localFavorites];
    
    // 2. 尝试同步
    const results = await syncToCloud(localFavorites);
    
    // 3. 验证同步结果
    const successCount = results.filter(r => r.success).length;
    
    // 4. 只有全部成功才清空本地
    if (successCount === localFavorites.length) {
      clearLocalFavorites();
    } else {
      // 恢复失败的项目
      console.error('部分收藏同步失败,保留本地数据');
    }
  } catch (error) {
    // 5. 出错不清空,下次再试
    console.error('同步失败,保留本地收藏');
  }
};

3.5 性能优化:从"龟速"到"可以接受"

首页加载时间:8秒 → 3秒 → 800ms

优化过程中的"惊喜"

// Claude:"我们一次性加载所有数据吧!"
const { data: servers } = useServers(); // 加载2000+条数据

// 我:为什么这么慢?
// Claude:让我加个loading...

// 我:不是,我是问为什么要加载所有数据?
// Claude:哦...让我们改成分页

最有趣的优化:Logo图片路径问题

97dda1d fix(Header): 将logo图片路径从变量改为静态路径
e11ada1 fix(Layout): 修复Header组件中logo图片的引用路径和样式
// Claude的动态路径(在生产环境失败)
<img src={`${import.meta.env.BASE_URL}logo.png`} />

// 尝试修复1:使用相对路径(还是失败)
<img src="../assets/logo.png" />

// 尝试修复2:使用public路径(终于成功)
<img src="/logo.png" />

// Claude:"原来Vite的静态资源处理是这样的..."
// 我:"你刚才不是很自信吗?"

3.6 国际化实现:七种语言的"巴别塔"

Claude:"支持7种语言很简单!"

实际情况:

9123c83 feat(i18n): 添加多语言404页面支持并优化动画效果
cb52045 feat(页脚): 添加多语言联系方式模块

最离谱的Bug:语言切换导致的"精神分裂"

// 用户报告:"我切换到中文后,有些地方还是英文"
// Claude的代码
const translation = {
  'zh-CN': {
    home: '首页',
    servers: '服务器',
    // 忘记翻译的部分...
    'server.status.active': 'Active', // 😅
    'server.status.deprecated': 'Deprecated' // 😅
  }
};

// 更离谱的是混合语言
<div>
  {t('welcome')} {/* 中文:欢迎 */}
  to MCP Hub! {/* 英文硬编码 */}
</div>

4. Supabase集成

4.1 数据库设计

-- 服务器表
CREATE TABLE servers (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  description JSONB, -- 多语言描述
  repository_url TEXT,
  stats JSONB, -- 包含stars, forks等
  category_id TEXT REFERENCES categories(id),
  tags TEXT[],
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 用户收藏表
CREATE TABLE user_favorites (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id TEXT NOT NULL, -- Clerk用户ID
  server_id TEXT REFERENCES servers(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(user_id, server_id)
);

-- 创建索引优化查询
CREATE INDEX idx_servers_category ON servers(category_id);
CREATE INDEX idx_servers_search ON servers USING gin(to_tsvector('english', name || ' ' || description));

5. 部署噩梦:Vercel说"不"

5.1 部署地狱的开始

Git提交历史揭示了真相:

086bd3f fix: 修复Vercel部署中SSR入口文件动态发现问题
13647d0 fix: 在build:vercel脚本中添加清理和重新安装依赖的步骤
03f58b2 fix: 修复 Vercel 部署中的 SSR 模块加载问题
00c6b78 fix: 修复Vercel部署后预渲染页面无法正确提供的问题
1d14e06 fix: 修复预渲染页面样式缺失问题
4b5474c fix: 修复 Vercel 部署后 SSR 预渲染页面无法正确展示的问题
aad1933 fix(vercel): 更新vercel配置和静态文件处理逻辑

最崩溃的时刻:部署成功但页面全白

// 本地:完美运行 ✅
// Vercel:白屏 ❌

// 调试了2小时后发现...
// Claude忘记告诉我Vercel的环境变量需要手动添加
console.log(process.env.VITE_SUPABASE_URL); // undefined 😭

5.2 "简化"SSR的诞生

在修复了无数SSR问题后,我做了一个决定:

// 原本的"完美"SSR(1000+行代码)
// 简化后的SSR(300行代码)

// Claude:"但是这样会损失一些功能..."
// 我:"能用就行!"

// 结果:
// - 代码复杂度降低70%
// - 部署成功率从30%提升到95%
// - 我的头发保住了

6. 开发效率对比:理想 vs 现实

6.1 预期 vs 实际

开发阶段 预期时间 实际时间 备注
架构设计 2小时 2小时 ✅ Claude确实很快
基础功能 10小时 20小时 ❌ 调试AI的代码花了一半时间
SSR实现 4小时 15小时 💀 参见上面的Git历史
Bug修复 5小时 25小时 😭 AI挖的坑,自己填
部署上线 2小时 8小时 🤯 "本地能跑"≠"线上能跑"
总计 23小时 70小时 效率提升:-204% 😂

6.2 真实的时间分配

写代码:30%
调试AI写的代码:40%
查文档理解AI的代码:20%
重写AI的代码:10%

7. 血泪教训总结

7.1 AI的真实面目

优点(是真的):

  • ✅ 代码写得快(虽然不一定对)
  • ✅ 知识面很广(虽然有时候会搞混)
  • ✅ 不会累(但会让你累)
  • ✅ 态度很好(即使写出bug也很有礼貌)

缺点(血泪经验):

  • ❌ 过度自信("这很简单"→ 15小时debug)
  • ❌ 爱炫技(简单需求复杂化)
  • ❌ 健忘(同样的错误能犯3次)
  • ❌ 不懂部署("本地能跑就行")

7.2 使用AI的正确姿势

// ❌ 错误示范
我:帮我写个完整的网站
Claude:好的!(然后给你一个需要debug 30小时的项目)

// ✅ 正确示范
我:帮我写个分页组件,要简单的,能用就行
Claude:好的!(给你一个真的能用的组件)

7.3 我学到的真理

  1. AI的代码要当草稿看,不是成品
  2. 简单 > 完美,特别是AI建议加功能时
  3. 本地测试 + 生产测试 + 祈祷 = 部署流程
  4. Git commit记录是你调试时的救命稻草
  5. AI说"这很简单"时,预留3倍时间

7.4 意外的收获

虽然过程很痛苦,但是:

  • 学会了如何快速调试未知代码
  • 对各种框架的坑了如指掌
  • Git使用技能MAX
  • 心态变好了(什么bug没见过)

8. 项目最终成果

经历了无数个"fix:"提交后:

  • ✅ 网站终于上线了
  • ✅ 功能都实现了(虽然和最初设想不太一样)
  • ✅ 性能还不错(简化后的)
  • ✅ 代码可维护(重写后的)
  • ⚠️ 头发少了一些
  • ❓ 不确定是否还会用AI写代码(claude code太贵了)

9. 写在最后

给想尝试AI编程的朋友们

  1. 要有心理准备,AI是工具不是魔法
  2. 保持怀疑,特别是AI说"很简单"的时候
  3. 学会阅读和调试代码比让AI写代码更重要
  4. 记得备份,记得commit,记得保存
  5. 保持幽默感,你会需要的

最后的最后

这个项目让我明白了一个道理:

"AI不会取代程序员,但会取代不会调试AI代码的程序员。"

现在,我既会写代码,又会调试AI的代码,我是不是进化了?🤔


P.S. 这篇文章是我自己写的,没让Claude帮忙。因为我怕它又给我加什么"优化"...


看看AI写的网站http://magicmcp.net

技术交流:欢迎在评论区讨论AI辅助开发的经验和问题。

posted on 2025-07-11 17:11  DevFreedom  阅读(34)  评论(0)    收藏  举报