Skill

Master Python Architecture & Decision-Making

Python architecture decision guidance - framework choice, async vs sync, type hints, project structure, testing.

Works with fastapidjangoflaskcelerypytest

91
Spark score
out of 100
Updated 16 days ago
Version 14.1.0

Add to Favorites

Why it matters

Elevate your Python development by mastering architectural principles and decision-making for 2025. Learn to think critically about framework selection, async patterns, and project structure, rather than just memorizing code.

Outcomes

What it gets done

01

Guide framework selection (FastAPI, Django, Flask) based on project needs.

02

Determine optimal async vs. sync implementation for I/O-bound and CPU-bound tasks.

03

Implement effective type hinting strategies and leverage Pydantic for validation.

04

Structure Python projects for scalability and maintainability.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-python-patterns | bash

Overview

Python Patterns

Python architecture decision guidance across framework selection, async vs sync design, type hints, project structure, Django/FastAPI specifics, error handling, and testing strategy. Use when making Python architecture decisions on framework, async design, typing strategy, project structure, or testing approach.

What it does

Python Patterns covers decision-level Python architecture guidance across eleven areas rather than syntax basics. Framework Selection uses a decision tree and comparison principles to choose between frameworks based on project needs. Async vs Sync Decision states a golden rule for when async is worth its complexity, plus async library selection guidance. Type Hints Strategy covers when to type and common type patterns:

### These are patterns, understand them:

### Optional → might be None
from typing import Optional
def find_user(id: int) -> Optional[User]: ...

### Union → one of multiple types
def process(data: str | dict) -> None: ...

### Generic collections
def get_items() -> list[Item]: ...
def get_mapping() -> dict[str, int]: ...

### Callable
from typing import Callable
def apply(fn: Callable[[int], str]) -> str: ...

plus Pydantic for validation - used for API request/response models, configuration, data validation, and serialization, valued for runtime validation, auto-generated JSON schema, and native FastAPI integration. Project Structure Principles gives structure templates scaled by project size, from a small script (main.py, utils.py) to a medium API's app/ layout, plus FastAPI-specific structure principles. Django Principles covers Django 5.0+ async support and Django best practices. FastAPI Principles covers when to use async def versus def in route handlers, dependency injection, and Pydantic v2 integration for request validation and response serialization, where the return type annotation becomes the response schema. Background Tasks gives a selection guide for when to use each background-execution mechanism. Error Handling Principles covers exception strategy and error-response philosophy. Testing Principles covers testing strategy, async testing via pytest-asyncio, and fixture strategy. A Decision Checklist and a list of anti-patterns to avoid versus recommended practices close out the guide.

When to use - and when NOT to

Use this skill when making Python architecture decisions - framework selection, async versus sync design, type hint strategy, project structure, Django or FastAPI-specific choices, background task design, error handling strategy, or testing strategy. The golden rule for the async decision is stated directly: I/O-bound work goes async, CPU-bound work stays synchronous plus multiprocessing, and sync libraries should never be called from async code.

Inputs and outputs

Given a Python architecture decision, the skill outputs the relevant decision framework - a decision tree, a selection guide, or a golden rule - matched to the specific choice being made (framework, async model, type strategy, project layout, background task mechanism, error handling approach, or testing approach), plus a closing decision checklist and named anti-patterns to avoid with their recommended alternatives.

Integrations

Covers FastAPI (async def vs def, dependency injection, Pydantic v2 request/response integration), Django (5.0+ async support, best practices), Pydantic (validation, JSON schema generation), and pytest-asyncio for async test execution. Async library selection maps each need to a named library: httpx for HTTP clients, asyncpg for PostgreSQL, aioredis or redis-py's async mode for Redis, aiofiles for file I/O, and SQLAlchemy 2.0 async or Tortoise for the ORM layer.

Who it's for

Python developers and architects making structural decisions - which framework, whether to go async, how to type-hint, how to structure a project, how to handle errors and background tasks - who want decision frameworks and golden rules rather than a syntax reference.

Source README

Python Patterns

Python development principles and decision-making for 2025.
Learn to THINK, not memorize patterns.

When to Use

Use this skill when making Python architecture decisions, choosing frameworks, designing async patterns, or structuring Python projects.


⚠️ How to Use This Skill

This skill teaches decision-making principles, not fixed code to copy.

  • ASK user for framework preference when unclear
  • Choose async vs sync based on CONTEXT
  • Don't default to same framework every time

1. Framework Selection (2025)

Decision Tree

What are you building?
│
├── API-first / Microservices
│   └── FastAPI (async, modern, fast)
│
├── Full-stack web / CMS / Admin
│   └── Django (batteries-included)
│
├── Simple / Script / Learning
│   └── Flask (minimal, flexible)
│
├── AI/ML API serving
│   └── FastAPI (Pydantic, async, uvicorn)
│
└── Background workers
    └── Celery + any framework

Comparison Principles

Factor FastAPI Django Flask
Best for APIs, microservices Full-stack, CMS Simple, learning
Async Native Django 5.0+ Via extensions
Admin Manual Built-in Via extensions
ORM Choose your own Django ORM Choose your own
Learning curve Low Medium Low

Selection Questions to Ask:

  1. Is this API-only or full-stack?
  2. Need admin interface?
  3. Team familiar with async?
  4. Existing infrastructure?

2. Async vs Sync Decision

When to Use Async

async def is better when:
├── I/O-bound operations (database, HTTP, file)
├── Many concurrent connections
├── Real-time features
├── Microservices communication
└── FastAPI/Starlette/Django ASGI

def (sync) is better when:
├── CPU-bound operations
├── Simple scripts
├── Legacy codebase
├── Team unfamiliar with async
└── Blocking libraries (no async version)

The Golden Rule

I/O-bound → async (waiting for external)
CPU-bound → sync + multiprocessing (computing)

Don't:
├── Mix sync and async carelessly
├── Use sync libraries in async code
└── Force async for CPU work

Async Library Selection

Need Async Library
HTTP client httpx
PostgreSQL asyncpg
Redis aioredis / redis-py async
File I/O aiofiles
Database ORM SQLAlchemy 2.0 async, Tortoise

3. Type Hints Strategy

When to Type

Always type:
├── Function parameters
├── Return types
├── Class attributes
├── Public APIs

Can skip:
├── Local variables (let inference work)
├── One-off scripts
├── Tests (usually)

Common Type Patterns

### These are patterns, understand them:

### Optional → might be None
from typing import Optional
def find_user(id: int) -> Optional[User]: ...

### Union → one of multiple types
def process(data: str | dict) -> None: ...

### Generic collections
def get_items() -> list[Item]: ...
def get_mapping() -> dict[str, int]: ...

### Callable
from typing import Callable
def apply(fn: Callable[[int], str]) -> str: ...

Pydantic for Validation

When to use Pydantic:
├── API request/response models
├── Configuration/settings
├── Data validation
├── Serialization

Benefits:
├── Runtime validation
├── Auto-generated JSON schema
├── Works with FastAPI natively
└── Clear error messages

4. Project Structure Principles

Structure Selection

Small project / Script:
├── main.py
├── utils.py
└── requirements.txt

Medium API:
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── models/
│   ├── routes/
│   ├── services/
│   └── schemas/
├── tests/
└── pyproject.toml

Large application:
├── src/
│   └── myapp/
│       ├── core/
│       ├── api/
│       ├── services/
│       ├── models/
│       └── ...
├── tests/
└── pyproject.toml

FastAPI Structure Principles

Organize by feature or layer:

By layer:
├── routes/ (API endpoints)
├── services/ (business logic)
├── models/ (database models)
├── schemas/ (Pydantic models)
└── dependencies/ (shared deps)

By feature:
├── users/
│   ├── routes.py
│   ├── service.py
│   └── schemas.py
└── products/
    └── ...

5. Django Principles (2025)

Django Async (Django 5.0+)

Django supports async:
├── Async views
├── Async middleware
├── Async ORM (limited)
└── ASGI deployment

When to use async in Django:
├── External API calls
├── WebSocket (Channels)
├── High-concurrency views
└── Background task triggering

Django Best Practices

Model design:
├── Fat models, thin views
├── Use managers for common queries
├── Abstract base classes for shared fields

Views:
├── Class-based for complex CRUD
├── Function-based for simple endpoints
├── Use viewsets with DRF

Queries:
├── select_related() for FKs
├── prefetch_related() for M2M
├── Avoid N+1 queries
└── Use .only() for specific fields

6. FastAPI Principles

async def vs def in FastAPI

Use async def when:
├── Using async database drivers
├── Making async HTTP calls
├── I/O-bound operations
└── Want to handle concurrency

Use def when:
├── Blocking operations
├── Sync database drivers
├── CPU-bound work
└── FastAPI runs in threadpool automatically

Dependency Injection

Use dependencies for:
├── Database sessions
├── Current user / Auth
├── Configuration
├── Shared resources

Benefits:
├── Testability (mock dependencies)
├── Clean separation
├── Automatic cleanup (yield)

Pydantic v2 Integration

### FastAPI + Pydantic are tightly integrated:

### Request validation
@app.post("/users")
async def create(user: UserCreate) -> UserResponse:
    # user is already validated
    ...

### Response serialization
### Return type becomes response schema

7. Background Tasks

Selection Guide

Solution Best For
BackgroundTasks Simple, in-process tasks
Celery Distributed, complex workflows
ARQ Async, Redis-based
RQ Simple Redis queue
Dramatiq Actor-based, simpler than Celery

When to Use Each

FastAPI BackgroundTasks:
├── Quick operations
├── No persistence needed
├── Fire-and-forget
└── Same process

Celery/ARQ:
├── Long-running tasks
├── Need retry logic
├── Distributed workers
├── Persistent queue
└── Complex workflows

8. Error Handling Principles

Exception Strategy

In FastAPI:
├── Create custom exception classes
├── Register exception handlers
├── Return consistent error format
└── Log without exposing internals

Pattern:
├── Raise domain exceptions in services
├── Catch and transform in handlers
└── Client gets clean error response

Error Response Philosophy

Include:
├── Error code (programmatic)
├── Message (human readable)
├── Details (field-level when applicable)
└── NOT stack traces (security)

9. Testing Principles

Testing Strategy

Type Purpose Tools
Unit Business logic pytest
Integration API endpoints pytest + httpx/TestClient
E2E Full workflows pytest + DB

Async Testing

### Use pytest-asyncio for async tests

import pytest
from httpx import AsyncClient

@pytest.mark.asyncio
async def test_endpoint():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/users")
        assert response.status_code == 200

Fixtures Strategy

Common fixtures:
├── db_session → Database connection
├── client → Test client
├── authenticated_user → User with token
└── sample_data → Test data setup

10. Decision Checklist

Before implementing:

  • Asked user about framework preference?
  • Chosen framework for THIS context? (not just default)
  • Decided async vs sync?
  • Planned type hint strategy?
  • Defined project structure?
  • Planned error handling?
  • Considered background tasks?

11. Anti-Patterns to Avoid

❌ DON'T:

  • Default to Django for simple APIs (FastAPI may be better)
  • Use sync libraries in async code
  • Skip type hints for public APIs
  • Put business logic in routes/views
  • Ignore N+1 queries
  • Mix async and sync carelessly

✅ DO:

  • Choose framework based on context
  • Ask about async requirements
  • Use Pydantic for validation
  • Separate concerns (routes → services → repos)
  • Test critical paths

Remember: Python patterns are about decision-making for YOUR specific context. Don't copy code-think about what serves your application best.

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.