四哥还会聊AI

80后突围:当程序员不再是第一个梦想,AI成了第四个梦想

第13篇-严肃产品和玩具的区别:杜绝AI的try-try-try-fallback思维,做真正的产品开发

严肃产品和玩具的区别:杜绝AI的"try-try-try-fallback"思维,做真正的产品开发

🚨 AI编程最危险的心态:把严肃产品当成玩具开发

在观察了大量AI编程项目后,我发现一个令人担忧的现象:大多数开发者正在用"玩具思维"开发严肃产品

这种"try-try-try-fallback"的AI思维模式,正在成为产品质量的隐形杀手。

🧠 玩具思维 vs 产品思维:根本性的认知差异

什么是"玩具思维"?

AI典型的玩具思维模式:

AI:这个API调用可能会失败,我加个重试机制
AI:数据库连接可能有问题,我加个fallback逻辑
AI:用户输入格式可能不对,我加多种兼容处理
AI:第三方服务可能宕机,我加个降级方案
AI:配置项可能缺失,我设置默认值

结果:代码充满了"try-try-try-fallback"的防御性逻辑

AI的内心独白:

  • "我要确保代码在任何情况下都能运行"
  • "我要考虑所有可能的边界情况"
  • "我要提供多种降级方案"
  • "我要让系统足够鲁棒"

这种思维的问题:

  • 掩盖真正的问题:用fallback掩盖了根本原因
  • 增加系统复杂度:防御性代码让维护成本暴增
  • 降低用户体验:降级功能往往功能不完整
  • 技术债务累积:短期"稳定"换来长期混乱

什么是"产品思维"?

产品思维的核心原则:

错误不可接受 → 找到根本原因 → 彻底解决问题

产品思维的实践模式:

API调用失败 → 立即调查失败原因 → 修复API或调整设计
数据库连接问题 → 检查配置和网络 → 确保连接稳定
用户输入错误 → 完善前端验证 → 教育用户正确输入
第三方服务不稳定 → 评估替代方案 → 优化服务商选择
配置缺失 → 启动时验证 → 强制配置完整

结果:问题被彻底解决,系统稳定可靠

💡 "try-try-try-fallback"的三大灾难性后果

1. 问题掩盖:真正的错误永不见天日

AI的防御性编程陷阱:

// ❌ 玩具思维:多层降级掩盖问题
async function fetchUserData(userId: string): Promise<UserData | null> {
  try {
    // 第1层:尝试主API
    const response = await fetch(`/api/users/${userId}`)
    if (response.ok) {
      return await response.json()
    }
  } catch (error) {
    console.log('Primary API failed, trying fallback...')
  }

  try {
    // 第2层:降级到缓存
    const cachedData = await cache.get(`user:${userId}`)
    if (cachedData) {
      console.log('Using cached data')
      return JSON.parse(cachedData)
    }
  } catch (error) {
    console.log('Cache failed, using mock...')
  }

  try {
    // 第3层:降级到模拟数据
    const mockUser = {
      id: userId,
      name: 'Unknown User',
      email: 'unknown@example.com',
      status: 'inactive'
    }
    console.log('Using mock data')
    return mockUser
  } catch (error) {
    console.log('All attempts failed')
    return null
  }
}

问题分析:

  • API故障被掩盖:主API的问题永远不知道
  • 用户看到假数据:用户体验极差
  • 问题无法修复:运维人员看不到真实错误
  • 技术债务累积:层层fallback让代码混乱

✅ 产品思维:让问题暴露并立即解决

// ✅ 产品思维:故障即暴露,立即修复
async function fetchUserData(userId: string): Promise<UserData> {
  // 前置验证:启动时验证API可用性
  if (!process.env.USER_API_URL) {
    throw new Error('USER_API_URL environment variable is required')
  }

  const response = await fetch(`${process.env.USER_API_URL}/users/${userId}`)

  if (!response.ok) {
    // 详细错误信息,便于问题定位
    const errorBody = await response.text()
    throw new Error(
      `User API failed: ${response.status} ${response.statusText}. ` +
      `URL: ${process.env.USER_API_URL}/users/${userId}. ` +
      `Response: ${errorBody}`
    )
  }

  const userData = await response.json()

  // 运行时数据验证
  if (!userData || typeof userData.id !== 'string') {
    throw new Error(`Invalid user data format received from API: ${JSON.stringify(userData)}`)
  }

  return userData
}

// 调用方必须处理所有可能的错误
try {
  const userData = await fetchUserData(userId)
  displayUserProfile(userData)
} catch (error) {
  // 立即显示用户友好的错误信息
  showUserError('Unable to load user profile. Please try again later.')

  // 记录详细错误用于排查
  logger.error('Failed to fetch user data', {
    userId,
    error: error instanceof Error ? error.message : 'Unknown error',
    timestamp: new Date().toISOString()
  })

  // 如果是系统错误,可以触发警报
  if (error.message.includes('500')) {
    alertSystem('User API server error', error)
  }
}

产品思维的优势:

  • 问题立即发现:API故障马上暴露
  • 快速修复响应:运维团队能立即处理
  • 用户体验一致:要么正常,要么明确错误
  • 系统健康透明:监控能看到真实状态

2. 维护成本爆炸:防御性代码的累积效应

玩具思维的维护噩梦:

// ❌ AI生成的复杂降级逻辑
class RobustUserService {
  constructor(
    private primaryApiClient: PrimaryAPIClient,
    private fallbackApiClient: FallbackAPIClient,
    private cacheService: CacheService,
    private mockDataService: MockDataService,
    private configService: ConfigService
  ) {}

  async getUser(userId: string): Promise<User | null> {
    // 复杂的重试和降级逻辑
    let lastError: Error | null = null

    // 策略1:主API + 重试
    for (let attempt = 1; attempt <= 3; attempt++) {
      try {
        const user = await this.primaryApiClient.getUser(userId)
        if (user) {
          await this.cacheService.set(`user:${userId}`, user, 300)
          return user
        }
      } catch (error) {
        lastError = error as Error
        console.warn(`Primary API attempt ${attempt} failed:`, error)
        if (attempt < 3) {
          await this.delay(1000 * attempt) // 指数退避
        }
      }
    }

    // 策略2:备选API
    try {
      const user = await this.fallbackApiClient.getUser(userId)
      if (user) {
        await this.cacheService.set(`user:${userId}`, user, 300)
        return user
      }
    } catch (error) {
      lastError = error as Error
      console.warn('Fallback API failed:', error)
    }

    // 策略3:缓存数据
    try {
      const cachedUser = await this.cacheService.get(`user:${userId}`)
      if (cachedUser) {
        console.warn('Using stale cached data')
        return cachedUser
      }
    } catch (error) {
      lastError = error as Error
      console.warn('Cache fallback failed:', error)
    }

    // 策略4:模拟数据
    try {
      const mockUser = await this.mockDataService.getMockUser(userId)
      console.warn('Using mock data - service degraded')
      return mockUser
    } catch (error) {
      lastError = error as Error
      console.error('All fallback strategies failed')
    }

    return null
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

维护成本分析:

  • 代码复杂度:150行复杂逻辑,难以理解
  • 测试复杂度:需要测试N×M种组合
  • 调试困难:不知道哪一层出了问题
  • 新功能添加:需要修改所有降级分支

✅ 产品思维的简洁设计:

// ✅ 简单直接,故障即暴露
class UserService {
  constructor(
    private apiClient: APIClient,
    private logger: Logger
  ) {}

  async getUser(userId: string): Promise<User> {
    // 前置验证
    if (!userId || typeof userId !== 'string') {
      throw new Error('Invalid user ID: must be a non-empty string')
    }

    try {
      const user = await this.apiClient.getUser(userId)

      // 数据验证
      if (!user || !user.id) {
        throw new Error(`Invalid user data received: ${JSON.stringify(user)}`)
      }

      this.logger.info(`User ${userId} retrieved successfully`)
      return user

    } catch (error) {
      this.logger.exception('Failed to retrieve user', error, { userId })
      throw new Error(`Failed to retrieve user ${userId}: ${error instanceof Error ? error.message : 'Unknown error'}`)
    }
  }
}

产品思维的优势:

  • 代码简洁:20行核心逻辑,易于理解
  • 测试简单:只需要测试正常和异常情况
  • 调试容易:错误信息明确,快速定位
  • 维护成本低:修改风险小,影响范围清晰

3. 用户体验灾难:降级功能的"半成品"体验

玩具思维的用户体验问题:

// ❌ 降级导致的用户体验灾难
async function loadUserProfile(userId: string) {
  const userProfile = await fetchUserData(userId) // 使用上面的复杂降级逻辑

  if (userProfile) {
    // 显示用户资料
    displayProfile(userProfile)
  } else {
    // 显示空页面或错误信息
    showEmptyState()
  }
}

// 用户可能看到的情况:
// 1. 正常用户资料 ✅
// 2. 缓存的旧数据 ⚠️ (信息过时)
// 3. 模拟的假数据 ❌ (名字是"Unknown User")
// 4. 空白页面 ❌ (不知道哪里出错了)
// 5. 加载状态持续不消失 ❌ (降级逻辑卡住)

用户体验的灾难性后果:

  • 数据不一致:用户看到错误的信息
  • 功能不可预测:同样的操作产生不同结果
  • 信任度下降:用户不相信系统的可靠性
  • 支持成本增加:用户反馈大量"奇怪"问题

✅ 产品思维的用户体验:

// ✅ 明确的状态和错误处理
async function loadUserProfile(userId: string) {
  try {
    // 显示加载状态
    showLoadingState('Loading user profile...')

    const userProfile = await fetchUserData(userId) // 简单直接的调用

    // 验证数据完整性
    if (!userProfile.email || !userProfile.name) {
      throw new Error('User profile data incomplete')
    }

    hideLoadingState()
    displayProfile(userProfile)

  } catch (error) {
    hideLoadingState()

    // 根据错误类型显示不同的提示
    if (error.message.includes('404')) {
      showError('User not found', 'The user profile you requested does not exist.')
    } else if (error.message.includes('API failed')) {
      showError('Service temporarily unavailable', 'Our user service is experiencing issues. Please try again in a few minutes.')
    } else {
      showError('Unable to load profile', 'An unexpected error occurred. Please try again later.')
    }

    // 记录用户友好的错误
    analytics.track('profile_load_failed', {
      userId,
      errorType: error.message.includes('404') ? 'not_found' : 'service_error',
      timestamp: new Date().toISOString()
    })
  }
}

产品思维的用户体验:

  • 状态明确:用户知道系统在做什么
  • 错误友好:提供清晰的错误信息和建议
  • 行为一致:相同操作总是产生相同结果
  • 可预期性:用户知道什么时候会成功或失败

🛡️ 产品开发的核心原则:Let it Crash, Fix it Fast

1. 零容忍的错误处理哲学

传统思维 vs 产品思维:

// ❌ 传统思维:容错为主
async function processPayment(amount: number, cardInfo: CardInfo): Promise<PaymentResult | null> {
  try {
    const result = await paymentService.charge(amount, cardInfo)
    return result
  } catch (error) {
    console.log('Payment failed, returning null...')
    return null // 静默失败,问题被掩盖
  }
}

// 调用方需要猜测null的含义
const result = await processPayment(100, card)
if (result) {
  showSuccess()
} else {
  // 是什么失败?网络问题?卡片无效?金额错误?
  // 用户不知道,开发者也不知道
  showGenericError()
}
// ✅ 产品思维:故障即暴露
async function processPayment(amount: number, cardInfo: CardInfo): Promise<PaymentResult> {
  // 输入验证
  if (!amount || amount <= 0) {
    throw new ValidationError('Payment amount must be positive')
  }

  if (!cardInfo || !validateCardInfo(cardInfo)) {
    throw new ValidationError('Invalid card information')
  }

  try {
    const result = await paymentService.charge(amount, cardInfo)

    // 验证结果
    if (!result || !result.transactionId) {
      throw new PaymentError('Invalid payment response from processor')
    }

    return result

  } catch (error) {
    // 详细记录错误信息
    logger.exception('Payment processing failed', error, {
      amount,
      cardLast4: cardInfo.last4,
      timestamp: new Date().toISOString()
    })

    // 重新抛出,让调用方知道具体问题
    throw error
  }
}

// 调用方能够明确处理每种错误
try {
  const result = await processPayment(100, card)
  showSuccess(`Payment successful! Transaction ID: ${result.transactionId}`)
} catch (error) {
  if (error instanceof ValidationError) {
    showFormError('Please check your payment information and try again.')
  } else if (error instanceof PaymentError) {
    showPaymentError('Payment processing failed. Please try a different payment method.')
  } else if (error.message.includes('declined')) {
    showDeclinedError('Your card was declined. Please contact your bank or try a different card.')
  } else {
    showGenericError('An unexpected error occurred. Please try again later.')
  }

  // 记录用户操作用于分析
  analytics.track('payment_failed', {
    amount,
    errorType: error.constructor.name,
    timestamp: new Date().toISOString()
  })
}

2. 启动时验证:不留给运行时的"惊喜"

玩具思维的问题:

// ❌ 运行时才发现问题
class Application {
  private database: Database
  private cache: Cache
  private emailService: EmailService

  constructor() {
    // 配置可能在运行时才验证
    this.database = new Database(process.env.DATABASE_URL || 'sqlite:memory')
    this.cache = new Cache(process.env.CACHE_URL || 'redis://localhost')
    this.emailService = new EmailService(process.env.SMTP_HOST || 'localhost')
  }

  async start() {
    // 运行时才发现配置错误
    try {
      await this.database.connect()
      await this.cache.connect()
      await this.emailService.testConnection()

      console.log('Application started successfully')
    } catch (error) {
      console.error('Failed to start application:', error)
      // 应用继续运行,但功能不完整
    }
  }
}

产品思维的解决方案:

// ✅ 启动时验证,配置错误立即暴露
interface AppConfig {
  database: {
    url: string
    maxConnections: number
    timeout: number
  }
  cache: {
    url: string
    maxRetries: number
  }
  email: {
    host: string
    port: number
    username: string
    password: string
  }
  server: {
    port: number
    host: string
  }
}

class ConfigValidator {
  static validate(config: AppConfig): void {
    // 数据库配置验证
    if (!config.database.url) {
      throw new Error('DATABASE_URL is required')
    }

    if (config.database.maxConnections < 1 || config.database.maxConnections > 100) {
      throw new Error('Database maxConnections must be between 1 and 100')
    }

    // 缓存配置验证
    if (!config.cache.url) {
      throw new Error('CACHE_URL is required')
    }

    // 邮件配置验证
    if (!config.email.host || !config.email.username || !config.email.password) {
      throw new Error('Email service configuration is incomplete')
    }

    if (config.email.port < 1 || config.email.port > 65535) {
      throw new Error('Email port must be between 1 and 65535')
    }

    // 服务器配置验证
    if (config.server.port < 1024 || config.server.port > 65535) {
      throw new Error('Server port must be between 1024 and 65535')
    }
  }
}

class Application {
  private database: Database
  private cache: Cache
  private emailService: EmailService
  private config: AppConfig

  constructor(config: AppConfig) {
    // 配置验证必须在启动时完成
    ConfigValidator.validate(config)
    this.config = config

    // 所有依赖都基于验证过的配置
    this.database = new Database(config.database)
    this.cache = new Cache(config.cache)
    this.emailService = new EmailService(config.email)
  }

  async start(): Promise<void> {
    console.log('Starting application...')

    // 依次启动所有服务,任何一个失败就停止
    await this.database.connect()
    console.log('✅ Database connected')

    await this.cache.connect()
    console.log('✅ Cache connected')

    await this.emailService.testConnection()
    console.log('✅ Email service ready')

    // 所有服务都正常才启动HTTP服务器
    this.startHttpServer()

    console.log(`🚀 Application started successfully on ${this.config.server.host}:${this.config.server.port}`)
  }

  private startHttpServer(): void {
    // HTTP服务器启动逻辑
  }
}

// 应用启动
async function main() {
  try {
    const config: AppConfig = {
      database: {
        url: process.env.DATABASE_URL!,
        maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS || '10'),
        timeout: parseInt(process.env.DB_TIMEOUT || '30000')
      },
      cache: {
        url: process.env.CACHE_URL!,
        maxRetries: parseInt(process.env.CACHE_MAX_RETRIES || '3')
      },
      email: {
        host: process.env.SMTP_HOST!,
        port: parseInt(process.env.SMTP_PORT || '587'),
        username: process.env.SMTP_USERNAME!,
        password: process.env.SMTP_PASSWORD!
      },
      server: {
        port: parseInt(process.env.PORT || '3000'),
        host: process.env.HOST || 'localhost'
      }
    }

    const app = new Application(config)
    await app.start()

  } catch (error) {
    console.error('❌ Application startup failed:', error instanceof Error ? error.message : error)

    // 配置错误时提供具体的修复建议
    if (error.message.includes('DATABASE_URL')) {
      console.error('💡 Fix: Set DATABASE_URL environment variable')
      console.error('💡 Example: export DATABASE_URL="postgresql://user:pass@localhost:5432/dbname"')
    }

    if (error.message.includes('CACHE_URL')) {
      console.error('💡 Fix: Set CACHE_URL environment variable')
      console.error('💡 Example: export CACHE_URL="redis://localhost:6379"')
    }

    // 退出进程,避免运行在错误配置下
    process.exit(1)
  }
}

// 启动应用
main()

3. 错误分类和标准化处理

建立清晰的错误分类体系:

// 基础错误类型
export abstract class ApplicationError extends Error {
  abstract readonly code: string
  abstract readonly httpStatus: number
  abstract readonly userMessage: string

  constructor(message: string, public readonly context?: Record<string, any>) {
    super(message)
    this.name = this.constructor.name
  }
}

// 用户输入错误
export class ValidationError extends ApplicationError {
  readonly code = 'VALIDATION_ERROR'
  readonly httpStatus = 400
  readonly userMessage = '请检查输入信息并重试'
}

export class AuthenticationError extends ApplicationError {
  readonly code = 'AUTHENTICATION_ERROR'
  readonly httpStatus = 401
  readonly userMessage = '请先登录后再试'
}

export class AuthorizationError extends ApplicationError {
  readonly code = 'AUTHORIZATION_ERROR'
  readonly httpStatus = 403
  readonly userMessage = '您没有权限执行此操作'
}

// 业务逻辑错误
export class BusinessError extends ApplicationError {
  readonly code = 'BUSINESS_ERROR'
  readonly httpStatus = 422
  readonly userMessage = '操作无法完成,请检查相关条件'
}

// 系统错误
export class DatabaseError extends ApplicationError {
  readonly code = 'DATABASE_ERROR'
  readonly httpStatus = 500
  readonly userMessage = '系统暂时繁忙,请稍后重试'
}

export class ExternalServiceError extends ApplicationError {
  readonly code = 'EXTERNAL_SERVICE_ERROR'
  readonly httpStatus = 503
  readonly userMessage = '外部服务暂时不可用,请稍后重试'
}

// 错误处理中间件
export function errorHandler(error: Error, req: Request, res: Response, next: NextFunction): void {
  // 记录详细错误信息
  logger.exception('Request error', error, {
    method: req.method,
    url: req.url,
    userAgent: req.get('User-Agent'),
    ip: req.ip,
    userId: req.user?.id,
    timestamp: new Date().toISOString()
  })

  // 根据错误类型返回不同的响应
  if (error instanceof ApplicationError) {
    res.status(error.httpStatus).json({
      success: false,
      error: {
        code: error.code,
        message: error.userMessage,
        timestamp: new Date().toISOString(),
        requestId: req.id
      }
    })
  } else {
    // 未知错误
    logger.error('Unknown error occurred', error)
    res.status(500).json({
      success: false,
      error: {
        code: 'INTERNAL_SERVER_ERROR',
        message: '服务器内部错误,请联系技术支持',
        timestamp: new Date().toISOString(),
        requestId: req.id
      }
    })
  }
}

// 统一的错误处理装饰器
export function withErrorHandling<T extends any[], R>(
  fn: (...args: T) => Promise<R>
): (...args: T) => Promise<R> {
  return async (...args: T): Promise<R> => {
    try {
      return await fn(...args)
    } catch (error) {
      // 如果是已知的ApplicationError,直接重新抛出
      if (error instanceof ApplicationError) {
        throw error
      }

      // 未知错误转换为ApplicationError
      throw new ExternalServiceError(
        `Unexpected error in ${fn.name}: ${error instanceof Error ? error.message : 'Unknown error'}`,
        { originalError: error, functionName: fn.name, args: args.length }
      )
    }
  }
}

// 使用示例
class UserService {
  @withErrorHandling
  async createUser(userData: CreateUserInput): Promise<User> {
    // 输入验证
    if (!userData.email || !userData.name) {
      throw new ValidationError('Email and name are required', { userData })
    }

    // 业务逻辑检查
    if (await this.emailExists(userData.email)) {
      throw new BusinessError('Email already registered', { email: userData.email })
    }

    // 数据库操作
    try {
      const user = await this.userRepository.create(userData)
      return user
    } catch (dbError) {
      throw new DatabaseError('Failed to create user', { userData, dbError })
    }
  }
}

🎯 从玩具思维到产品思维的转型路径

第一步:认知转换 - 重新定义"好代码"

玩具思维的"好代码"标准:

  • ✅ 不会崩溃
  • ✅ 有降级方案
  • ✅ 能处理各种异常
  • ✅ 看起来很"健壮"

产品思维的"好代码"标准:

  • ✅ 问题会立即暴露
  • ✅ 有清晰的错误信息
  • ✅ 容易调试和维护
  • ✅ 用户体验一致

第二步:建立错误分类体系

创建清晰的错误分类:

// 错误分类决策树
function classifyError(error: Error): ErrorType {
  if (error instanceof ValidationError) {
    return 'USER_ERROR'
  }

  if (error instanceof DatabaseError) {
    return 'SYSTEM_ERROR'
  }

  if (error instanceof ExternalServiceError) {
    return 'DEPENDENCY_ERROR'
  }

  return 'UNKNOWN_ERROR'
}

不同错误类型的处理策略:

  • 用户错误:立即提示用户修正
  • 系统错误:记录详细信息,快速修复
  • 依赖错误:评估替代方案,优化服务商
  • 未知错误:紧急调查,建立监控

第三步:重构现有代码

重构原则:

  1. 移除不必要的降级逻辑
  2. 增加前置验证
  3. 标准化错误处理
  4. 完善错误信息

重构示例:

// 重构前:玩具思维
async function processData(data: any): Promise<any> {
  try {
    const result = await processWithService(data)
    return result
  } catch (error) {
    try {
      const fallbackResult = await processWithFallback(data)
      return fallbackResult
    } catch (fallbackError) {
      return null
    }
  }
}

// 重构后:产品思维
async function processData(inputData: ProcessDataInput): Promise<ProcessDataOutput> {
  // 前置验证
  validateInputData(inputData)

  try {
    const result = await processWithService(inputData)
    validateOutputData(result)
    return result
  } catch (error) {
    logger.exception('Data processing failed', error, { inputData })
    throw new ProcessingError('Unable to process data', { inputData, error })
  }
}

第四步:建立监控和告警机制

关键监控指标:

  • 错误率:按错误类型分类
  • 响应时间:识别性能退化
  • 成功率:核心功能的可用性
  • 用户影响:错误对用户体验的影响

告警策略:

  • 立即告警:系统核心功能失败
  • 5分钟告警:错误率超过阈值
  • 1小时告警:性能指标异常
  • 1天报告:趋势分析和总结

🌟 真实案例:希语低喃心绪笔记的产品思维实践

案例1:AI对话接口的重构

玩具思维时期的问题:

// ❌ 复杂的降级逻辑
async function getAIResponse(message: string): Promise<string | null> {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4',
      messages: [{ role: 'user', content: message }]
    })
    return response.choices[0]?.message?.content || null
  } catch (error) {
    console.log('OpenAI failed, trying local model...')

    try {
      const localResponse = await localAIModel.process(message)
      return localResponse
    } catch (localError) {
      console.log('Local model failed, using preset response...')
      return presetResponses[Math.floor(Math.random() * presetResponses.length)]
    }
  }
}

产品思维的重构:

// ✅ 明确的错误处理和快速修复
class AIService {
  async generateResponse(message: string): Promise<string> {
    // 输入验证
    if (!message || message.trim().length === 0) {
      throw new ValidationError('Message cannot be empty')
    }

    if (message.length > 10000) {
      throw new ValidationError('Message too long (max 10000 characters)')
    }

    try {
      const response = await this.openaiClient.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: message.trim() }],
        temperature: 0.7,
        max_tokens: 1000
      })

      const content = response.choices[0]?.message?.content
      if (!content) {
        throw new ExternalServiceError('AI service returned empty response')
      }

      return content

    } catch (error) {
      this.logger.exception('AI response generation failed', error, {
        messageLength: message.length,
        timestamp: new Date().toISOString()
      })

      // 根据错误类型给出具体提示
      if (error.message.includes('rate_limit_exceeded')) {
        throw new ExternalServiceError('AI service is busy, please try again in a moment')
      }

      if (error.message.includes('insufficient_quota')) {
        throw new ExternalServiceError('AI service quota exceeded, please contact support')
      }

      throw new ExternalServiceError('Unable to generate AI response at this time')
    }
  }
}

重构效果:

  • 用户体验提升:明确的错误提示,不再看到奇怪的回复
  • 问题发现速度:OpenAI API问题立即暴露,快速修复
  • 维护成本降低:代码从50行减少到30行,逻辑清晰
  • 监控完善:能够精确跟踪AI服务的可用性

案例2:数据同步服务的改造

玩具思维的问题:

// ❌ 静默失败,数据不一致
async function syncUserData(userId: string): Promise<void> {
  try {
    await localDB.updateUser(userId)
    await remoteDB.updateUser(userId)
    await cache.invalidateUser(userId)
  } catch (error) {
    console.log('Sync failed, but continuing...')
    // 静默失败,不同步步数据不一致
  }
}

产品思维的解决方案:

// ✅ 事务性操作,要么全部成功,要么全部失败
class UserDataSyncService {
  async syncUser(userId: string): Promise<void> {
    // 获取当前数据快照
    const currentUserData = await this.localDB.getUser(userId)
    if (!currentUserData) {
      throw new ValidationError(`User ${userId} not found in local database`)
    }

    try {
      // 开始分布式事务
      const transactionId = await this.transactionManager.begin()

      try {
        // 步骤1:更新远程数据库
        await this.remoteDB.updateUser(userId, currentUserData)

        // 步骤2:更新缓存
        await this.cache.set(`user:${userId}`, currentUserData, 3600)

        // 步骤3:提交事务
        await this.transactionManager.commit(transactionId)

        this.logger.info(`User ${userId} synced successfully`, {
          transactionId,
          timestamp: new Date().toISOString()
        })

      } catch (stepError) {
        // 任何步骤失败,回滚事务
        await this.transactionManager.rollback(transactionId)
        throw stepError
      }

    } catch (error) {
      this.logger.exception('User sync failed', error, {
        userId,
        userDataKeys: Object.keys(currentUserData)
      })

      // 根据错误类型决定重试策略
      if (error instanceof NetworkError) {
        throw new ExternalServiceError('Network connectivity issues prevented sync')
      }

      if (error instanceof DatabaseError) {
        throw new DatabaseError('Database operation failed during sync')
      }

      throw new SyncError('Unable to sync user data', { userId, error })
    }
  }
}

改造效果:

  • 数据一致性:不再出现部分同步的问题
  • 故障可追踪:每次同步都有明确的成功/失败状态
  • 错误可恢复:网络问题时可以重试,数据库问题时可以修复
  • 监控可视化:能够看到同步成功率和失败原因

💡 给AI开发者的实用建议

1. 建立"产品思维"检查清单

每次AI生成代码后问自己:

## 产品思维检查清单

### 错误处理检查
- [ ] 错误是否会立即暴露?
- [ ] 错误信息是否清晰明确?
- [ ] 是否有必要的日志记录?
- [ ] 用户是否知道如何处理错误?

### 用户体验检查
- [ ] 功能是否始终一致?
- [ ] 用户是否知道系统状态?
- [ ] 错误时是否有友好的提示?
- [ ] 是否避免了"神奇"的降级行为?

### 维护性检查
- [ ] 代码逻辑是否简单直接?
- [ ] 是否容易调试和排查问题?
- [ ] 是否有完善的测试覆盖?
- [ ] 是否遵循了单一职责原则?

### 监控和运维检查
- [ ] 关键操作是否有日志?
- [ ] 是否有错误告警机制?
- [ ] 是否有性能监控?
- [ ] 是否有健康检查端点?

2. 建立AI输出的质量标准

禁止AI生成的代码模式:

// ❌ 禁止的模式
try {
  result = riskyOperation()
} catch {
  result = fallbackValue()  // 静默降级
}

// ❌ 禁止的模式
if (data === undefined || data === null) {
  data = defaultValue  // 掩盖数据问题
}

// ❌ 禁止的模式
async function robustOperation() {
  for (let i = 0; i < 3; i++) {
    try {
      return await operation()
    } catch {
      continue  // 无限重试,不暴露问题
    }
  }
}

要求AI生成的代码模式:

// ✅ 要求的模式
async function operation(): Promise<Result> {
  const input = validateInput()  // 前置验证
  const result = await performOperation(input)
  return validateOutput(result)  // 结果验证
}

// ✅ 要求的模式
try {
  const result = await operation()
  return result
} catch (error) {
  logger.exception('Operation failed', error)
  throw new OperationError('Unable to complete operation', { error })
}

3. 建立代码审查标准

产品思维的代码审查要点:

  • 错误处理是否合适:不应该有静默失败
  • 用户体验是否一致:相同操作应该有相同结果
  • 故障是否快速暴露:问题应该立即被发现
  • 维护是否简单:代码应该容易理解和修改

🌟 总结:从玩具开发者到产品工程师的转变

核心认知转变

玩具开发者思维:

  • "我要让代码在任何情况下都能运行"
  • "我要提供多种降级方案"
  • "我要防御所有可能的错误"
  • "系统稳定比用户体验重要"

产品工程师思维:

  • "我要让问题立即暴露并快速修复"
  • "我要提供一致的用户体验"
  • "我要确保问题的可追溯性"
  • "用户体验是第一位的"

实践建议

  1. 拒绝降级思维:不要用fallback掩盖问题
  2. 拥抱快速失败:Let it Crash, Fix it Fast
  3. 建立监控体系:实时了解系统健康状况
  4. 完善错误处理:提供清晰的错误信息和用户指导

最终目标

真正的产品开发不是写出"不会崩溃"的代码,而是构建"快速发现问题、快速修复问题"的系统。

记住:用户宁愿看到一个明确的错误信息,也不愿接受一个功能不完整的降级产品。


下一篇预告: 《AI编程的终极挑战:如何构建真正的企业级应用》


微信搜索"四哥还会聊AI",学习更多产品级AI开发的实战经验

微信搜索"汐屿笔记",看这些架构原则如何在实际项目中应用

← 返回首页