feat: initial commit - opencode spec-driven development boilerplate

Converted from Claude Code boilerplate to opencode:
- CLAUDE.md -> AGENTS.md (opencode instructions)
- .claude/settings.json -> opencode.json (permissions schema)
- .claude/agents/ -> .opencode/agent/ (subagents with mode: subagent)
- .claude/commands/ -> .opencode/command/ (slash commands with )
- .claude/skills/ -> .opencode/skills/ (7 skills, removed allowed-tools)
- DevContainer updated to install opencode
- All .claude/ paths and Claude Code references updated
This commit is contained in:
2026-06-24 22:32:45 +02:00
commit ced279e2df
45 changed files with 7439 additions and 0 deletions

View File

@ -0,0 +1,107 @@
---
name: development-guidelines
description: A comprehensive guide and template for establishing a unified development process and coding conventions across the team. Use when creating development guidelines and when implementing code.
---
# Development Guidelines Skill
Covers the two elements needed for team development:
1. Coding conventions for implementation (implementation-guide.md)
2. Standardization of the development process (process-guide.md)
## Prerequisites
Before starting to create development guidelines, check the following:
### Recommended Documents
1. `docs/architecture.md` (architecture design document) - confirm the technology stack
2. `docs/repository-structure.md` (repository structure) - confirm the directory structure
The development guidelines define concrete coding conventions and a development process
based on the project's technology stack and directory structure.
## Priority of Existing Documents
**Important**: If existing development guidelines are present at `docs/development-guidelines.md`,
follow this priority order:
1. **The existing development guidelines (`docs/development-guidelines.md`)** - highest priority
- They document project-specific conventions and processes
- They take precedence over this skill's guide
2. **This skill's guide** - reference material
- ./guides/implementation.md: general-purpose coding conventions
- ./guides/process.md: general-purpose development process
- Use when there are no existing guidelines, or as a supplement
**When creating new**: Refer to this skill's guides and template
**When updating**: Update while preserving the structure and content of the existing guidelines
## Output Destination
Save the development guidelines you create to:
```
docs/development-guidelines.md
```
## Quick Reference
### When Implementing Code
Rules and conventions for code implementation: ./guides/implementation.md
Contents:
- TypeScript/JavaScript conventions
- Type definitions and naming conventions
- Function design and error handling
- Comment conventions
- Security and performance
- Test code implementation
- Refactoring techniques
### When Referencing/Defining the Development Process
Git workflow, test strategy, code review: ./guides/process.md
Contents:
- Basic principles (the importance of concrete examples, explaining rationale)
- Git workflow rules (Git Flow branching strategy)
- Commit messages and the PR process
- Test strategy (pyramid and coverage)
- The code review process
- Quality automation
### Template
When creating development guidelines: ./template.md
## Guide by Use Case
### When Developing New Code
1. Confirm naming conventions and coding standards in ./guides/implementation.md
2. Confirm the branching strategy and PR handling in ./guides/process.md
3. Write tests first (TDD)
### During Code Review
- Refer to "The Code Review Process" in ./guides/process.md
- Check for convention violations against ./guides/implementation.md
### When Designing Tests
- "Test Strategy" in ./guides/process.md (pyramid, coverage)
- "Test Code" in ./guides/implementation.md (implementation patterns)
### When Preparing a Release
- "Git Workflow Rules" in ./guides/process.md (policy for merging into main)
- Confirm that commit messages follow Conventional Commits
## Checklist
- [ ] Coding conventions are defined with concrete examples
- [ ] Naming conventions are clear (per language and project-specific)
- [ ] An error handling policy is defined
- [ ] A branching strategy is decided (Git Flow recommended)
- [ ] Commit message conventions are clear
- [ ] A PR template is prepared
- [ ] Test types and coverage targets are set
- [ ] A code review process is defined
- [ ] A CI/CD pipeline is established

View File

@ -0,0 +1,627 @@
# Implementation Guide
## TypeScript/JavaScript Conventions
### Type Definitions
**Using built-in types**:
```typescript
// ✅ Good: Use built-in types
function processItems(items: string[]): Record<string, number> {
return items.reduce((acc, item) => {
acc[item] = (acc[item] || 0) + 1;
return acc;
}, {} as Record<string, number>);
}
// ❌ Bad: Importing from the typing module
import { List, Dict } from 'typing';
function processItems(items: List[str]): Dict[str, int] { }
```
**Type annotation principles**:
```typescript
// ✅ Good: Explicit type annotations
function calculateTotal(prices: number[]): number {
return prices.reduce((sum, price) => sum + price, 0);
}
// ❌ Bad: Over-relying on type inference
function calculateTotal(prices) { // becomes the any type
return prices.reduce((sum, price) => sum + price, 0);
}
```
**Interface vs. type alias**:
```typescript
// Interface: extensible object types
interface Task {
id: string;
title: string;
completed: boolean;
}
// Extension
interface ExtendedTask extends Task {
priority: string;
}
// Type alias: union types, primitive types, etc.
type TaskStatus = 'todo' | 'in_progress' | 'completed';
type TaskId = string;
type Nullable<T> = T | null;
```
### Naming Conventions
**Variables and functions**:
```typescript
// Variables: camelCase, nouns
const userName = 'John';
const taskList = [];
const isCompleted = true;
// Functions: camelCase, start with a verb
function fetchUserData() { }
function validateEmail(email: string) { }
function calculateTotalPrice(items: Item[]) { }
// Boolean: start with is, has, should, or can
const isValid = true;
const hasPermission = false;
const shouldRetry = true;
const canDelete = false;
```
**Classes and interfaces**:
```typescript
// Classes: PascalCase, nouns
class TaskManager { }
class UserAuthenticationService { }
// Interfaces: PascalCase
interface TaskRepository { }
interface UserProfile { }
// Type aliases: PascalCase
type TaskStatus = 'todo' | 'in_progress' | 'completed';
```
**Constants**:
```typescript
// UPPER_SNAKE_CASE
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = 'https://api.example.com';
const DEFAULT_TIMEOUT = 5000;
// For configuration objects
const CONFIG = {
maxRetryCount: 3,
apiBaseUrl: 'https://api.example.com',
defaultTimeout: 5000,
} as const;
```
**File names**:
```typescript
// Class files: PascalCase
// TaskService.ts
// UserRepository.ts
// Functions and utilities: camelCase
// formatDate.ts
// validateEmail.ts
// Components (React, etc.): PascalCase
// TaskList.tsx
// UserProfile.tsx
// Constants: kebab-case or UPPER_SNAKE_CASE
// api-endpoints.ts
// ERROR_MESSAGES.ts
```
### Function Design
**Single responsibility principle**:
```typescript
// ✅ Good: A single responsibility
function calculateTotalPrice(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
function formatPrice(amount: number): string {
return ${amount.toLocaleString()}`;
}
// ❌ Bad: Multiple responsibilities
function calculateAndFormatPrice(items: CartItem[]): string {
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return ${total.toLocaleString()}`;
}
```
**Function length**:
- Target: within 20 lines
- Recommended: within 50 lines
- 100 lines or more: consider refactoring
**Number of parameters**:
```typescript
// ✅ Good: Group into an object
interface CreateTaskOptions {
title: string;
description?: string;
priority?: 'high' | 'medium' | 'low';
dueDate?: Date;
}
function createTask(options: CreateTaskOptions): Task {
// implementation
}
// ❌ Bad: Too many parameters
function createTask(
title: string,
description: string,
priority: string,
dueDate: Date,
tags: string[],
assignee: string
): Task {
// implementation
}
```
### Error Handling
**Custom error classes**:
```typescript
// Defining error classes
class ValidationError extends Error {
constructor(
message: string,
public field: string,
public value: unknown
) {
super(message);
this.name = 'ValidationError';
}
}
class NotFoundError extends Error {
constructor(
public resource: string,
public id: string
) {
super(`${resource} not found: ${id}`);
this.name = 'NotFoundError';
}
}
class DatabaseError extends Error {
constructor(message: string, public cause?: Error) {
super(message);
this.name = 'DatabaseError';
this.cause = cause;
}
}
```
**Error handling patterns**:
```typescript
// ✅ Good: Proper error handling
async function getTask(id: string): Promise<Task> {
try {
const task = await repository.findById(id);
if (!task) {
throw new NotFoundError('Task', id);
}
return task;
} catch (error) {
if (error instanceof NotFoundError) {
// Expected error: handle appropriately
logger.warn(`Task not found: ${id}`);
throw error;
}
// Unexpected error: wrap and propagate upward
throw new DatabaseError('Failed to retrieve the task', error as Error);
}
}
// ❌ Bad: Ignoring the error
async function getTask(id: string): Promise<Task | null> {
try {
return await repository.findById(id);
} catch (error) {
return null; // error information is lost
}
}
```
**Error messages**:
```typescript
// ✅ Good: Specific and suggests a solution
throw new ValidationError(
'Title must be 1-200 characters. Current length: 250',
'title',
title
);
// ❌ Bad: Vague and unhelpful
throw new Error('Invalid input');
```
### Asynchronous Processing
**Using async/await**:
```typescript
// ✅ Good: async/await
async function fetchUserTasks(userId: string): Promise<Task[]> {
try {
const user = await userRepository.findById(userId);
const tasks = await taskRepository.findByUserId(user.id);
return tasks;
} catch (error) {
logger.error('Failed to retrieve tasks', error);
throw error;
}
}
// ❌ Bad: Promise chains
function fetchUserTasks(userId: string): Promise<Task[]> {
return userRepository.findById(userId)
.then(user => taskRepository.findByUserId(user.id))
.then(tasks => tasks)
.catch(error => {
logger.error('Failed to retrieve tasks', error);
throw error;
});
}
```
**Parallel processing**:
```typescript
// ✅ Good: Run in parallel with Promise.all
async function fetchMultipleUsers(ids: string[]): Promise<User[]> {
const promises = ids.map(id => userRepository.findById(id));
return Promise.all(promises);
}
// ❌ Bad: Sequential execution
async function fetchMultipleUsers(ids: string[]): Promise<User[]> {
const users: User[] = [];
for (const id of ids) {
const user = await userRepository.findById(id); // slow
users.push(user);
}
return users;
}
```
## Comment Conventions
### Documentation Comments
**TSDoc format**:
```typescript
/**
* Creates a task
*
* @param data - Data for the task to create
* @returns The created task
* @throws {ValidationError} When the data is invalid
* @throws {DatabaseError} When a database error occurs
*
* @example
* ```typescript
* const task = await createTask({
* title: 'New task',
* priority: 'high'
* });
* ```
*/
async function createTask(data: CreateTaskData): Promise<Task> {
// implementation
}
```
### Inline Comments
**Good comments**:
```typescript
// ✅ Explain the reason
// Invalidate the cache to fetch the latest data
cache.clear();
// ✅ Explain complex logic
// Compute the maximum subarray sum using Kadane's algorithm
// Time complexity: O(n)
let maxSoFar = arr[0];
let maxEndingHere = arr[0];
// ✅ Make use of TODO and FIXME
// TODO: Implement caching (Issue #123)
// FIXME: Performance degrades with large datasets (Issue #456)
// HACK: Temporary workaround, needs refactoring later
```
**Bad comments**:
```typescript
// ❌ Merely restating the code
// Increment i by 1
i++;
// ❌ Stale information
// This code was added in 2020 (unnecessary information)
// ❌ Commented-out code
// const oldImplementation = () => { ... }; // should be deleted
```
## Security
### Input Validation
```typescript
// ✅ Good: Strict validation
function validateEmail(email: string): void {
if (!email || typeof email !== 'string') {
throw new ValidationError('Email address is required', 'email', email);
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new ValidationError('Email address format is invalid', 'email', email);
}
if (email.length > 254) {
throw new ValidationError('Email address is too long', 'email', email);
}
}
// ❌ Bad: No validation
function validateEmail(email: string): void {
// no validation
}
```
### Managing Sensitive Information
```typescript
// ✅ Good: Load from environment variables
import { config } from './config';
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('The API_KEY environment variable is not set');
}
// ❌ Bad: Hardcoding
const apiKey = 'sk-1234567890abcdef'; // never do this!
```
## Performance
### Choosing Data Structures
```typescript
// ✅ Good: O(1) access with a Map
const userMap = new Map(users.map(u => [u.id, u]));
const user = userMap.get(userId); // O(1)
// ❌ Bad: O(n) search with an array
const user = users.find(u => u.id === userId); // O(n)
```
### Loop Optimization
```typescript
// ✅ Good: Move unnecessary computation outside the loop
const length = items.length;
for (let i = 0; i < length; i++) {
process(items[i]);
}
// ❌ Bad: Recomputing length every iteration
for (let i = 0; i < items.length; i++) {
process(items[i]);
}
// ✅ Better: Use for...of
for (const item of items) {
process(item);
}
```
### Memoization
```typescript
// Caching computation results
const cache = new Map<string, Result>();
function expensiveCalculation(input: string): Result {
if (cache.has(input)) {
return cache.get(input)!;
}
const result = /* heavy computation */;
cache.set(input, result);
return result;
}
```
## Test Code
### Test Structure (Given-When-Then)
```typescript
describe('TaskService', () => {
describe('create', () => {
it('can create a task with valid data', async () => {
// Given: setup
const service = new TaskService(mockRepository);
const taskData = {
title: 'Test task',
description: 'Description for testing',
};
// When: execute
const result = await service.create(taskData);
// Then: verify
expect(result).toBeDefined();
expect(result.id).toBeDefined();
expect(result.title).toBe('Test task');
expect(result.description).toBe('Description for testing');
expect(result.createdAt).toBeInstanceOf(Date);
});
it('throws a ValidationError when the title is empty', async () => {
// Given: setup
const service = new TaskService(mockRepository);
const invalidData = { title: '' };
// When/Then: execute and verify
await expect(
service.create(invalidData)
).rejects.toThrow(ValidationError);
});
});
});
```
### Creating Mocks
```typescript
// ✅ Good: Mocks based on an interface
const mockRepository: TaskRepository = {
save: jest.fn(),
findById: jest.fn(),
findAll: jest.fn(),
delete: jest.fn(),
};
// Configure behavior per test
beforeEach(() => {
mockRepository.findById = jest.fn((id) => {
if (id === 'existing-id') {
return Promise.resolve(mockTask);
}
return Promise.resolve(null);
});
});
```
## Refactoring
### Eliminating Magic Numbers
```typescript
// ✅ Good: Define constants
const MAX_RETRY_COUNT = 3;
const RETRY_DELAY_MS = 1000;
for (let i = 0; i < MAX_RETRY_COUNT; i++) {
try {
return await fetchData();
} catch (error) {
if (i < MAX_RETRY_COUNT - 1) {
await sleep(RETRY_DELAY_MS);
}
}
}
// ❌ Bad: Magic numbers
for (let i = 0; i < 3; i++) {
try {
return await fetchData();
} catch (error) {
if (i < 2) {
await sleep(1000);
}
}
}
```
### Extracting Functions
```typescript
// ✅ Good: Extract functions
function processOrder(order: Order): void {
validateOrder(order);
calculateTotal(order);
applyDiscounts(order);
saveOrder(order);
}
function validateOrder(order: Order): void {
if (!order.items || order.items.length === 0) {
throw new ValidationError('No items selected', 'items', order.items);
}
}
function calculateTotal(order: Order): void {
order.total = order.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
}
// ❌ Bad: A long function
function processOrder(order: Order): void {
if (!order.items || order.items.length === 0) {
throw new ValidationError('No items selected', 'items', order.items);
}
order.total = order.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
if (order.coupon) {
order.total -= order.total * order.coupon.discountRate;
}
repository.save(order);
}
```
## Checklist
Confirm before completing implementation:
### Code Quality
- [ ] Naming is clear and consistent
- [ ] Functions have a single responsibility
- [ ] No magic numbers
- [ ] Type annotations are written appropriately
- [ ] Error handling is implemented
### Security
- [ ] Input validation is implemented
- [ ] No sensitive information is hardcoded
- [ ] SQL injection countermeasures are in place
### Performance
- [ ] Appropriate data structures are used
- [ ] Unnecessary computation is avoided
- [ ] Loops are optimized
### Testing
- [ ] Unit tests are written
- [ ] Tests pass
- [ ] Edge cases are covered
### Documentation
- [ ] Functions and classes have TSDoc comments
- [ ] Complex logic has comments
- [ ] TODOs and FIXMEs are noted (where applicable)
### Tooling
- [ ] No lint errors
- [ ] Type checking passes
- [ ] Formatting is consistent

View File

@ -0,0 +1,421 @@
# Process Guide
## Basic Principles
### 1. Include Plenty of Concrete Examples
Present concrete code examples, not just abstract rules.
**Bad example**:
```
Make variable names easy to understand
```
**Good example**:
```typescript
// ✅ Good: The role is clear
const userAuthentication = new UserAuthenticationService();
const taskRepository = new TaskRepository();
// ❌ Bad: Ambiguous
const auth = new Service();
const repo = new Repository();
```
### 2. Explain the Reasoning
Make clear "why we do it this way."
**Example**:
```
## Don't Ignore Errors
Reason: Ignoring errors makes it difficult to identify the cause of a problem.
Handle expected errors appropriately, and propagate unexpected errors upward
so they can be recorded in the logs.
```
### 3. Set Measurable Criteria
Avoid vague expressions and provide concrete numbers.
**Bad example**:
```
Keep code coverage high
```
**Good example**:
```
Code coverage targets:
- Unit tests: 80% or higher
- Integration tests: 60% or higher
- E2E tests: 100% of the main flows
```
## Git Operation Rules
### Branching Strategy (Adopting Git Flow)
**What is Git Flow**:
A branching model proposed by Vincent Driessen that systematically manages feature development, releases, and hotfixes. With clearly divided roles, it enables parallel work and stable releases in team development.
**Branch structure**:
```
main (production environment)
└── develop (development/integration environment)
├── feature/* (new feature development)
├── fix/* (bug fixes)
└── release/* (release preparation) *as needed
```
**Operation rules**:
- **main**: Holds only stable code already released to production. Versioned with tags
- **develop**: Integrates the latest development code for the next release. Automated tests run in CI
- **feature/\*, fix/\***: Branch from develop, and merge into develop via a PR after the work is complete
- **No direct commits**: Require PR review on all branches to ensure code quality
- **Merge policy**: Recommend squash merge for feature→develop, and a merge commit for develop→main
**Benefits of Git Flow**:
- Branch roles are clear, making parallel development by multiple people easier
- The production environment (main) is always kept clean
- During emergencies, hotfix branches enable rapid response (introduce as needed)
### Commit Message Conventions
**Conventional Commits is recommended**:
```
<type>(<scope>): <subject>
<body>
<footer>
```
**List of types**:
```
feat: New feature (minor version up)
fix: Bug fix (patch version up)
docs: Documentation
style: Formatting (no effect on code behavior)
refactor: Refactoring
perf: Performance improvement
test: Adding or fixing tests
build: Build system
ci: CI/CD configuration
chore: Other (dependency updates, etc.)
BREAKING CHANGE: Breaking change (major version up)
```
**Example of a good commit message**:
```
feat(task): Add priority-setting feature
Users can now set a priority (high/medium/low) on tasks.
Implementation details:
- Added a priority field to the Task model
- Added a --priority option to the CLI
- Implemented sorting by priority
Breaking changes:
- The structure of the Task type has changed
- Existing task data requires migration
Closes #123
BREAKING CHANGE: Added a required priority field to the Task type
```
### Pull Request Template
**An effective PR template**:
```markdown
## Type of Change
- [ ] New feature (feat)
- [ ] Bug fix (fix)
- [ ] Refactoring (refactor)
- [ ] Documentation (docs)
- [ ] Other (chore)
## Changes
### What was changed
[Brief description]
### Why it was changed
[Background and reasoning]
### How it was changed
- [Change 1]
- [Change 2]
## Testing
### Tests performed
- [ ] Added unit tests
- [ ] Added integration tests
- [ ] Performed manual testing
### Test results
[Description of the test results]
## Related Issues
Closes #[number]
Refs #[number]
## Review Points
[Points you especially want reviewers to look at]
```
## Test Strategy
### Test Pyramid
```
/\
/E2E\ Few (slow, high cost)
/------\
/ Integ. \ Medium
/----------\
/ Unit \ Many (fast, low cost)
/--------------\
```
**Target ratio**:
- Unit tests: 70%
- Integration tests: 20%
- E2E tests: 10%
### How to Write Tests
**Given-When-Then pattern**:
```typescript
describe('TaskService', () => {
describe('task creation', () => {
it('can create a task with valid data', async () => {
// Given: setup
const service = new TaskService(mockRepository);
const validData = { title: 'Test' };
// When: execute
const result = await service.create(validData);
// Then: verify
expect(result.id).toBeDefined();
expect(result.title).toBe('Test');
});
it('throws a ValidationError when the title is empty', async () => {
// Given: setup
const service = new TaskService(mockRepository);
const invalidData = { title: '' };
// When/Then: execute and verify
await expect(
service.create(invalidData)
).rejects.toThrow(ValidationError);
});
});
});
```
### Coverage Targets
**Measurable targets**:
```json
// jest.config.js
{
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
},
"./src/services/": {
"branches": 90,
"functions": 90,
"lines": 90,
"statements": 90
}
}
}
```
**Reasoning**:
- Critical business logic (services/) requires high coverage
- The UI layer is acceptable at a lower level
- Don't aim for 100% (balance cost and benefit)
## Code Review Process
### Purpose of Review
1. **Quality assurance**: Early detection of bugs
2. **Knowledge sharing**: Understanding the codebase across the whole team
3. **Learning opportunity**: Sharing best practices
### Points for Effective Reviews
**For reviewers**:
1. **Constructive feedback**
```markdown
## ❌ Bad example
This code is no good.
## ✅ Good example
This implementation results in O(n²) time complexity.
Using a Map can improve it to O(n):
```typescript
const taskMap = new Map(tasks.map(t => [t.id, t]));
const result = ids.map(id => taskMap.get(id));
```
```
2. **State the priority explicitly**
```markdown
[Required] Security: A password is being output to the logs
[Recommended] Performance: Avoid DB calls inside a loop
[Suggestion] Readability: Could this function name be made clearer?
[Question] Could you explain the intent of this processing?
```
3. **Positive feedback too**
```markdown
✨ This implementation is easy to understand!
👍 Edge cases are thoroughly considered
💡 This pattern looks reusable elsewhere
```
**For reviewees**:
1. **Perform a self-review**
- Review your own code before creating the PR
- Add comments where explanation is needed
2. **Aim for small PRs**
- 1 PR = 1 feature
- Number of changed files: 10 or fewer recommended
- Number of changed lines: 300 or fewer recommended
3. **Explain carefully**
- Why you chose this implementation
- Alternatives you considered
- Points you especially want reviewed
### Review Time Guidelines
- Small PR (100 lines or fewer): 15 minutes
- Medium PR (100-300 lines): 30 minutes
- Large PR (300 lines or more): 1 hour or more
**Principle**: Avoid large PRs; split them up
## Promoting Automation (Where Applicable)
### Automating Quality Checks
**Automation items and the tools adopted**:
1. **Lint checks**
- **ESLint 9.x** + **@typescript-eslint**
- Unifies coding conventions with a TypeScript-specific rule set
- Automatically detects potential bugs and deprecated patterns
- Configuration file: `eslint.config.js` (Flat Config format)
2. **Code formatting**
- **Prettier 3.x**
- Automatically formats code style, reducing debate during review
- Used together with ESLint, avoiding conflicts via `eslint-config-prettier`
- Configuration file: `.prettierrc`
3. **Type checking**
- **TypeScript Compiler (tsc) 5.x**
- Checks only type errors with `tsc --noEmit`
- Verifies type safety independently of the build
- Configuration file: `tsconfig.json`
4. **Running tests**
- **Vitest 2.x**
- Fast startup and execution based on Vite
- Natively supports TypeScript/ESM and works with zero configuration
- Coverage measurement (@vitest/coverage-v8) is built in as standard
- Modern development experience with HMR support
5. **Build verification**
- **TypeScript Compiler (tsc)**
- Guarantees a type-checked build with the standard compiler
- Simple configuration with no additional tools required
- Centralizes output settings in `tsconfig.json`
**Implementation methods**:
**1. CI/CD (GitHub Actions)**
```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm run test
- run: npm run build
```
**2. Pre-commit hooks (Husky 9.x + lint-staged)**
```json
// package.json
{
"scripts": {
"prepare": "husky",
"lint": "eslint .",
"format": "prettier --write .",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"build": "tsc"
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
}
```
```bash
# .husky/pre-commit
npm run lint-staged
npm run typecheck
```
**Effects of adoption**:
- Automated checks run before committing, preventing defective code from being introduced
- CI runs automatically when a PR is created, ensuring quality before merging
- Early detection reduces fix costs by up to 80% (compared to when bugs are found after production)
**Why this configuration was chosen**:
- A standard and modern configuration in the TypeScript ecosystem as of 2025
- High compatibility between tools, with few configuration conflicts
- An excellent balance between development experience and execution speed
## Checklist
- [ ] A branching strategy is decided
- [ ] Commit message conventions are clear
- [ ] A PR template is prepared
- [ ] Test types and coverage targets are set
- [ ] A code review process is defined
- [ ] A CI/CD pipeline is built

View File

@ -0,0 +1,410 @@
# Development Guidelines
## Coding Conventions
### Naming Conventions
#### Variables and Functions
**TypeScript/JavaScript**:
```typescript
// ✅ Good example
const userProfileData = fetchUserProfile();
function calculateTotalPrice(items: CartItem[]): number { }
// ❌ Bad example
const data = fetch();
function calc(arr: any[]): number { }
```
**Principles**:
- Variables: camelCase, noun or noun phrase
- Functions: camelCase, start with a verb
- Constants: UPPER_SNAKE_CASE
- Boolean: start with `is`, `has`, or `should`
#### Classes and Interfaces
```typescript
// Class: PascalCase, noun
class TaskManager { }
class UserAuthenticationService { }
// Interface: PascalCase, with or without the I prefix
interface ITaskRepository { }
interface Task { }
// Type alias: PascalCase
type TaskStatus = 'todo' | 'in_progress' | 'completed';
```
### Code Formatting
**Indentation**: [2 spaces / 4 spaces / tabs]
**Line length**: maximum [80/100/120] characters
**Example**:
```typescript
// [language] code formatting example
[code example]
```
### Comment Conventions
**Function and class documentation**:
```typescript
/**
* Calculate the total number of tasks
*
* @param tasks - Array of tasks to count
* @param filter - Filter condition (optional)
* @returns Total number of tasks
* @throws {ValidationError} When the task array is invalid
*/
function countTasks(
tasks: Task[],
filter?: TaskFilter
): number {
// Implementation
}
```
**Inline comments**:
```typescript
// ✅ Good example: explain why you do it
// Invalidate the cache to fetch the latest data
cache.clear();
// ❌ Bad example: what you are doing (obvious from the code)
// Clear the cache
cache.clear();
```
### Error Handling
**Principles**:
- Expected errors: define an appropriate error class
- Unexpected errors: propagate upward
- Do not ignore errors
**Example**:
```typescript
// Error class definition
class ValidationError extends Error {
constructor(
message: string,
public field: string,
public value: unknown
) {
super(message);
this.name = 'ValidationError';
}
}
// Error handling
try {
const task = await taskService.create(data);
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation error [${error.field}]: ${error.message}`);
// Provide feedback to the user
} else {
console.error('Unexpected error:', error);
throw error; // Propagate upward
}
}
```
## Git Workflow Rules
### Branching Strategy
**Branch types**:
- `main`: ready to deploy to production
- `develop`: latest development state
- `feature/[feature-name]`: new feature development
- `fix/[fix-description]`: bug fixes
- `refactor/[target]`: refactoring
**Flow**:
```
main
└─ develop
├─ feature/task-management
├─ feature/user-auth
└─ fix/task-validation
```
### Commit Message Conventions
**Format**:
```
<type>(<scope>): <subject>
<body>
<footer>
```
**Type**:
- `feat`: new feature
- `fix`: bug fix
- `docs`: documentation
- `style`: code formatting
- `refactor`: refactoring
- `test`: adding or fixing tests
- `chore`: build, supporting tools, etc.
**Example**:
```
feat(task): add a task priority setting feature
Allowed users to set a priority (high/medium/low) on tasks.
- Added a priority field to the Task model
- Added a --priority option to the CLI
- Implemented sorting by priority
Closes #123
```
### Pull Request Process
**Checks before creating**:
- [ ] All tests pass
- [ ] No lint errors
- [ ] Type checking passes
- [ ] Conflicts are resolved
**PR template**:
```markdown
## Overview
[A brief description of the changes]
## Reason for Change
[Why this change is necessary]
## Changes
- [change 1]
- [change 2]
## Testing
- [ ] Unit tests added
- [ ] Manual testing performed
## Screenshots (if applicable)
[image]
## Related Issue
Closes #[issue number]
```
**Review process**:
1. Self-review
2. Run automated tests
3. Assign reviewers
4. Address review feedback
5. Merge after approval
## Test Strategy
### Test Types
#### Unit Tests
**Target**: individual functions and classes
**Coverage target**: [80/90/100]%
**Example**:
```typescript
describe('TaskService', () => {
describe('create', () => {
it('can create a task with valid data', async () => {
const service = new TaskService(mockRepository);
const task = await service.create({
title: 'Test task',
description: 'Description',
});
expect(task.id).toBeDefined();
expect(task.title).toBe('Test task');
});
it('throws ValidationError when the title is empty', async () => {
const service = new TaskService(mockRepository);
await expect(
service.create({ title: '' })
).rejects.toThrow(ValidationError);
});
});
});
```
#### Integration Tests
**Target**: coordination of multiple components
**Example**:
```typescript
describe('Task CRUD', () => {
it('can create, read, update, and delete a task', async () => {
// Create
const created = await taskService.create({ title: 'Test' });
// Read
const found = await taskService.findById(created.id);
expect(found?.title).toBe('Test');
// Update
await taskService.update(created.id, { title: 'Updated' });
const updated = await taskService.findById(created.id);
expect(updated?.title).toBe('Updated');
// Delete
await taskService.delete(created.id);
const deleted = await taskService.findById(created.id);
expect(deleted).toBeNull();
});
});
```
#### E2E Tests
**Target**: the entire user scenario
**Example**:
```typescript
describe('Task management flow', () => {
it('a user can add and complete a task', async () => {
// Add a task
await cli.run(['add', 'New task']);
expect(output).toContain('Task added');
// Display the task list
await cli.run(['list']);
expect(output).toContain('New task');
// Complete the task
await cli.run(['complete', '1']);
expect(output).toContain('Task completed');
});
});
```
### Test Naming Conventions
**Pattern**: `[target]_[condition]_[expectedResult]`
**Example**:
```typescript
// ✅ Good example
it('create_emptyTitle_throwsValidationError', () => { });
it('findById_existingId_returnsTask', () => { });
it('delete_nonExistentId_throwsNotFoundError', () => { });
// ❌ Bad example
it('test1', () => { });
it('works', () => { });
it('should work correctly', () => { });
```
### Using Mocks and Stubs
**Principles**:
- Mock external dependencies (API, DB, file system)
- Use the real implementation for business logic
**Example**:
```typescript
// Mock the repository
const mockRepository: ITaskRepository = {
save: jest.fn(),
findById: jest.fn(),
findAll: jest.fn(),
delete: jest.fn(),
};
// The service uses the real implementation
const service = new TaskService(mockRepository);
```
## Code Review Criteria
### Review Points
**Functionality**:
- [ ] Does it meet the requirements?
- [ ] Are edge cases considered?
- [ ] Is error handling appropriate?
**Readability**:
- [ ] Is the naming clear?
- [ ] Are the comments appropriate?
- [ ] Is complex logic explained?
**Maintainability**:
- [ ] Is there any duplicate code?
- [ ] Are responsibilities clearly separated?
- [ ] Is the impact scope of changes limited?
**Performance**:
- [ ] Are there any unnecessary computations?
- [ ] Is there any possibility of a memory leak?
- [ ] Are database queries optimized?
**Security**:
- [ ] Is input validation appropriate?
- [ ] Is any sensitive information hardcoded?
- [ ] Are permission checks implemented?
### How to Write Review Comments
**Constructive feedback**:
```markdown
## ✅ Good example
With this implementation, performance may degrade as the number of tasks grows.
How about considering a search that uses an index instead?
## ❌ Bad example
This way of writing it is not good.
```
**Indicate priority**:
- `[required]`: must be fixed
- `[recommended]`: fix recommended
- `[suggestion]`: please consider
- `[question]`: a question for understanding
## Development Environment Setup
### Required Tools
| Tool | Version | Installation method |
|--------|-----------|-----------------|
| [tool 1] | [version] | [command] |
| [tool 2] | [version] | [command] |
### Setup Procedure
```bash
# 1. Clone the repository
git clone [URL]
cd [project-name]
# 2. Install dependencies
[install command]
# 3. Set environment variables
cp .env.example .env
# Edit the .env file
# 4. Start the development server
[start command]
```
### Recommended Development Tools (if applicable)
- [tool 1]: [description]
- [tool 2]: [description]