@jjdenhertog/ai-driven-development
Version:
AI-driven development workflow with learning capabilities for Claude
290 lines (234 loc) • 7.22 kB
Markdown
---
name: "Coding Style Guide"
description: "Defines TypeScript/React coding conventions and patterns"
ai_instructions: |
When writing code:
1. Use arrow functions for components and callbacks
2. Let TypeScript infer types when obvious
3. Use interfaces for object shapes, types for unions/aliases
4. Follow the exact naming conventions specified
5. Prefer functional patterns and immutability
---
# Coding Style Guide
<ai-context>
This document defines HOW to write TypeScript/React code. These are strict conventions that must be
followed exactly. The style emphasizes readability, TypeScript inference, and functional patterns.
AI must follow these patterns precisely when generating code.
</ai-context>
This document defines HOW I write TypeScript/React code. Follow these patterns exactly.
## Core Principles
<ai-rules>
- USE arrow functions for components and most functions
- LET TypeScript infer types when obvious
- PREFER functional patterns over imperative
- DOCUMENT complex logic and public APIs with JSDoc
- AVOID unnecessary type annotations
</ai-rules>
1. **Strategic Documentation** - Code should be self-explanatory, but document complex logic and public APIs
2. **TypeScript Inference** - Let TypeScript infer types when possible
3. **Functional Style** - Prefer functional patterns and immutability
4. **Minimal JSDoc** - Use JSDoc sparingly for complex functions and public APIs
## Naming Conventions
### Variables
<validation-schema>
Variable Naming:
- ✅ userName, isActive, hasPermission (camelCase)
- ✅ users, files (plural for arrays)
- ✅ MAX_FILE_SIZE (UPPER_SNAKE for constants)
- ❌ user_name (no snake_case)
- ❌ UserName (PascalCase for variables)
- ❌ USERS (uppercase for regular arrays)
</validation-schema>
```typescript
// camelCase for regular variables
const userName = 'John';
const isActive = true;
const hasPermission = false;
const canEdit = false;
const shouldRender = true;
// Plural for collections
const users = [];
const files = [];
// UPPER_SNAKE_CASE for environment variables and enum-like objects
const API_KEY = process.env.API_KEY;
const MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB
export const ActivityTypes = {
CLIENT_CREATE: 40,
FLIGHT_CREATE: 30
};
```
### Functions
```typescript
// camelCase, verb-based, descriptive
const fetchUserData = async () => {};
const calculateTotal = (items) => {};
const formatBytes = (bytes: number, decimals: number = 2) => {};
// Event handlers with 'on' prefix
const onChange = () => {};
const onMouseOver = useCallback(() => {}, []);
// Hooks with 'use' prefix
const useAuth = () => {};
const useConfig = () => {};
```
### Types and Interfaces
```typescript
// PascalCase, no prefixes
// Use interface for object shapes
interface UserData {
id: string;
name: string;
}
interface Props {
title: string;
isActive?: boolean;
}
// Use type for unions, intersections, and aliases
type Status = "active" | "inactive" | "pending";
type ID = string | number;
type PartialUser = Partial<UserData>;
```
### Components
```typescript
// PascalCase with arrow functions
export const UserProfile: React.FC<Props> = ({ name, email }) => {
return <div>{name}</div>;
};
// B-prefix only when extending library components
export const BTextField = (props: Props) => {
// Extends @mui/TextField
};
```
## Function Patterns
### Component Style
<code-template name="react-component">
```typescript
// Arrow function with const
export const HomePage = ({ title, content }: Props) => {
// Hooks first
const [state, setState] = useState('');
const mounted = useRef(false);
// Callbacks with useCallback
const handleClick = useCallback(() => {
// handler logic
}, []);
// Effects
useEffect(() => {
// effect logic
}, []);
// Early returns
if (!title) return null;
// Main render
return <div>{content}</div>;
};
```
</code-template>
### Utility Functions
```typescript
// Named function with export
export function filterUnique(val: any, index: number, array: any[]) {
return array.indexOf(val) === index;
}
// Parameters on one line
export function processData(id: string, config: Config, options: Options) {}
// Default values inline
const formatBytes = (bytes: number, decimals: number = 2) => {};
```
### Return Patterns
```typescript
// Early returns without braces
if (!session) return;
if (typeof window === "undefined") return null;
// Multi-line conditions without braces
if (!origin || origin === 'center')
return defaults;
// No explicit return types (let TypeScript infer)
function calculateSum(a: number, b: number) {
return a + b;
}
```
## TypeScript Usage
### Type Inference
<ai-decision-tree>
Should I add a type annotation?
1. Is the type obvious from the value?
→ YES: Don't add type (let TypeScript infer)
→ NO: Continue to 2
2. Is it a function parameter?
→ YES: Add type annotation
→ NO: Continue to 3
3. Is it a complex object or union?
→ YES: Add type annotation
→ NO: Continue to 4
4. Would the inferred type be 'any'?
→ YES: Add type annotation
→ NO: Let TypeScript infer
</ai-decision-tree>
```typescript
// Let TypeScript infer when obvious
const [loading, setLoading] = useState(false); // NOT useState<boolean>(false)
const [name, setName] = useState(''); // NOT useState<string>('')
// Add types only when necessary
const [user, setUser] = useState<User | null>(null); // Type needed here
// Use @ts-ignore over casting
// @ts-ignore
element.style.webkitClipPath = clipPath;
// NOT: (element.style as any).webkitClipPath = clipPath;
```
### Interface vs Type Usage
```typescript
// Interfaces for object shapes and contracts
interface UserData {
id: string;
name: string;
email?: string;
}
interface Props {
readonly label?: string;
readonly onChange: (val: string) => void;
}
// Types for everything else
type Status = "active" | "inactive" | "pending";
type ID = string | number;
type UserWithStatus = UserData & { status: Status };
```
## State Management
<code-template name="state-update-pattern">
```typescript
// Functional updates when using previous state
setState(prev => ({
...prev,
layers: prev.layers.map(layer => {
if (layer.id !== targetId) return layer;
return { ...layer, ...updates };
})
}));
// Direct updates otherwise
setLoading(false);
setName('John');
```
</code-template>
## Import Organization
<ai-rules>
- GROUP imports by type (external, internal, types)
- ORDER: React first, then external libs, then internal
- USE absolute imports with @ alias
- AVOID default exports except for pages/components
- PREFER named exports for better refactoring
</ai-rules>
## Summary
<ai-decision-tree>
Which pattern should I use?
1. Defining a component?
→ Arrow function with const
2. Defining a type?
→ Object shape: interface
→ Union/alias: type
3. Naming something?
→ Component: PascalCase
→ Function: camelCase
→ Constant: UPPER_SNAKE_CASE
→ File: camelCase.ts or PascalCase.tsx
4. Adding types?
→ Obvious from value: No
→ Complex/unclear: Yes
</ai-decision-tree>