Pulsebook

Pulsebook

Development practices, workflows, and technical architecture guide for Equevu


Coding Standards

Overview

This document defines the coding standards and conventions for the Equevu fintech platform. All code must adhere to these standards to ensure consistency, maintainability, and alignment with our repository pattern architecture.

Table of Contents

Coding Standards

Python Style Guide

Basic Formatting

Import Organization

# Standard library imports
import os
import sys
from datetime import datetime

# Third-party imports
import requests
from django.db import models
from rest_framework import serializers

# Local application imports
from apps.companies.repository.company_repository import CompanyRepository
from apps.companies.services.company_service import CompanyService

Naming Conventions

Type Hints

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

def calculate_contribution(
    employee_id: int, 
    salary: Decimal, 
    is_native: bool = False
) -> Dict[str, Decimal]:
    pass

Repository Pattern Standards

All code must follow the repository pattern architecture as defined in architecture.md. Key principles:

Refer to the architecture document for detailed implementation examples and migration strategies.

Django Conventions

App Structure

Each Django app must follow this structure:

app_name/
├── models.py           # Minimal models only
├── repository/         # Data access layer
├── services/          # Business logic layer
├── external/          # External service integrations
├── utils/             # Helper functions
├── api_views.py       # API endpoints
├── serializers.py     # DRF serializers
├── urls.py
└── migrations/

Django-Specific Rules

# BAD - Slow operations inside transaction
@transaction.atomic
def process_payment(company_id, amount):
    company = Company.objects.select_for_update().get(id=company_id)
    
    # External API call inside transaction - BLOCKS OTHER OPERATIONS!
    payment_result = external_payment_api.process(amount)  # Could take 30+ seconds
    
    if payment_result.success:
        company.balance += amount
        company.save()

# GOOD - Only critical operations inside transaction
def process_payment(company_id, amount):
    # Prepare data outside transaction
    company = Company.objects.get(id=company_id)
    
    # External call outside transaction
    payment_result = external_payment_api.process(amount)
    
    if payment_result.success:
        # Only the atomic database operations
        with transaction.atomic():
            Company.objects.filter(id=company_id).update(
                balance=F('balance') + amount,
                last_payment=timezone.now()
            )
            PaymentLog.objects.create(
                company_id=company_id,
                amount=amount,
                reference=payment_result.reference
            )

Serializer Guidelines

class CreateCompanySerializer(serializers.Serializer):
    name = serializers.CharField(max_length=255)
    email = serializers.EmailField()
    
    def validate_email(self, value):
        return value.lower()  # Simple transformation only

class CompanyOutputSerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ['id', 'name', 'email', 'created_at']

Code Organization

File Naming

Method Organization

Within classes, organize methods in this order:

  1. __init__ and other special methods
  2. Public methods (business operations)
  3. Private helper methods (prefixed with _)

Dependency Injection

class CompanyService:
    def __init__(
        self, 
        company_repo: CompanyRepository = None,
        notification_client: NotificationClient = None
    ):
        self.company_repo = company_repo or CompanyRepository()
        self.notification_client = notification_client or NotificationClient()

Testing Requirements

Test Coverage

Test Structure

class TestCompanyService(TestCase):
    def setUp(self):
        self.service = CompanyService()
        self.mock_repo = Mock(spec=CompanyRepository)
        self.service.company_repo = self.mock_repo
    
    def test_create_company_success(self):
        # Arrange
        data = {'name': 'Test Corp', 'email': 'test@example.com'}
        self.mock_repo.get_by_email.return_value = None
        
        # Act
        result = self.service.create_company(data)
        
        # Assert
        self.assertEqual(result['status'], 'created')
        self.mock_repo.create.assert_called_once()

API Integration Tests

Example Apidog test scenarios:

Testing Best Practices

Documentation Standards

Docstrings

Every public class and method must have docstrings:

class CompanyService:
    """
    Service layer for company-related business operations.
    
    Handles company creation, updates, and business rule validation.
    """
    
    def create_company(self, data: Dict) -> Dict:
        """
        Create a new company with initial setup.
        
        Args:
            data: Dictionary containing company details
                - name: Company name
                - email: Company email
                - phone_number: Contact number
        
        Returns:
            Dictionary with company_id and status
        
        Raises:
            ValueError: If company with email already exists
            ValidationError: If data validation fails
        """
        pass

Inline Comments

Business Rules Documentation

def calculate_contribution(self, employee) -> Decimal:
    """Calculate employee contribution based on residency status."""
    base_salary = employee.basic_salary or Decimal('0')
    allowance = employee.allowance or Decimal('0')
    total_salary = base_salary + allowance
    
    # UAE nationals have higher contribution rate per government regulation
    if employee.is_native_resident:
        return total_salary * Decimal('0.125')  # 12.5% for nationals
    
    return total_salary * Decimal('0.05')  # 5% for expats

Security Guidelines

Never Commit

Input Validation

Database Security

API Security

# GOOD - Generic error message
except Exception as e:
    logger.error(f"Company creation failed: {str(e)}")
    return Response(
        {'error': 'Failed to create company'}, 
        status=status.HTTP_500_INTERNAL_SERVER_ERROR
    )

# BAD - Exposing internal details
except Exception as e:
    return Response(
        {'error': str(e), 'stack_trace': traceback.format_exc()}, 
        status=status.HTTP_500_INTERNAL_SERVER_ERROR
    )

Code Review Checklist

Before submitting a PR, ensure:

Enforcement

Remember: These standards exist to ensure our codebase remains maintainable, secure, and scalable as our fintech platform grows.