Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
aiskillstore avatar

Python Best Practices

  • 1 installs
  • 404 repo stars
  • Updated August 5, 2026
  • aiskillstore/marketplace

python-best-practices is a Claude Code skill providing type-first Python patterns using dataclasses, discriminated unions, NewType, and Protocol.

About

python-best-practices is a Claude Code skill providing type-first Python patterns for reading and writing Python code. It covers frozen dataclasses, discriminated unions with Literal, NewType for domain primitives, Enums, Protocol structural typing, TypedDict for external data, module structure, functional patterns, and explicit exception handling. A developer uses it to write well-typed, maintainable Python.

  • Type-first Python development with dataclasses, discriminated unions, NewType, and Protocol
  • Patterns to make illegal states unrepresentable via the type system
  • Module structure, functional patterns, and explicit error-handling guidance

Python Best Practices by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #240 of 290 Python skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

python-best-practices capabilities & compatibility

Free; a pattern/reference skill with no external service

Capabilities
refactoring · code quality · typing
Use cases
refactoring · api development
Pricing
Free
From the docs

What python-best-practices says it does

Provides Python patterns for type-first development with dataclasses, discriminated unions, NewType, and Protocol.
SKILL.md
Use Python's type system to prevent invalid states at type-check time.
SKILL.md
npx skills add https://github.com/aiskillstore/marketplace --skill python-best-practices

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars404
Last updatedAugust 5, 2026
Repositoryaiskillstore/marketplace

What it does

Write type-first Python using dataclasses, discriminated unions, NewType, Protocol, and explicit error handling.

Who is it for?

Writing well-typed, maintainable Python with types defined before implementation

Skip if: Non-Python languages or runtime-heavy tasks unrelated to code structure

When should I use this skill?

Reading or writing Python files

What you get

Type-safe Python where illegal states are unrepresentable and failures are explicit.

  • type-first Python modules
  • typed data models

By the numbers

  • recommends splitting modules over ~300 lines
  • covers 6 typing constructs (dataclass, Literal union, NewType, Enum, Protocol, TypedDict)

Files

SKILL.mdMarkdownGitHub ↗

Python Best Practices

Type-First Development

Types define the contract before implementation. Follow this workflow:

1. Define data models - dataclasses, Pydantic models, or TypedDict first 2. Define function signatures - parameter and return type hints 3. Implement to satisfy types - let the type checker guide completeness 4. Validate at boundaries - runtime checks where data enters the system

Make Illegal States Unrepresentable

Use Python's type system to prevent invalid states at type-check time.

Dataclasses for structured data:

from dataclasses import dataclass
from datetime import datetime

@dataclass(frozen=True)
class User:
    id: str
    email: str
    name: str
    created_at: datetime

@dataclass(frozen=True)
class CreateUser:
    email: str
    name: str

# Frozen dataclasses are immutable - no accidental mutation

Discriminated unions with Literal:

from dataclasses import dataclass
from typing import Literal

@dataclass
class Idle:
    status: Literal["idle"] = "idle"

@dataclass
class Loading:
    status: Literal["loading"] = "loading"

@dataclass
class Success:
    status: Literal["success"] = "success"
    data: str

@dataclass
class Failure:
    status: Literal["error"] = "error"
    error: Exception

RequestState = Idle | Loading | Success | Failure

def handle_state(state: RequestState) -> None:
    match state:
        case Idle():
            pass
        case Loading():
            show_spinner()
        case Success(data=data):
            render(data)
        case Failure(error=err):
            show_error(err)

NewType for domain primitives:

from typing import NewType

UserId = NewType("UserId", str)
OrderId = NewType("OrderId", str)

def get_user(user_id: UserId) -> User:
    # Type checker prevents passing OrderId here
    ...

def create_user_id(raw: str) -> UserId:
    return UserId(raw)

Enums for constrained values:

from enum import Enum, auto

class Role(Enum):
    ADMIN = auto()
    USER = auto()
    GUEST = auto()

def check_permission(role: Role) -> bool:
    match role:
        case Role.ADMIN:
            return True
        case Role.USER:
            return limited_check()
        case Role.GUEST:
            return False
    # Type checker warns if case is missing

Protocol for structural typing:

from typing import Protocol

class Readable(Protocol):
    def read(self, n: int = -1) -> bytes: ...

def process_input(source: Readable) -> bytes:
    # Accepts any object with a read() method
    return source.read()

TypedDict for external data shapes:

from typing import TypedDict, Required, NotRequired

class UserResponse(TypedDict):
    id: Required[str]
    email: Required[str]
    name: Required[str]
    avatar_url: NotRequired[str]

def parse_user(data: dict) -> UserResponse:
    # Runtime validation needed - TypedDict is structural
    return UserResponse(
        id=data["id"],
        email=data["email"],
        name=data["name"],
    )

Module Structure

Prefer smaller, focused files: one class or closely related set of functions per module. Split when a file handles multiple concerns or exceeds ~300 lines. Use __init__.py to expose public API; keep implementation details in private modules (_internal.py). Colocate tests in tests/ mirroring the source structure.

Functional Patterns

  • Use list/dict/set comprehensions and generator expressions over explicit loops.
  • Prefer @dataclass(frozen=True) for immutable data; avoid mutable default arguments.
  • Use functools.partial for partial application; compose small functions over large classes.
  • Avoid class-level mutable state; prefer pure functions that take inputs and return outputs.

Instructions

  • Raise descriptive exceptions for unsupported cases; every code path returns a value or raises. This makes failures debuggable and prevents silent corruption.
  • Propagate exceptions with context using from err; catching requires re-raising or returning a meaningful result. Swallowed exceptions hide root causes.
  • Handle edge cases explicitly: empty inputs, None, boundary values. Include else clauses in conditionals where appropriate.
  • Use context managers for I/O; prefer pathlib and explicit encodings. Resource leaks cause production issues.
  • Add or adjust unit tests when touching logic; prefer minimal repros that isolate the failure.

Examples

Explicit failure for unimplemented logic:

def build_widget(widget_type: str) -> Widget:
    raise NotImplementedError(f"build_widget not implemented for type: {widget_type}")

Propagate with context to preserve the original traceback:

try:
    data = json.loads(raw)
except json.JSONDecodeError as err:
    raise ValueError(f"invalid JSON payload: {err}") from err

Exhaustive match with explicit default:

def process_status(status: str) -> str:
    match status:
        case "active":
            return "processing"
        case "inactive":
            return "skipped"
        case _:
            raise ValueError(f"unhandled status: {status}")

Debug-level tracing with namespaced logger:

import logging

logger = logging.getLogger("myapp.widgets")

def create_widget(name: str) -> Widget:
    logger.debug("creating widget: %s", name)
    widget = Widget(name=name)
    logger.debug("created widget id=%s", widget.id)
    return widget

Configuration

  • Load config from environment variables at startup; validate required values before use. Missing config should fail immediately.
  • Define a config dataclass or Pydantic model as single source of truth; avoid os.getenv scattered throughout code.
  • Use sensible defaults for development; require explicit values for production secrets.

Examples

Typed config with dataclass:

import os
from dataclasses import dataclass

@dataclass(frozen=True)
class Config:
    port: int = 3000
    database_url: str = ""
    api_key: str = ""
    env: str = "development"

    @classmethod
    def from_env(cls) -> "Config":
        database_url = os.environ.get("DATABASE_URL", "")
        if not database_url:
            raise ValueError("DATABASE_URL is required")
        return cls(
            port=int(os.environ.get("PORT", "3000")),
            database_url=database_url,
            api_key=os.environ["API_KEY"],  # required, will raise if missing
            env=os.environ.get("ENV", "development"),
        )

config = Config.from_env()

Optional: ty

For fast type checking, consider ty from Astral (creators of ruff and uv). Written in Rust, it's significantly faster than mypy or pyright.

Installation and usage:

# Run directly with uvx (no install needed)
uvx ty check

# Check specific files
uvx ty check src/main.py

# Install permanently
uv tool install ty

Key features:

  • Automatic virtual environment detection (via VIRTUAL_ENV or .venv)
  • Project discovery from pyproject.toml
  • Fast incremental checking
  • Compatible with standard Python type hints

Configuration in `pyproject.toml`:

[tool.ty]
python-version = "3.12"

When to use ty vs alternatives:

  • ty - fastest, good for CI and large codebases (early stage, rapidly evolving)
  • pyright - most complete type inference, VS Code integration
  • mypy - mature, extensive plugin ecosystem

Related skills

FAQ

What is the type-first workflow?

Define data models first, then function signatures, then implement to satisfy the types, and validate at boundaries.

How does it prevent invalid states?

By using frozen dataclasses, discriminated unions, NewType, Enums, and Protocol so illegal states are unrepresentable at type-check time.

Pythonbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.