mcpField
P
phase-2-convention

phase-2-convention

github.com/ww-w-ai开发60652 次安装4 周前更新
代码仓库

关于 phase-2-convention

用于项目规划阶段制定编码规范的开发指南,涵盖命名、代码结构、环境变量和架构,并支持在后续审查阶段核对规范。

主要功能

  1. 01命名与代码风格规则
  2. 02文件夹和文件结构指引
  3. 03环境变量约定
  4. 04分层架构与依赖规则
  5. 05代码复用与重复检查

适用场景

  1. 01编码前制定项目规范
  2. 02定义环境变量处理方式
  3. 03为团队统一架构规则
  4. 04在后续阶段检查代码一致性

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ww-w-ai/bkit-claude-code --skill phase-2-convention

安装后在 Claude Code 中输入/phase-2-convention确认已加载。

详细介绍

阶段 2:编码规范

定义代码编写规则

目的

保持代码风格一致。与 AI 协作时尤其重要——明确 AI 编写代码时应采用的风格。

本阶段要做什么

  1. 命名规则:变量、函数、文件、文件夹名称
  2. 代码风格:缩进、引号、分号等
  3. 结构规则:文件夹结构、文件拆分标准
  4. 模式定义:常用代码模式

交付成果

Project Root/
├── CONVENTIONS.md          # Full conventions
└── docs/01-plan/
    ├── naming.md           # Naming rules
    └── structure.md        # Structure rules

PDCA 应用

  • 计划:确定必要的规范条目
  • 设计:设计详细规则
  • 执行:编写规范文档
  • 检查:检查一致性和实用性
  • 改进:最终确定并进入阶段 3

分级应用

级别应用层级
入门基础(仅包含必要规则)
动态扩展(包含 API、状态管理)
企业扩展(针对各服务的规则)

核心规范条目

命名
  • 组件:PascalCase
  • 函数:camelCase
  • 常量:UPPER_SNAKE_CASE
  • 文件:kebab-case 或 PascalCase
文件夹结构
src/
├── components/     # Reusable components
├── features/       # Feature modules
├── hooks/          # Custom hooks
├── utils/          # Utilities
└── types/          # Type definitions

环境变量规范

为什么要在设计阶段定义?
❌ Organizing env vars just before deployment
   → Missing variables, naming inconsistency, deployment delays

✅ Establish convention at design stage
   → Consistent naming, clear categorization, fast deployment
环境变量命名规则
前缀用途暴露范围示例
NEXT_PUBLIC_暴露给客户端浏览器NEXT_PUBLIC_API_URL
DB_数据库仅服务端DB_HOST、DB_PASSWORD
API_外部 API 密钥仅服务端API_STRIPE_SECRET
AUTH_身份验证仅服务端AUTH_SECRET、AUTH_GOOGLE_ID
SMTP_电子邮件服务仅服务端SMTP_HOST、SMTP_PASSWORD
STORAGE_文件存储仅服务端STORAGE_S3_BUCKET
⚠️ Security Principles
- Never expose anything except NEXT_PUBLIC_* to client
- API keys and passwords must be server-only variables
- Never commit sensitive info in .env files
.env 文件结构
Project Root/
├── .env.example        # Template (in Git, values empty)
├── .env.local          # Local development (Git ignored)
├── .env.development    # Development env defaults
├── .env.staging        # Staging env defaults
├── .env.production     # Production defaults (no sensitive info)
└── .env.test           # Test environment
.env.example 模板
# .env.example - This file is included in Git
# Set actual values in .env.local

# ===== App Settings =====
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000

# ===== Database =====
DB_HOST=
DB_PORT=5432
DB_NAME=
DB_USER=
DB_PASSWORD=

# ===== Authentication =====
AUTH_SECRET=                    # openssl rand -base64 32
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=

# ===== External Services =====
NEXT_PUBLIC_API_URL=
API_STRIPE_SECRET=
SMTP_HOST=
SMTP_USER=
SMTP_PASSWORD=
按环境分类的变量值
变量类型.env.example.env.localCI/CD Secrets
应用 URL模板本地值各环境对应的值
API 端点模板本地/开发环境各环境对应的值
DB 密码空本地值✅ Secrets
API 密钥空测试密钥✅ Secrets
JWT 密钥空本地值✅ Secrets
环境变量验证
// lib/env.ts - Validate env vars at app startup
import { z } from 'zod';

const envSchema = z.object({
  // Required
  DATABASE_URL: z.string().url(),
  AUTH_SECRET: z.string().min(32),

  // Optional (with defaults)
  NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),

  // Client-exposed
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

// Validation and type inference
export const env = envSchema.parse(process.env);

// Type-safe usage
// env.DATABASE_URL  ← autocomplete supported
环境变量检查清单
  • 命名一致性

    • 遵循前缀规则(NEXT_PUBLIC_、DB_、API_ 等)
    • 使用 UPPER_SNAKE_CASE
  • 文件结构

    • 创建 .env.example(模板)
    • 将 .env.local 添加到 .gitignore
    • 为各环境分别设置 .env 文件
  • 安全性

    • 对敏感信息进行分类
    • 检查暴露给客户端的变量
    • 整理 Secrets 列表(用于阶段 9 部署)

整洁架构原则

为什么要在设计阶段定义?
Clean Architecture = Code resilient to change

❌ Developing without architecture
   → Spaghetti code, multiple file changes for each modification

✅ Define layers at design stage
   → Separation of concerns, easy testing, easy maintenance
4 层架构(推荐)
src/
├── presentation/        # or app/, pages/
│   ├── components/      # UI components
│   ├── hooks/           # State management hooks
│   └── pages/           # Page components
│
├── application/         # or services/, features/
│   ├── use-cases/       # Business use cases
│   └── services/        # API service wrappers
│
├── domain/              # or types/, entities/
│   ├── entities/        # Domain entities
│   ├── types/           # Type definitions
│   └── constants/       # Domain constants
│
└── infrastructure/      # or lib/, api/
    ├── api/             # API clients
    ├── db/              # Database connections
    └── external/        # External services
各层职责与规则
层职责可以依赖不可依赖
表示层UI 渲染、用户事件应用层、领域层基础设施层
应用层业务逻辑编排领域层、基础设施层表示层
领域层核心业务规则、类型无(独立)所有外部层
基础设施层外部系统连接领域层应用层、表示层
依赖规则
// ❌ Bad: Presentation directly calls Infrastructure
// components/UserList.tsx
import { apiClient } from '@/lib/api/client';  // Direct import forbidden!

export function UserList() {
  const users = apiClient.get('/users');  // ❌
}

// ✅ Good: Presentation → Application → Infrastructure
// hooks/useUsers.ts
import { userService } from '@/services/user.service';

export function useUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: userService.getList,  // ✅ Call through Service
  });
}

// components/UserList.tsx
import { useUsers } from '@/hooks/useUsers';

export function UserList() {
  const { data: users } = useUsers();  // ✅ Call through Hook
}
文件导入规则
// ===== Allowed import directions =====

// In presentation/:
import { User } from '@/domain/types';           // ✅ Domain OK
import { useUsers } from '@/hooks/useUsers';     // ✅ Same layer OK
import { userService } from '@/services/user';   // ✅ Application OK

// In application/:
import { User } from '@/domain/types';           // ✅ Domain OK
import { apiClient } from '@/lib/api/client';    // ✅ Infrastructure OK

// In domain/:
// Minimize external imports (pure types/logic only)

// In infrastructure/:
import { User } from '@/domain/types';           // ✅ Domain OK

// ===== Forbidden imports =====

// In domain/:
import { apiClient } from '@/lib/api/client';    // ❌ Infrastructure forbidden
import { Button } from '@/components/ui/button'; // ❌ Presentation forbidden

// In infrastructure/:
import { useUsers } from '@/hooks/useUsers';     // ❌ Presentation forbidden
分级应用
级别架构应用方式
入门简单结构(components、lib)
动态分为 3-4 层(推荐结构)
企业严格分层 + DI 容器
入门级文件夹结构
src/
├── components/     # UI components
├── lib/            # Utilities, API
└── types/          # Type definitions
动态级文件夹结构
src/
├── components/     # Presentation
│   └── ui/
├── features/       # Feature modules (Application + Presentation)
│   ├── auth/
│   └── product/
├── hooks/          # Presentation (state management)
├── services/       # Application
├── types/          # Domain
└── lib/            # Infrastructure
    └── api/
企业级文件夹结构
src/
├── presentation/
│   ├── components/
│   ├── hooks/
│   └── pages/
├── application/
│   ├── use-cases/
│   └── services/
├── domain/
│   ├── entities/
│   └── types/
└── infrastructure/
    ├── api/
    └── db/

阶段衔接

本阶段定义的规范将在后续阶段中验证:

定义(阶段 2)验证(阶段 8)
命名规则命名一致性检查
文件夹结构结构一致性检查
环境变量规范环境变量命名检查
清洁架构原则依赖方向检查

模板

参见 templates/pipeline/phase-2-convention.template.md

下一阶段

阶段 3:原型开发 → 规则已确定,现在开始快速原型设计


6. 可复用性原则

6.1 函数设计
创建通用函数
// ❌ Handles only specific case
function formatUserName(user: User) {
  return `${user.firstName} ${user.lastName}`
}

// ✅ Generic
function formatFullName(firstName: string, lastName: string) {
  return `${firstName} ${lastName}`
}

// Usage
formatFullName(user.firstName, user.lastName)
formatFullName(author.first, author.last)
参数泛化
// ❌ Tied to specific type
function calculateOrderTotal(order: Order) {
  return order.items.reduce((sum, item) => sum + item.price, 0)
}

// ✅ Generalized with interface
interface HasPrice { price: number }
function calculateTotal<T extends HasPrice>(items: T[]) {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// Can be used in various places
calculateTotal(order.items)
calculateTotal(cart.products)
calculateTotal(invoice.lineItems)
6.2 组件设计
可组合组件
// ❌ Hardcoded structure
function UserCard({ user }: { user: User }) {
  return (
    <div className="card">
      <img src={user.avatar} />
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  )
}

// ✅ Composable
function Card({ children, className }: CardProps) {
  return <div className={cn("card", className)}>{children}</div>
}

function Avatar({ src, alt }: AvatarProps) {
  return <img src={src} alt={alt} className="avatar" />
}

// Use by combining
<Card>
  <Avatar src={user.avatar} alt={user.name} />
  <h3>{user.name}</h3>
  <p>{user.email}</p>
</Card>
Props 可扩展性
// ❌ Limited props
interface ButtonProps {
  label: string
  onClick: () => void
}

// ✅ Extend HTML attributes
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'default' | 'outline' | 'ghost'
  size?: 'sm' | 'md' | 'lg'
}

// All button attributes available
<Button type="submit" disabled={isLoading}>
  Save
</Button>
6.3 提取标准
何时提取为函数
1. Same logic used 2+ times
2. Logic is complex enough to need a name
3. Logic that needs testing
4. Can be used in other files
何时提取为组件
1. Same UI pattern repeats
2. Has independent state
3. Is a reusable unit
4. JSX over 50 lines

7. 可扩展性原则

7.1 基于配置的设计
// ❌ Listing conditionals
function getStatusColor(status: string) {
  if (status === 'active') return 'green'
  if (status === 'pending') return 'yellow'
  if (status === 'error') return 'red'
  return 'gray'
}

// ✅ Configuration object
const STATUS_CONFIG = {
  active: { color: 'green', label: 'Active' },
  pending: { color: 'yellow', label: 'Pending' },
  error: { color: 'red', label: 'Error' },
} as const

function getStatusConfig(status: keyof typeof STATUS_CONFIG) {
  return STATUS_CONFIG[status] ?? { color: 'gray', label: status }
}

// Adding new status = just add config
7.2 策略模式
// ❌ Listing switch statements
function processPayment(method: string, amount: number) {
  switch (method) {
    case 'card':
      // Card payment logic
      break
    case 'bank':
      // Bank transfer logic
      break
  }
}

// ✅ Strategy pattern
interface PaymentStrategy {
  process(amount: number): Promise<Result>
}

const paymentStrategies: Record<string, PaymentStrategy> = {
  card: new CardPayment(),
  bank: new BankTransfer(),
}

function processPayment(method: string, amount: number) {
  const strategy = paymentStrategies[method]
  if (!strategy) throw new Error(`Unknown method: ${method}`)
  return strategy.process(amount)
}

// Adding new payment method = just add strategy
7.3 插件结构
// Extensible system
interface Plugin {
  name: string
  init(): void
  execute(data: unknown): unknown
}

class PluginManager {
  private plugins: Plugin[] = []

  register(plugin: Plugin) {
    this.plugins.push(plugin)
  }

  executeAll(data: unknown) {
    return this.plugins.reduce(
      (result, plugin) => plugin.execute(result),
      data
    )
  }
}

// New feature = add plugin

8. 防止重复检查清单

编写代码前
  • utils/ 中是否有类似函数?
  • components/ 中是否有类似组件?
  • hooks/ 中是否有类似 Hook?
  • 是否搜索过整个项目?
编写代码后
  • 同一段代码是否出现在 2 个或更多位置?→ 提取
  • 这段代码能否在其他地方使用?→ 移动
  • 是否有硬编码值?→ 定义为常量
  • 是否与特定类型绑定?→ 泛化

评论

支持 Markdown