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

7.6 KiB

Coding Standards

Technical coding standards and patterns for Syndarix development.

Table of Contents


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>
# 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
# 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
# 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

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!

// ❌ 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

# 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

// 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:

// ✅ 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:

// ✅ 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.

# 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

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.

# 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.