Files
peikarband/docs/development/coding-standards.md
Ehsan.Asadi 8a924f6091
Some checks failed
CD - Build & Deploy / build-and-push (push) Has been cancelled
CD - Build & Deploy / package-helm (push) Has been cancelled
CD - Build & Deploy / deploy-staging (push) Has been cancelled
CD - Build & Deploy / deploy-production (push) Has been cancelled
CD - Build & Deploy / release (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / security (push) Has been cancelled
[INIT-001] Initial project setup with Clean Architecture (feat)
- Implemented Clean Architecture with Domain, Application, Infrastructure, Presentation layers
- Added comprehensive project structure following SOLID principles
- Created Kubernetes deployment with Helm charts (HPA, PDB, NetworkPolicy)
- Configured ArgoCD for automated deployment (production + staging)
- Implemented CI/CD pipeline with GitHub Actions
- Added comprehensive documentation (handbook, architecture, coding standards)
- Configured PostgreSQL, Redis, Celery for backend services
- Created modern landing page with Persian fonts (Vazirmatn)
- Added Docker multi-stage build for production
- Configured development tools (pytest, black, flake8, mypy, isort)
- Added pre-commit hooks for code quality
- Implemented Makefile for common operations
2025-12-26 15:52:50 +03:30

7.7 KiB

استانداردهای کدنویسی

اصول کلی

1. کد تمیز (Clean Code)

کد باید:

  • خوانا باشد
  • ساده باشد
  • قابل فهم باشد
  • قابل نگهداری باشد

2. PEP 8 Compliance

الزامی: تمام کدها باید با PEP 8 مطابقت داشته باشند.

استفاده از ابزارها:

black src/      # Auto-formatting
flake8 src/     # Linting
isort src/      # Import sorting

قوانین Naming

Classes

# ✅ GOOD: PascalCase
class UserService:
    pass

class PaymentGateway:
    pass

class DatabaseConnection:
    pass

# ❌ BAD
class user_service:  # Wrong case
class payment_gateway_service:  # Too long
class UServ:  # Unclear abbreviation

Functions & Methods

# ✅ GOOD: snake_case, descriptive
def calculate_total_price(items: List[Item]) -> Decimal:
    pass

def send_welcome_email(user: User) -> bool:
    pass

# ❌ BAD
def calcTotPrice():  # Mixed case
def cp():  # Not descriptive
def calculate():  # Too vague

Variables

# ✅ GOOD
user_email = "test@example.com"
total_amount = Decimal("100.00")
is_active = True
MAX_ATTEMPTS = 3  # Constant

# ❌ BAD
e = "test@example.com"  # Too short
userEmail = "test@example.com"  # camelCase
TOTAL = 100  # Constant style for variable

Type Hints

الزامی: همه function signatures باید type hints داشته باشند.

from typing import Optional, List, Dict, Any
from decimal import Decimal

# ✅ GOOD
def get_user(user_id: int) -> Optional[User]:
    pass

def create_invoice(
    user_id: int,
    amount: Decimal,
    items: List[InvoiceItem]
) -> Invoice:
    pass

# ❌ BAD
def get_user(user_id):  # No types
    pass

def create_invoice(user_id, amount, items):  # No types
    pass

Docstrings

الزامی: همه public functions, classes, و methods باید docstring داشته باشند.

Google Style (استاندارد پروژه)

def create_user(
    email: str,
    password: str,
    full_name: str
) -> User:
    """Create a new user account.
    
    Args:
        email: User's email address
        password: Plain text password
        full_name: User's full name
        
    Returns:
        User: Created user object
        
    Raises:
        EmailAlreadyExistsException: If email exists
        WeakPasswordException: If password is weak
        
    Example:
        >>> user = create_user(
        ...     email="test@example.com",
        ...     password="SecurePass123!",
        ...     full_name="John Doe"
        ... )
    """
    pass

Error Handling

Use Specific Exceptions

# ✅ GOOD
try:
    user = user_repository.get_by_id(user_id)
    if not user:
        raise UserNotFoundException(f"User {user_id} not found")
except DatabaseException as e:
    logger.error("database_error", error=str(e))
    raise
except Exception as e:
    logger.critical("unexpected_error", error=str(e))
    raise

# ❌ BAD
try:
    user = get_user(user_id)
except:  # Never use bare except!
    pass

try:
    user = get_user(user_id)
except Exception:  # Too broad
    pass

Custom Exception Hierarchy

class PeikarbandException(Exception):
    """Base exception."""
    pass

class DomainException(PeikarbandException):
    """Domain layer exceptions."""
    pass

class UserNotFoundException(DomainException):
    """User not found."""
    pass

Code Organization

Imports

# 1. Standard library
import os
import sys
from typing import Optional, List
from decimal import Decimal

# 2. Third-party
import redis
from sqlalchemy import Column, Integer
from pydantic import BaseModel

# 3. Local
from src.config.settings import settings
from src.core.domain.entities.user import User

Function Length

Max 50 lines per function (توصیه: 20-30 lines)

# ✅ GOOD: Short and focused
def calculate_discount(amount: Decimal, user: User) -> Decimal:
    """Calculate user discount."""
    if user.is_premium:
        return amount * Decimal("0.10")
    return Decimal("0.00")

# ❌ BAD: Too long (100+ lines)
def process_order(order_data):
    # 100+ lines of code
    pass

Class Length

Max 300 lines per class (توصیه: 100-200 lines)

اگر class بزرگ شد، به چند class کوچکتر تقسیم کنید.

Comments

When to Comment

# ✅ GOOD: Explain WHY, not WHAT
def calculate_tax(amount: Decimal) -> Decimal:
    # Iranian tax rate is 9% as of 2024
    TAX_RATE = Decimal("0.09")
    return amount * TAX_RATE

# ❌ BAD: States the obvious
def add(a, b):
    # Add two numbers
    return a + b

TODO Comments

# TODO(username): Description of what needs to be done
# TODO(john): Implement caching for this query
# FIXME(jane): This breaks when amount is negative
# HACK(bob): Temporary workaround until API is fixed

Best Practices

1. Single Responsibility

# ✅ GOOD
class UserRepository:
    def save(self, user: User) -> User:
        pass

class EmailService:
    def send_email(self, to: str, subject: str) -> bool:
        pass

# ❌ BAD
class UserManager:
    def save_user(self, user):
        pass
    
    def send_email(self, user):
        pass
    
    def log_activity(self, user):
        pass

2. DRY (Don't Repeat Yourself)

# ❌ BAD
def get_user_name(user_id):
    db = connect_db()
    user = db.query(User).filter_by(id=user_id).first()
    db.close()
    return user.name

def get_user_email(user_id):
    db = connect_db()
    user = db.query(User).filter_by(id=user_id).first()
    db.close()
    return user.email

# ✅ GOOD
def get_user(user_id: int) -> Optional[User]:
    with get_db_context() as db:
        return db.query(User).filter_by(id=user_id).first()

def get_user_name(user_id: int) -> Optional[str]:
    user = get_user(user_id)
    return user.name if user else None

3. Early Return

# ✅ GOOD: Early return
def process_payment(amount: Decimal) -> bool:
    if amount <= 0:
        return False
    
    if not user.has_sufficient_balance(amount):
        return False
    
    # Process payment
    return True

# ❌ BAD: Nested conditions
def process_payment(amount):
    if amount > 0:
        if user.has_sufficient_balance(amount):
            # Process payment
            return True
    return False

4. Use Constants

# ✅ GOOD
MAX_LOGIN_ATTEMPTS = 3
PASSWORD_MIN_LENGTH = 8
SESSION_TIMEOUT_MINUTES = 30

if attempts >= MAX_LOGIN_ATTEMPTS:
    lock_account()

# ❌ BAD: Magic numbers
if attempts >= 3:  # What's 3?
    lock_account()

5. Avoid Deep Nesting

# ✅ GOOD: Max 2-3 levels
def process_order(order: Order) -> bool:
    if not order.is_valid():
        return False
    
    if not user.can_order():
        return False
    
    return save_order(order)

# ❌ BAD: Too nested
def process_order(order):
    if order:
        if order.is_valid():
            if user:
                if user.can_order():
                    if save_order(order):
                        return True
    return False

Code Review Checklist

قبل از ارسال PR، این موارد را بررسی کنید:

  • همه تست‌ها pass می‌شوند
  • Code coverage کافی است (>80%)
  • تمام functions دارای type hints هستند
  • تمام public functions دارای docstring هستند
  • black, flake8, mypy بدون error
  • هیچ TODO/FIXME جدید بدون توضیح نیست
  • تغییرات در CHANGELOG.md ثبت شده
  • مستندات به‌روز شده

الزامات: این استانداردها الزامی هستند و در code review بررسی می‌شوند.