Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .augment/rules/agent_tool_calls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: "always_apply"
---

- Always list the tools called (if any) during the prompt processing at the end of your response.
- Always list the rules file(s) used (if any) at the end of your prompt response.
227 changes: 227 additions & 0 deletions .augment/rules/backend-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# Backend Development Rules

## Python 3 Best Practices

### Rule 1: Always Use Type Hints
Use type hints for all function signatures, class attributes, and variables where type is not obvious. Use `typing` module for complex types.

**Example:**
```python
# ❌ Bad
def get_user(user_id):
return db.query(user_id)

# ✅ Good
from typing import Optional
from models import User

def get_user(user_id: str) -> Optional[User]:
"""Retrieve a user by ID.

Args:
user_id: The unique identifier for the user

Returns:
User object if found, None otherwise
"""
return db.query(User).filter_by(id=user_id).first()
```

**For complex types:**
```python
from typing import Dict, List, Union, TypedDict

class UserData(TypedDict):
id: str
name: str
email: str
roles: List[str]

def process_users(users: List[UserData]) -> Dict[str, Union[str, int]]:
return {
"count": len(users),
"status": "processed"
}
```

### Rule 2: Use Async/Await for I/O Operations
Use `async`/`await` for I/O-bound operations (database queries, API calls, file operations) to improve performance.

**Example:**
```python
# ❌ Bad - Synchronous I/O
def fetch_user_data(user_ids: List[str]) -> List[User]:
users = []
for user_id in user_ids:
user = requests.get(f"/api/users/{user_id}").json()
users.append(user)
return users

# ✅ Good - Async I/O
import asyncio
import aiohttp
from typing import List

async def fetch_user_data(user_ids: List[str]) -> List[User]:
async with aiohttp.ClientSession() as session:
tasks = [fetch_single_user(session, user_id) for user_id in user_ids]
return await asyncio.gather(*tasks)

async def fetch_single_user(session: aiohttp.ClientSession, user_id: str) -> User:
async with session.get(f"/api/users/{user_id}") as response:
data = await response.json()
return User(**data)
```

### Rule 3: Use Context Managers for Resource Management
Always use context managers (`with` statement) for managing resources like files, database connections, and locks.

**Example:**
```python
# ❌ Bad
def read_config():
file = open('config.json')
data = json.load(file)
file.close()
return data

# ✅ Good
from pathlib import Path
import json

def read_config() -> dict:
config_path = Path('config.json')
with config_path.open('r') as file:
return json.load(file)

# ✅ Good - Custom context manager
from contextlib import asynccontextmanager
from typing import AsyncGenerator

@asynccontextmanager
async def get_db_session() -> AsyncGenerator[Session, None]:
session = SessionLocal()
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
```

## UV Package Manager Usage

### Rule 4: Use UV for Dependency Management
Use `uv` for fast, reliable Python package management. Define dependencies in `pyproject.toml` and use `uv.lock` for reproducible builds.

**Example:**
```toml
# pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.104.0",
"pydantic>=2.0.0",
"sqlalchemy>=2.0.0",
]

[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"pytest-asyncio>=0.21.0",
"ruff>=0.1.0",
"mypy>=1.7.0",
]
```

**Commands:**
```bash
# Install dependencies
uv pip install -e .

# Install with dev dependencies
uv pip install -e ".[dev]"

# Add a new dependency
uv pip install package-name
# Then update pyproject.toml manually

# Sync dependencies (install/update to match pyproject.toml)
uv pip sync
```

## API Design Patterns

### Rule 5: Follow RESTful Conventions and Use Proper HTTP Status Codes
Design APIs following REST principles with proper HTTP methods and status codes. Use Pydantic models for request/response validation.

**Example:**
```python
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, EmailStr
from typing import List

app = FastAPI()

class UserCreate(BaseModel):
name: str
email: EmailStr

class UserResponse(BaseModel):
id: str
name: str
email: str

class Config:
from_attributes = True

# ✅ Good - RESTful endpoints with proper status codes
@app.post("/users", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(user: UserCreate) -> UserResponse:
"""Create a new user."""
existing = await get_user_by_email(user.email)
if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="User with this email already exists"
)
new_user = await db.create_user(user)
return UserResponse.from_orm(new_user)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example declares pydantic>=2.0.0 but uses UserResponse.from_orm(...) and an inner Config class; in Pydantic v2, from_orm is replaced and configuration uses model_config, so this may be misleading for readers.

🤖 Was this useful? React with 👍 or 👎


@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: str) -> UserResponse:
"""Retrieve a user by ID."""
user = await db.get_user(user_id)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User {user_id} not found"
)
return UserResponse.from_orm(user)

@app.put("/users/{user_id}", response_model=UserResponse)
async def update_user(user_id: str, user: UserCreate) -> UserResponse:
"""Update an existing user."""
updated = await db.update_user(user_id, user)
if not updated:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User {user_id} not found"
)
return UserResponse.from_orm(updated)

@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(user_id: str) -> None:
"""Delete a user."""
deleted = await db.delete_user(user_id)
if not deleted:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User {user_id} not found"
)
```

Loading