# استانداردهای کدنویسی ## اصول کلی ### 1. کد تمیز (Clean Code) کد باید: - خوانا باشد - ساده باشد - قابل فهم باشد - قابل نگهداری باشد ### 2. PEP 8 Compliance **الزامی**: تمام کدها باید با PEP 8 مطابقت داشته باشند. استفاده از ابزارها: ```bash black src/ # Auto-formatting flake8 src/ # Linting isort src/ # Import sorting ``` ## قوانین Naming ### Classes ```python # ✅ 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 ```python # ✅ 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 ```python # ✅ 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 داشته باشند. ```python 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 (استاندارد پروژه) ```python 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 ```python # ✅ 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 ```python class PeikarbandException(Exception): """Base exception.""" pass class DomainException(PeikarbandException): """Domain layer exceptions.""" pass class UserNotFoundException(DomainException): """User not found.""" pass ``` ## Code Organization ### Imports ```python # 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) ```python # ✅ 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 ```python # ✅ 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 ```python # 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 ```python # ✅ 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) ```python # ❌ 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 ```python # ✅ 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 ```python # ✅ 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 ```python # ✅ 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 بررسی می‌شوند.