Files
syndarix/docs/development/CODING_STANDARDS.md
Felipe Cardoso a7ba0f9bd8 docs: extract coding standards and add workflow documentation
- Create docs/development/WORKFLOW.md with branch strategy, issue
  management, testing requirements, and code review process
- Create docs/development/CODING_STANDARDS.md with technical patterns,
  auth DI pattern, testing patterns, and security guidelines
- Streamline CLAUDE.md to link to detailed documentation instead of
  embedding all content
- Add branch/issue workflow rules: single branch per feature for both
  design and implementation phases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:46:09 +01:00

313 lines
7.6 KiB
Markdown

# Coding Standards
Technical coding standards and patterns for Syndarix development.
## Table of Contents
- [Backend Standards](#backend-standards)
- [Frontend Standards](#frontend-standards)
- [Testing Patterns](#testing-patterns)
- [Security Guidelines](#security-guidelines)
---
## Backend Standards
### Dependency Management
- Use **uv** (modern Python package manager), not pip
- Always use `uv run` prefix for commands
- Add dependencies: `uv add <package>` or `uv add --dev <package>`
```bash
# Running tests
IS_TEST=True uv run pytest
# Or use Makefile
make test
make install-dev
```
### Database Migrations
- Use the `migrate.py` helper script, not Alembic directly
- Never commit migrations without testing them first
```bash
# Generate and apply migration
python migrate.py auto "message"
# Check current state
python migrate.py current
```
### Authentication Testing Fixtures
Backend fixtures in `tests/conftest.py`:
| Fixture | Purpose |
|---------|---------|
| `async_test_db` | Fresh SQLite per test |
| `async_test_user` | Pre-created regular user |
| `async_test_superuser` | Pre-created admin user |
| `user_token` | Access token for regular user |
| `superuser_token` | Access token for admin |
```python
# Always use these decorators for async tests
@pytest.mark.asyncio
async def test_something():
pass
# Use for async fixtures
@pytest_asyncio.fixture
async def my_fixture():
pass
```
### Database Exception Mocking
```python
from unittest.mock import patch, AsyncMock
async def mock_commit():
raise OperationalError("Connection lost", {}, Exception())
with patch.object(session, 'commit', side_effect=mock_commit):
with patch.object(session, 'rollback', new_callable=AsyncMock) as mock_rollback:
with pytest.raises(OperationalError):
await crud_method(session, obj_in=data)
mock_rollback.assert_called_once()
```
### Security Considerations
- Backend has comprehensive security tests (JWT attacks, session hijacking)
- Never skip security headers in production
- Rate limiting: `@limiter.limit("10/minute")`
- Session revocation is database-backed, not just JWT expiry
---
## Frontend Standards
### Auth Store Dependency Injection
**CRITICAL: ALWAYS use `useAuth()` from `AuthContext`, NEVER import `useAuthStore` directly!**
```typescript
// ❌ WRONG - Bypasses dependency injection
import { useAuthStore } from '@/lib/stores/authStore';
const { user, isAuthenticated } = useAuthStore();
// ✅ CORRECT - Uses dependency injection
import { useAuth } from '@/lib/auth/AuthContext';
const { user, isAuthenticated } = useAuth();
```
**Why This Matters:**
- E2E tests inject mock stores via `window.__TEST_AUTH_STORE__`
- Unit tests inject via `<AuthProvider store={mockStore}>`
- Direct `useAuthStore` imports bypass this injection → **tests fail**
- ESLint will catch violations
**Exceptions:**
1. `AuthContext.tsx` - DI boundary, legitimately needs real store
2. `client.ts` - Non-React context, uses dynamic import + `__TEST_AUTH_STORE__` check
### API Client Generation
```bash
# After backend schema changes
npm run generate:api
```
- Client is auto-generated from OpenAPI spec
- Located in `frontend/src/lib/api/generated/`
- **NEVER manually edit generated files**
### Component Development
- Follow design system docs in `frontend/docs/design-system/`
- Read `08-ai-guidelines.md` for AI code generation rules
- Use parent-controlled spacing (see `04-spacing-philosophy.md`)
- WCAG AA compliance required (see `07-accessibility.md`)
### Route Groups Structure
```
src/app/[locale]/
├── (auth)/ # Public auth pages (login, register, password reset)
│ # Uses AuthLayoutClient - redirects authenticated users
├── (authenticated)/ # Protected app pages
│ # Uses AuthGuard + Header/Footer
├── admin/ # Admin pages with sidebar
│ # Uses AuthGuard requireAdmin
├── dev/ # Design system documentation
└── prototypes/ # UI prototypes for approval
```
---
## Testing Patterns
### Frontend Unit Tests
```typescript
// Mock useAuth correctly
jest.mock('@/lib/auth/AuthContext', () => ({
useAuth: () => ({
user: { id: '1', email: 'test@example.com' },
isAuthenticated: true,
}),
}));
```
### E2E Test Best Practices (Playwright)
**Navigation Pattern:**
```typescript
// ✅ CORRECT - Use Promise.all for Next.js Link clicks
await Promise.all([
page.waitForURL('/target', { timeout: 10000 }),
link.click()
]);
```
**Selectors:**
- Use ID-based selectors for validation errors: `#email-error`
- Error IDs use dashes not underscores: `#new-password-error`
- Target `.border-destructive[role="alert"]` to avoid Next.js route announcer conflicts
- Avoid generic `[role="alert"]` which matches multiple elements
**URL Assertions:**
```typescript
// ✅ Use regex to handle query params
await expect(page).toHaveURL(/\/auth\/login/);
// ❌ Don't use exact strings (fails with query params)
await expect(page).toHaveURL('/auth/login');
```
**Configuration:**
- Uses 12 workers in non-CI mode
- Reduces to 2 workers in CI for stability
### Backend E2E Testing
Requires Docker for Testcontainers.
```bash
# Install deps
make install-e2e
# Run all E2E tests
make test-e2e
# Run schema tests only
make test-e2e-schema
# Run all tests (unit + E2E)
make test-all
```
Uses:
- Testcontainers (real PostgreSQL)
- Schemathesis (OpenAPI contract testing)
Markers:
- `@pytest.mark.e2e`
- `@pytest.mark.postgres`
- `@pytest.mark.schemathesis`
See: `backend/docs/E2E_TESTING.md` for complete guide.
---
## Security Guidelines
### OWASP Top 10 Compliance
All code must be reviewed for:
- Injection vulnerabilities (SQL, command, XSS)
- Broken authentication
- Sensitive data exposure
- Security misconfiguration
- Insecure deserialization
### Rate Limiting
```python
from app.core.limiter import limiter
@router.post("/endpoint")
@limiter.limit("10/minute")
async def endpoint():
pass
```
### JWT Security
- Tokens stored securely (httpOnly cookies preferred)
- Short expiration times
- Refresh token rotation
- Database-backed session revocation
---
## Common Workflows
### Adding a New Feature
1. Start with backend schema and CRUD
2. Implement API route with proper authorization
3. Write backend tests (aim for >90% coverage)
4. Generate frontend API client: `npm run generate:api`
5. Implement frontend components
6. Write frontend unit tests
7. Add E2E tests for critical flows
8. Update relevant documentation
### Fixing Tests
| Layer | Check |
|-------|-------|
| Backend | Test database isolation, async fixture usage |
| Frontend unit | Mock `useAuth()` not `useAuthStore` |
| E2E | Use `Promise.all()` pattern, regex URL assertions |
### Debugging
| Layer | Action |
|-------|--------|
| Backend | Check `IS_TEST=True` environment variable |
| Frontend | Run `npm run type-check` first |
| E2E | Use `npm run test:e2e:debug` for step-by-step |
| All | Check logs - backend has detailed error logging |
---
## Demo Mode
Frontend-only showcase mode for demos without backend.
```bash
# Enable
echo "NEXT_PUBLIC_DEMO_MODE=true" > frontend/.env.local
```
- Uses MSW (Mock Service Worker) to intercept API calls
- Zero backend required - perfect for Vercel deployments
- MSW handlers auto-generated from OpenAPI spec
- Run `npm run generate:api` → updates both API client AND MSW handlers
**Demo Credentials:**
- User: `demo@example.com` / `DemoPass123`
- Admin: `admin@example.com` / `AdminPass123`
**Safety:**
- MSW never runs during tests (Jest or Playwright)
- Mock files excluded from linting and coverage
See: `frontend/docs/DEMO_MODE.md` for complete guide.