Skip to Content
DevelopmentContributingCoding Standards

Coding Standards

This document outlines the coding standards and best practices for the Rhesis project. Following these standards ensures code quality, maintainability, and consistency across the codebase.

Code Quality Matters Consistent coding standards help us maintain high-quality code, improve collaboration, and reduce bugs. Please follow these guidelines for all contributions.

Python Standards

Code Style

We follow PEP 8  and use Ruff  for automatic formatting and linting.

Basic Rules:

  • Indentation: 4 spaces (no tabs)
  • Line Length: 88 characters maximum
  • Naming: snake_case for variables, functions, and modules
  • Classes: PascalCase for class names
  • Constants: UPPER_CASE for constants

Type Hints

All functions should include type hints:

from typing import List, Optional, Dict, Any from datetime import datetime def create_user( user_data: UserCreate, db: Session, organization_id: Optional[int] = None ) -> User: """Create a new user in the database.""" user = User( email=user_data.email, name=user_data.name, organization_id=organization_id ) db.add(user) db.commit() db.refresh(user) return user async def get_users_by_organization( org_id: int, db: AsyncSession, limit: int = 100 ) -> List[User]: """Retrieve users for a specific organization.""" result = await db.execute( select(User) .where(User.organization_id == org_id) .limit(limit) ) return result.scalars().all()

Docstrings

Use Google-style docstrings for all functions and classes:

def calculate_test_coverage( test_results: List[Dict[str, Any]], threshold: Optional[float] = None ) -> float: """ Calculate test coverage percentage from test results. Args: test_results: List of test result dictionaries containing status and other test information. threshold: Optional minimum coverage threshold. If provided, raises ValueError if coverage is below this value. Returns: Coverage percentage as a float between 0.0 and 100.0. Raises: ValueError: If test_results is empty or coverage is below threshold. Example: >>> results = [{'status': 'passed'}, {'status': 'failed'}] >>> calculate_test_coverage(results) 50.0 """ if not test_results: raise ValueError("Test results cannot be empty") total_tests = len(test_results) passed_tests = sum( 1 for result in test_results if result.get('status') == 'passed' ) coverage = (passed_tests / total_tests) * 100 if threshold and coverage < threshold: raise ValueError( f"Coverage {coverage}% below threshold {threshold}%" ) return coverage

Error Handling

Use specific exceptions and proper error handling:

from rhesis.backend.core.exceptions import ( UserNotFoundError, ValidationError, DatabaseError ) def get_user_by_id(user_id: int, db: Session) -> User: """Retrieve a user by ID with proper error handling.""" try: user = db.query(User).filter(User.id == user_id).first() if not user: raise UserNotFoundError(f"User with ID {user_id} not found") return user except SQLAlchemyError as e: raise DatabaseError(f"Database error: {str(e)}") from e except Exception as e: raise ValidationError(f"Unexpected error: {str(e)}") from e

Testing Standards

Unit Tests

Test individual functions and methods in isolation.

Integration Tests

Test component interactions and API endpoints.

Fixtures

Use pytest fixtures for test data and setup.

Coverage

Maintain 90%+ test coverage for new code.

Mocking

Mock external dependencies and services.

Example Test:

import pytest from unittest.mock import Mock, patch from rhesis.backend.services.user_service import UserService from rhesis.backend.models.user import User class TestUserService: """Test cases for UserService.""" @pytest.fixture def mock_db(self): """Create a mock database session.""" return Mock() @pytest.fixture def user_service(self, mock_db): """Create UserService instance with mock database.""" return UserService(mock_db) def test_create_user_success(self, user_service, mock_db): """Test successful user creation.""" # Arrange user_data = UserCreate( email="test@example.com", name="Test User", password="securepassword" ) mock_user = User( id=1, email="test@example.com", name="Test User" ) mock_db.add.return_value = None mock_db.commit.return_value = None mock_db.refresh.return_value = None # Act result = user_service.create_user(user_data) # Assert assert result.email == "test@example.com" assert result.name == "Test User" mock_db.add.assert_called_once() mock_db.commit.assert_called_once() mock_db.refresh.assert_called_once() def test_create_user_duplicate_email(self, user_service, mock_db): """Test user creation with duplicate email raises error.""" # Arrange user_data = UserCreate( email="existing@example.com", name="Test User", password="securepassword" ) mock_db.add.side_effect = IntegrityError("", "", "") # Act & Assert with pytest.raises(UserAlreadyExistsError): user_service.create_user(user_data)

JavaScript/TypeScript Standards

Code Style

We use ESLint and Prettier for code formatting and linting.

Basic Rules:

  • Indentation: 2 spaces
  • Line Length: 80 characters maximum
  • Naming: camelCase for variables and functions
  • Components: PascalCase for React components
  • Constants: UPPER_SNAKE_CASE for constants

TypeScript Usage

Use TypeScript for all new code with strict configuration:

// Define interfaces for data structures interface User { id: string email: string name: string organizationId: string createdAt: Date updatedAt: Date } interface UserCreateRequest { email: string name: string password: string organizationId?: string } interface ApiResponse<T> { data: T message: string success: boolean } // Use proper typing for functions const createUser = async (userData: UserCreateRequest): Promise<ApiResponse<User>> => { try { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const result = await response.json() return result } catch (error) { throw new Error(`Failed to create user: ${error.message}`) } }

React Components

Follow React best practices and hooks:

import React, { useState, useEffect, useCallback } from 'react'; import { useQuery, useMutation } from '@tanstack/react-query'; interface UserFormProps { onSubmit: (userData: UserCreateRequest) => void; initialData?: Partial<User>; isLoading?: boolean; } export const UserForm: React.FC<UserFormProps> = ({ onSubmit, initialData, isLoading = false, }) => { const [formData, setFormData] = useState<UserCreateRequest>({ email: initialData?.email || '', name: initialData?.name || '', password: '', }); const handleInputChange = useCallback( (field: keyof UserCreateRequest) => ( event: React.ChangeEvent<HTMLInputElement> ) => { setFormData(prev => ({ ...prev, [field]: event.target.value, })); }, [] ); const handleSubmit = useCallback( (event: React.FormEvent) => { event.preventDefault(); onSubmit(formData); }, [formData, onSubmit] ); return ( <form onSubmit={handleSubmit} className="space-y-4"> <div> <label htmlFor="email" className="block text-sm font-medium"> Email </label> <input id="email" type="email" value={formData.email} onChange={handleInputChange('email')} required className="mt-1 block w-full rounded-md border-gray-300" disabled={isLoading} /> </div> <div> <label htmlFor="name" className="block text-sm font-medium"> Name </label> <input id="name" type="text" value={formData.name} onChange={handleInputChange('name')} required className="mt-1 block w-full rounded-md border-gray-300" disabled={isLoading} /> </div> <button type="submit" disabled={isLoading} className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50" > {isLoading ? 'Creating...' : 'Create User'} </button> </form> ); };

Custom Hooks

Create reusable custom hooks for common functionality:

import { useState, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' export const useUsers = (organizationId: string) => { return useQuery({ queryKey: ['users', organizationId], queryFn: () => api.getUsers(organizationId), enabled: !!organizationId, staleTime: 5 * 60 * 1000, // 5 minutes }) } export const useCreateUser = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: api.createUser, onSuccess: newUser => { // Invalidate and refetch users list queryClient.invalidateQueries(['users', newUser.organizationId]) // Add new user to cache queryClient.setQueryData(['users', newUser.organizationId], (oldUsers: User[] = []) => [ ...oldUsers, newUser, ]) }, onError: error => { console.error('Failed to create user:', error) }, }) } export const useLocalStorage = <T>(key: string, initialValue: T): [T, (value: T) => void] => { const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key) return item ? JSON.parse(item) : initialValue } catch (error) { console.error(`Error reading localStorage key "${key}":`, error) return initialValue } }) const setValue = (value: T) => { try { setStoredValue(value) window.localStorage.setItem(key, JSON.stringify(value)) } catch (error) { console.error(`Error setting localStorage key "${key}":`, error) } } return [storedValue, setValue] }

Error Handling

Implement proper error boundaries and error handling:

import React, { Component, ErrorInfo, ReactNode } from 'react'; interface Props { children: ReactNode; fallback?: ReactNode; } interface State { hasError: boolean; error?: Error; } export class ErrorBoundary extends Component<Props, State> { constructor(props: Props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error: Error): State { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('Error caught by boundary:', error, errorInfo); } render() { if (this.state.hasError) { return ( this.props.fallback || ( <div className="p-4 bg-red-50 border border-red-200 rounded-md"> <h2 className="text-lg font-semibold text-red-800"> Something went wrong </h2> <p className="text-red-600"> {this.state.error?.message || 'An unexpected error occurred'} </p> </div> ) ); } return this.props.children; } }

General Best Practices

Code Organization

File Structure

Organize files logically and consistently.

Separation of Concerns

Keep functions and components focused and single-purpose.

DRY Principle

Don’t Repeat Yourself - extract common functionality.

SOLID Principles

Follow object-oriented design principles.

Clean Code

Write readable, self-documenting code.

Performance Considerations

// Use React.memo for expensive components const ExpensiveComponent = React.memo<Props>(({ data }) => { return <div>{/* Expensive rendering logic */}</div>; }); // Use useMemo for expensive calculations const expensiveValue = useMemo(() => { return performExpensiveCalculation(data); }, [data]); // Use useCallback for function props const handleClick = useCallback(() => { // Handle click logic }, [dependencies]);

Security Best Practices

Security Guidelines - Always validate and sanitize user input - Use parameterized queries to prevent SQL injection - Implement proper authentication and authorization - Keep dependencies updated and scan for vulnerabilities - Never expose sensitive information in logs or error messages

Documentation Standards

Code Comments:

  • Write comments for complex logic
  • Explain “why” not “what”
  • Keep comments up to date with code changes

README Files:

  • Include setup instructions
  • Document API usage
  • Provide examples

API Documentation:

  • Use OpenAPI/Swagger for backend APIs
  • Include request/response examples
  • Document error codes and messages

Pre-commit Hooks

We use pre-commit hooks to enforce code quality:

# .pre-commit-config.yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.0 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-eslint rev: v8.0.0 hooks: - id: eslint files: \.(js|ts|tsx)$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files

Code Review Checklist

When reviewing code, check for:

Functionality

Does the code work as intended?

Code Quality

Is the code readable and maintainable?

Testing

Are there adequate tests?

Documentation

Is the code well-documented?

Performance

Are there performance implications?

Security

Are there security concerns?


Questions? If you have questions about these coding standards: - Check our Development Setup Guide - Ask in GitHub Discussions  - Review existing code for examples