The Problem
You established a great pattern for API endpoints in one service. Then you built a second service and used a different pattern. A third service uses yet another approach. Now you have 12 microservices, each with different file structures, naming conventions, and error handling strategies. Onboarding takes weeks. AI suggestions are inconsistent because it can't tell which pattern to follow.
Patterns that don't scale across your codebase create chaos and confusion.
The challenge: taking a pattern that works at one level and replicating it consistently across all levels - from single functions to entire service fleets.
The Core Insight
Fractal scaling means applying the same patterns at function, file, module, service, and system levels. AI learns once, applies everywhere.
Think of a matryoshka doll: each level is a smaller version of the larger one. In code: a function should look like a miniature service. A service should look like a miniature system. Same patterns, different scales.
When AI sees this consistency, it doesn't need to learn your architecture separately for each component. It recognizes the pattern once and transfers the understanding everywhere.
The Walkthrough
Level 1: Function-Level Patterns
Establish a consistent pattern for all functions:
# Pattern: Input validation → Business logic → Output formatting
def create_user(user_data: dict) -> Result[User]:
# 1. Validate
if not user_data.get('email'):
return Err(ValidationError("Email required"))
# 2. Business logic
user = User(**user_data)
user.hash_password()
# 3. Return
return Ok(user)
def create_post(post_data: dict) -> Result[Post]:
# Same 3-step pattern
if not post_data.get('title'):
return Err(ValidationError("Title required"))
post = Post(**post_data)
post.generate_slug()
return Ok(post)
AI sees: "Every function follows validate → process → return. All return Result[T]."
Level 2: File-Level Patterns
Every file in a domain has the same structure:
# users/models.py
from dataclasses import dataclass
@dataclass
class User:
id: str
email: str
# Always: dataclasses for models
# users/services.py
class UserService:
def __init__(self, repo: UserRepository):
self.repo = repo
# Always: service class with injected repository
# users/routes.py
@app.route('/users', methods=['POST'])
def create_user(ctx: Context):
result = ctx.services.users.create(ctx.request.json)
return ctx.respond(result)
# Always: route functions that delegate to services
This pattern repeats for every domain: posts/, comments/, orders/, etc.
Level 3: Module-Level Patterns
Every domain module has identical structure:
users/
├── __init__.py # Exports public API
├── models.py # Data definitions
├── services.py # Business logic
├── repository.py # Data access
├── routes.py # HTTP endpoints
├── tests.py # Tests
└── README.md # Domain documentation
posts/
├── __init__.py # Same structure
├── models.py
├── services.py
├── repository.py
├── routes.py
├── tests.py
└── README.md
AI prompt: "Add a new 'notifications' domain"
AI response: Creates all 7 files following the established pattern.
Level 4: Service-Level Patterns
Every microservice has the same architecture:
user-service/
├── api/ # HTTP layer
├── core/ # Business logic
├── data/ # Database layer
├── config/ # Configuration
├── tests/ # Test suite
└── Dockerfile # Deployment
order-service/
├── api/ # Same layers
├── core/
├── data/
├── config/
├── tests/
└── Dockerfile
payment-service/
├── api/ # Identical pattern
├── core/
├── data/
├── config/
├── tests/
└── Dockerfile
Someone joining the team knows: "All services follow this structure. If I learned user-service, I know how to navigate payment-service."
The Cognitive Load Advantage
When patterns scale, developers (and AI) only learn architecture once. New services, modules, or functions are immediately familiar. This compounds over time.
Real Example: E-commerce Platform
Fractal Pattern at Every Scale
Pattern: Separate concerns into layers: API → Domain → Data
| Scale | Pattern Application | Example |
|---|---|---|
| Function | Parse input → Execute logic → Format output | def create_order(data) → validate() → process() → respond() |
| Class | Constructor → Methods → Private helpers | class OrderService: __init__() → public methods → _private helpers |
| Module | routes.py → services.py → repository.py | HTTP → business logic → database |
| Service | api/ → core/ → data/ | REST layer → domain layer → persistence |
| System | API Gateway → Business Services → Data Stores | nginx → microservices → PostgreSQL |
At every scale, the pattern is the same: interface → logic → storage. AI sees this once and understands it everywhere.
Enforcement Through Tooling
Scaffolding Scripts
Don't manually replicate patterns. Automate it:
#!/usr/bin/env python3
# scripts/new_domain.py
import os
import sys
def create_domain(name):
"""Create a new domain following fractal pattern."""
domain_dir = f"src/{name}"
# Create directory structure
os.makedirs(f"{domain_dir}")
# Generate files from templates
files = {
'models.py': MODEL_TEMPLATE.format(name=name),
'services.py': SERVICE_TEMPLATE.format(name=name),
'repository.py': REPO_TEMPLATE.format(name=name),
'routes.py': ROUTES_TEMPLATE.format(name=name),
'tests.py': TEST_TEMPLATE.format(name=name),
'__init__.py': INIT_TEMPLATE.format(name=name),
'README.md': README_TEMPLATE.format(name=name)
}
for filename, content in files.items():
with open(f"{domain_dir}/{filename}", 'w') as f:
f.write(content)
print(f"Created {name} domain following fractal pattern")
# Usage: python scripts/new_domain.py notifications
Now every new domain is guaranteed to follow the pattern. No drift.
Linting for Pattern Compliance
# .eslintrc.yml or similar
rules:
# Enforce naming patterns
- "Service classes must end with 'Service'"
- "Repository classes must end with 'Repository'"
- "Route functions must be in routes.py"
- "Models must use dataclasses or Pydantic"
# Enforce structure
- "Services must inject dependencies via constructor"
- "All functions must have type hints"
- "All public methods must have docstrings"
Architecture Decision Records (ADRs)
Document your fractal patterns:
# docs/architecture/ADR-001-fractal-patterns.md
## Decision: Fractal Domain Structure
All domains (users, orders, payments, etc.) follow this structure:
```
domain/
├── models.py # Data models (dataclasses)
├── services.py # Business logic (injected dependencies)
├── repository.py # Data access (interface + impl)
├── routes.py # HTTP endpoints (thin layer)
├── tests.py # Test suite
└── __init__.py # Public API
```
**Rationale:** Consistency enables fast onboarding, clear ownership, and predictable AI suggestions.
**Enforcement:** Use `scripts/new_domain.py` to scaffold. Linter checks for compliance.
Migrating to Fractal Patterns
Step 1: Document Current State
# Audit existing patterns
find . -name "*.py" -exec grep "class.*Service" {} \; | wc -l
# Count: 47 service classes
# How many follow DI pattern?
grep -r "def __init__.*Repository" --include="*service.py" | wc -l
# Count: 12 (only 25% fractal!)
# Document gap
echo "39 services need refactoring to fractal pattern"
Step 2: Pick One Pattern and Migrate
Don't try to fix everything. Pick one pattern (e.g., service layer) and migrate it fully:
# Week 1: Refactor all services to use DI
# Week 2: Refactor all repositories to use interfaces
# Week 3: Standardize all route handlers
# Week 4: Unify error handling across all layers
Step 3: Prevent Regression
# Add CI check
# .github/workflows/fractal-check.yml
name: Fractal Pattern Check
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check pattern compliance
run: |
python scripts/check_fractal_patterns.py
# Fails if new code violates patterns
Failure Patterns
1. Partial Migration
Symptom: Half the codebase uses new pattern, half uses old. Confusion reigns.
Fix: Don't introduce new patterns until you can migrate all existing code or clearly mark legacy vs new.
2. Pattern Drift
Symptom: Pattern starts consistent but each team adds "improvements" until it fragments.
Fix: Central architecture ownership. Changes to patterns require approval.
3. Over-Engineering for Fractal Purity
Symptom: Forcing fractal patterns where they don't fit, creating unnecessary abstraction.
# Don't do this
class PrintHelloService:
def __init__(self, printer: Printer):
self.printer = printer
def execute(self):
self.printer.print("Hello")
# Simple function is fine
def print_hello():
print("Hello")
Fix: Apply fractal patterns to reusable components. One-off scripts don't need it.
4. No Documentation
Symptom: Patterns exist but aren't documented. New developers don't know they should follow them.
Fix: ADRs, scaffolding scripts, and onboarding docs that explicitly teach the patterns.
When NOT to Scale Patterns
- Experiments: Prototypes don't need fractal rigor
- Legacy systems: Don't refactor working legacy code just for pattern purity
- Third-party integrations: Sometimes you must match their patterns
- Unique domains: Some problems genuinely need custom patterns
AI-Assisted Pattern Scaling
Prompt: Generate Scaffolding
Prompt to AI:
"Create a scaffolding script that generates a new domain following this pattern:
Existing domains:
- users/ (with models, services, repository, routes, tests)
- posts/ (same structure)
Generate a Python script that creates a new domain with the same file structure and template content."
Prompt: Migrate to Pattern
Prompt to AI:
"Refactor this service to match the fractal pattern used in UserService.
Current code:
[paste non-fractal code]
Reference pattern (UserService):
[paste fractal example]
Requirements:
- Constructor dependency injection
- Same method naming conventions
- Same error handling approach"
Prompt: Validate Pattern Compliance
Prompt to AI:
"Review this code and check if it follows our fractal patterns:
1. Services end with 'Service' suffix
2. Dependencies injected via constructor
3. Returns Result[T] type
4. Has docstrings on public methods
Code:
[paste code]
List any violations and suggest fixes."
Quick Reference
Fractal Scaling Checklist:
- ✓ Function patterns are consistent across codebase
- ✓ File structures repeat for each domain
- ✓ Module layouts are identical for similar components
- ✓ Service architectures mirror each other
- ✓ Naming conventions scale across all levels
- ✓ Error handling is uniform everywhere
- ✓ Testing approaches are standardized
Pattern Scaling Layers:
| Layer | What to Standardize |
|---|---|
| Function | Signature style, error handling, return types |
| File | Imports order, section organization, naming |
| Module | File structure, directory layout, exports |
| Service | Architecture layers, config management, deployment |
| System | Inter-service communication, observability, infrastructure |
Enforcement Tools:
# 1. Scaffolding
python scripts/new_domain.py {name}
python scripts/new_service.py {name}
# 2. Linting
eslint --rule "fractal-patterns"
pylint --load-plugins fractal_checker
# 3. CI/CD Checks
.github/workflows/pattern-check.yml
# 4. Documentation
docs/architecture/ADR-fractal-patterns.md
# 5. Code Review
.github/PULL_REQUEST_TEMPLATE.md
- [ ] Follows fractal patterns
- [ ] Used scaffolding scripts
- [ ] Updated pattern docs if changed
Migration Strategy:
Phase 1: Document current patterns (1 week)
Phase 2: Choose target fractal pattern (1 week)
Phase 3: Create scaffolding tools (1 week)
Phase 4: Migrate core services (4 weeks)
Phase 5: Enforce for new code (ongoing)
Phase 6: Gradual migration of legacy (ongoing)