ルールは、Droidが毎回従う成文化された標準です。(決定と文脈を捉える)メモリーとは異なり、ルールはコードがどのように書かれるべきかを定義します。このガイドでは、個人やチームにおいて効果的にルールを整理する方法を説明します。
Works with Factory App: These conventions work identically in both CLI and Factory App—Droid reads the same .factory/rules/ files regardless of interface.
ルールと他の設定の比較
| タイプ | 目的 | 例 |
|---|
| ルール | コードがどのように書かれるべきか | ”ネストした条件文の代わりに早期リターンを使用する” |
| Memory | 何が決定され、なぜそうしたか | ”Reduxが冗長すぎたため、Zustandを選んだ” |
| AGENTS.md | ビルド/テスト/実行の方法 | ”作業完了前に npm test を実行する” |
| Skills | 特定のタスクの実行方法 | ”新しいAPIエンドポイントを実装するステップ” |
ルールは規範的です—DroidにすべきことをL告します。一貫して適用されるべき標準に使用してください。
ルールディレクトリの設定
基本構造
プロジェクトに .factory/rules/ ディレクトリを作成します:
.factory/
└── rules/
├── typescript.md # TypeScript conventions
├── react.md # React patterns
├── testing.md # Testing requirements
├── api.md # API design rules
└── security.md # Security requirements
すべてのプロジェクトに適用される個人ルールの場合:
~/.factory/
└── rules/
├── style.md # Your personal style
└── tools.md # Tool preferences
AGENTS.mdでルールを参照する
AGENTS.md にセクションを追加します:
## Coding Standards
Follow the conventions documented in `.factory/rules/`:
- **TypeScript**: `.factory/rules/typescript.md`
- **React**: `.factory/rules/react.md`
- **Testing**: `.factory/rules/testing.md`
- **API Design**: `.factory/rules/api.md`
- **Security**: `.factory/rules/security.md`
When working on a file, check the relevant rules first.
効果的なルールの記述
ルール構造
各ルールは以下であるべきです:
- 具体的: 解釈の余地なく従えるほど明確
- 実行可能: 避けるべきことではなく、すべきことを指示
- スコープ化: いつ適用されるかを明記
- 正当化 (任意): 複雑なルールに対する理由の説明
テンプレート
# [Category] Rules
## [Rule Name]
**Applies to**: [file types, contexts]
**Rule**: [specific instruction]
**Example**: [code showing correct usage]
**Rationale**: [why this matters - optional]
ルールファイルの例
TypeScriptルール
.factory/rules/typescript.md を作成します:
# TypeScript Rules
## Type Definitions
### Use `interface` for object shapes
**Applies to**: All type definitions for objects
**Rule**: Use `interface` for object types, `type` for unions, intersections, and primitives.
```typescript
// ✅ 正しい
interface User {
id: string;
name: string;
}
type Status = 'active' | 'inactive';
type UserWithStatus = User & { status: Status };
// ❌ 避ける
type User = {
id: string;
name: string;
};
Avoid any
Applies to: All TypeScript files
Rule: Never use any. Use unknown with type guards, or define proper types.
// ✅ 正しい
function processData(data: unknown): string {
if (typeof data === 'string') {
return data.toUpperCase();
}
throw new Error('Expected string');
}
// ❌ 避ける
function processData(data: any): string {
return data.toUpperCase();
}
Function Patterns
Use early returns
Applies to: All functions with conditionals
Rule: Return early for edge cases instead of nesting.
// ✅ 正しい
function processUser(user: User | null): string {
if (!user) return 'No user';
if (!user.active) return 'User inactive';
return `Processing ${user.name}`;
}
// ❌ 避ける
function processUser(user: User | null): string {
if (user) {
if (user.active) {
return `Processing ${user.name}`;
} else {
return 'User inactive';
}
} else {
return 'No user';
}
}
Named exports over default
Applies to: All module exports
Rule: Use named exports for better refactoring and import clarity.
// ✅ 正しい
export function createUser() {}
export const USER_ROLES = ['admin', 'user'] as const;
// ❌ 避ける
export default function createUser() {}
### Reactルール
`.factory/rules/react.md` を作成します:
```markdown
# React Rules
## Component Structure
### Functional components only
**Applies to**: All React components
**Rule**: Use functional components with hooks. Never use class components.
### Props interface naming
**Applies to**: All components with props
**Rule**: Name props interface as `{ComponentName}Props`.
```tsx
// ✅ 正しい
interface UserCardProps {
user: User;
onSelect: (user: User) => void;
}
export function UserCard({ user, onSelect }: UserCardProps) {
return <div onClick={() => onSelect(user)}>{user.name}</div>;
}
Component file structure
Applies to: All component files
Rule: Order sections as: imports, types, component, exports.
// 1. インポート(React、外部、内部、型)
import { useState } from 'react';
import { Button } from '@/components/ui';
import type { User } from '@/types';
// 2. 型
interface UserListProps {
users: User[];
}
// 3. コンポーネント
export function UserList({ users }: UserListProps) {
const [selected, setSelected] = useState<string | null>(null);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
State Management
Zustand for client state
Applies to: Client-side state that isn’t server data
Rule: Use Zustand stores in src/stores/. One store per domain.
// src/stores/useUserStore.ts
import { create } from 'zustand';
interface UserState {
currentUser: User | null;
setUser: (user: User) => void;
logout: () => void;
}
export const useUserStore = create<UserState>((set) => ({
currentUser: null,
setUser: (user) => set({ currentUser: user }),
logout: () => set({ currentUser: null }),
}));
React Query for server state
Applies to: All data fetched from APIs
Rule: Use React Query. Queries go in src/queries/.
// src/queries/useUsers.ts
import { useQuery } from '@tanstack/react-query';
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(r => r.json()),
});
}
### Testing Rules
Create `.factory/rules/testing.md`:
```markdown
# Testing Rules
## File Organization
### Colocate test files
**Applies to**: All tests except E2E
**Rule**: Place test files next to source files.
src/
└── components/
└── UserCard/
├── UserCard.tsx
├── UserCard.test.tsx # ✅ Colocated
└── index.ts
### E2E tests in dedicated directory
**Applies to**: End-to-end tests
**Rule**: Place E2E tests in `e2e/` at project root.
## Test Structure
### Descriptive test names
**Applies to**: All test cases
**Rule**: Format as "should [action] when [condition]".
```typescript
// ✅ Correct
it('should display error message when login fails', () => {});
it('should redirect to dashboard when login succeeds', () => {});
// ❌ Avoid
it('login error', () => {});
it('works', () => {});
One assertion per test
Applies to: Unit tests
Rule: Test one behavior per test case. Multiple assertions OK if testing same behavior.
// ✅ Correct - testing one behavior
it('should format user name correctly', () => {
const result = formatUserName({ first: 'John', last: 'Doe' });
expect(result).toBe('John Doe');
});
// ✅ Also correct - same behavior, multiple aspects
it('should return complete user object', () => {
const user = createUser('John');
expect(user.id).toBeDefined();
expect(user.name).toBe('John');
expect(user.createdAt).toBeInstanceOf(Date);
});
// ❌ Avoid - testing multiple behaviors
it('should handle user operations', () => {
expect(createUser('John').name).toBe('John');
expect(deleteUser('123')).toBe(true);
expect(listUsers()).toHaveLength(0);
});
Mocking
Mock at boundaries
Applies to: All mocked dependencies
Rule: Mock external APIs and services, not internal functions.
// ✅ Correct - mock external API
vi.mock('@/lib/api', () => ({
fetchUser: vi.fn().mockResolvedValue({ id: '1', name: 'John' }),
}));
// ❌ Avoid - mock internal implementation
vi.mock('@/utils/formatName', () => ({
formatName: vi.fn().mockReturnValue('John'),
}));
Use MSW for API mocking in integration tests
Applies to: Integration tests that need API responses
Rule: Use Mock Service Worker instead of mocking fetch directly.
// ✅ Correct
import { http, HttpResponse } from 'msw';
const handlers = [
http.get('/api/users', () => {
return HttpResponse.json([{ id: '1', name: 'John' }]);
}),
];
### Security Rules
Create `.factory/rules/security.md`:
```markdown
# Security Rules
## Secrets Management
### Never hardcode secrets
**Applies to**: All code
**Rule**: Use environment variables for all secrets. Never commit secrets.
```typescript
// ✅ Correct
const apiKey = process.env.API_KEY;
// ❌ Never do this
const apiKey = 'sk-1234567890abcdef';
Validate environment variables
Applies to: Application startup
Rule: Validate required env vars exist at startup.
// ✅ Correct
const config = {
apiKey: requireEnv('API_KEY'),
dbUrl: requireEnv('DATABASE_URL'),
};
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing required env var: ${name}`);
return value;
}
Applies to: API routes, form handlers
Rule: Use Zod to validate all input from users or external sources.
// ✅ Correct
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export async function createUser(input: unknown) {
const data = CreateUserSchema.parse(input);
// data is now typed and validated
}
Error Handling
Never expose internal errors
Applies to: API error responses
Rule: Log detailed errors server-side; return generic messages to clients.
// ✅ Correct
try {
await processPayment(data);
} catch (error) {
console.error('Payment failed:', error); // Detailed log
throw new ApiError('Payment processing failed', 500); // Generic message
}
Authentication
Check authentication on every protected route
Applies to: All API routes requiring auth
Rule: Use middleware or guards. Never assume auth from client.
// ✅ Correct
export async function GET(request: Request) {
const session = await getSession(request);
if (!session) {
return new Response('Unauthorized', { status: 401 });
}
// ... handle authenticated request
}
---
## Organizing Team Rules
### Layered Rules
For teams, organize rules in layers:
.factory/rules/
├── _base/ # Foundation rules (everyone follows)
│ ├── typescript.md
│ └── security.md
├── frontend/ # Frontend-specific
│ ├── react.md
│ └── styling.md
├── backend/ # Backend-specific
│ ├── api.md
│ └── database.md
└── testing/ # Testing standards
├── unit.md
└── integration.md
Reference in AGENTS.md:
```markdown
## Rules
- Base rules: `.factory/rules/_base/` - Apply to all code
- Frontend rules: `.factory/rules/frontend/` - React components
- Backend rules: `.factory/rules/backend/` - API and services
- Testing rules: `.factory/rules/testing/` - All tests
Rule Ownership
Add ownership to track who maintains each rule set:
# TypeScript Rules
**Owner**: Platform Team
**Last Updated**: 2024-02-15
**Review Cycle**: Quarterly
[rules content...]
Current Limitation: No Glob Pattern Support
Currently, Droid doesn’t support conditional rule application based on file patterns (e.g., “apply these rules only to *.tsx files”). This is on the roadmap.
回避策:
- ファイルタイプごとに整理: 別々のルールファイルを作成し、コンテキストに応じて参照する
# In AGENTS.md
When working on React components (*.tsx), follow `.factory/rules/react.md`
When working on API routes, follow `.factory/rules/api.md`
- ルールで明確なスコープを使用: 適用可能性を明確に述べる
## This rule applies to: React component files (*.tsx)
- 複雑なワークフローにはskillsを使用: Skillsはファイルタイプ固有の指示をエンコードできる
ルールの保守
新しいルールの追加
Droidを繰り返し修正している場合:
- パターンを特定する
- 例を含む明確なルールを記述する
- 適切なルールファイルに追加する
- 必要に応じてAGENTS.mdを更新する
- 類似の作業をDroidに依頼してテストする
ルールのレビュー
四半期レビューチェックリスト:
ルールの廃止
ルールが時代遅れになった場合:
## ~~Use PropTypes for type checking~~ (DEPRECATED)
**Status**: Deprecated as of 2024-02
**Reason**: We now use TypeScript for all type checking
**Replacement**: See TypeScript rules for prop typing
ルールの自動実行
Droidは .factory/rules/ ファイルのルールに従いますが、hooks で自動実行を追加できます。
編集後にリンターを実行
ファイル編集後にリンターを自動実行するPostToolUseフックを追加します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Create",
"hooks": [
{
"type": "command",
"command": "cd \"$FACTORY_PROJECT_DIR\" && npm run lint -- --fix 2>/dev/null || true"
}
]
}
]
}
}
コードスタイルの検証
Prettierやフォーマッターを自動実行します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Create",
"hooks": [
{
"type": "command",
"command": "cd \"$FACTORY_PROJECT_DIR\" && npx prettier --write \"$(jq -r '.tool_input.file_path')\" 2>/dev/null || true"
}
]
}
]
}
}
クイックリファレンス
ファイルの場所
| スコープ | 場所 | 用途 |
|---|
| 個人 | ~/.factory/rules/ | あなたのスタイル設定 |
| プロジェクト | .factory/rules/ | チーム標準 |
ルール形式
## [Rule Name]
**Applies to**: [scope]
**Rule**: [what to do]
**Example**: [code]
**Rationale**: [why - optional]
良いルールの条件
- ✅ 具体的で曖昧さがない
- ✅ コード例を含む
- ✅ いつ適用されるかを明記
- ✅ 実行可能(「Xを検討する」ではなく「Xを行う」)
- ❌ 曖昧ではない(「クリーンなコードを書く」)
- ❌ リンタールールと重複していない
- ❌ 他のルールと矛盾していない
次のステップ