New to Claude Skills? Learn how to install them →

basicmachines-co on GitHub

Pythonic Code

Free

Write clear and maintainable Python code with ease.

Get this skill

Free · Opens the source repo

What Pythonic Code does

The Pythonic Code skill is designed to assist developers in writing Python code that is not only functional but also clear and maintainable. It emphasizes the importance of clarity, explicit behavior, and local reasoning in code design. By adopting a Constructive Domain Modeling approach, this skill encourages developers to define valid values and outcomes that their code can produce, allowing the types to guide the implementation and ensure correctness. This methodology is particularly beneficial when dealing with complex or object-heavy code, as it helps to simplify and clarify the intended behavior of the program.

When using Pythonic Code, developers are guided through a structured process that begins with understanding the project context and existing conventions. This includes reading relevant documentation and ensuring that any changes respect the established patterns and stability of the codebase. The skill promotes making decisions that prioritize correctness and clarity, ensuring that data flow and control flow are obvious to both human and AI readers. By following these principles, developers can create Python code that is not only effective but also easy to understand and maintain over time.

This skill is particularly useful for developers working on large codebases or those who need to refactor existing code to improve its readability and maintainability. It serves as a valuable resource for both novice and experienced Python programmers who want to enhance their coding practices and adhere to Python's idiomatic standards. By leveraging the Pythonic Code skill, users can ensure that their code is not only functional but also aligns with best practices in Python programming, making it easier for others to collaborate and contribute to the project.

When to use it

Use this skill when creating or modifying Python code that requires clarity and maintainability, especially in complex projects.

When not to use it

This skill may not be suitable for simple scripts or projects where performance is the primary concern over code clarity.

What you can build with it

Refactoring a Legacy Codebase

Use this skill to simplify and clarify a legacy Python codebase, making it more maintainable and easier to understand.

Creating New Python Modules

When developing new modules, apply the principles of Pythonic Code to ensure your design is clear and idiomatic.

Evaluating Code for Pythonic Practices

Utilize this skill to review existing code and ensure it adheres to Python's best practices, improving overall code quality.

How to install Pythonic Code

View source

1. Install with the skills CLI

npx skills add basicmachines-co/basic-memory/pythonic-code --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by basicmachines-co

Pythonic Code

Write Python that makes domain behavior obvious to human and AI readers. Apply a WWGD lens: choose the simplest correct design that feels native to Python and is easy to verify.

The preferred design method is Constructive Domain Modeling: define the valid values and outcomes a program can construct, then let their types carry obligations to the code that consumes them.

Orient Before Coding

  1. Read the repository's AGENTS.md or CLAUDE.md instructions.
  2. Read docs/ENGINEERING_STYLE.md when present.
  3. Read docs/DOMAIN_MODEL.md when the change touches domain language, ownership, identity, source-of-truth rules, or lifecycle behavior.
  4. Read each target file completely before editing it.
  5. Identify the supported Python version, configured tools, surrounding patterns, and behavior that must remain stable.

Let local project rules override generic style advice.

Make Decisions In This Order

  1. Preserve correctness, domain invariants, and public behavior.
  2. Respect repository conventions and compatibility constraints.
  3. Make data flow, control flow, errors, and side effects obvious.
  4. Choose the smallest abstraction that reduces cognitive load now.
  5. Use Python idioms when they clarify intent rather than merely shorten code.
  6. Prove the result with types, tests, and repository tooling.

Model The Positive Space

Constructive Domain Modeling describes what the program supports instead of starting with a broad representation and a growing list of invalid combinations.

  • Represent one valid state with a product of required fields, usually a frozen dataclass.
  • Represent meaningful alternatives with a closed union using a Python 3.12 type alias.
  • Use Pydantic models and discriminated unions at API, CLI, MCP, configuration, and persistence boundaries where untrusted values require runtime validation or serialization.
  • Parse or classify a broad boundary shape once, then pass the narrower domain value internally. Do not make every consumer rediscover the invariant through checks and casts.
  • Consume a closed union with explicit match cases. Use typing.assert_never when it proves exhaustive handling, and avoid catch-all cases that hide a newly added variant.
  • Prefer a total function over a partial one. When a case is expected, either narrow the input so the case is impossible or widen the return union so the caller must handle it.
  • Return explicit variants for recoverable domain outcomes when callers can respond differently. Keep exceptions for broken invariants, cancellation, and unpredictable filesystem, network, queue, or database failures.
  • Choose the simplest model that rules out a real error. Do not add wrapper-only IDs, Result types around every operation, or maximum-precision unions that cost more than they clarify.

Before narrowing an ORM model or compatibility schema, trace its writers and serialized forms. Storage may remain broad while a parser constructs a safer domain value for the core workflow.

Prefer Functions Before Hierarchies

  • Start with an ordinary, fully typed function.
  • Pair functions with a dataclass when related state or an operation result needs a name.
  • Use callbacks, closures, or functools.partial when binding behavior is clearer than creating another object.
  • Use functools.singledispatch only when behavior genuinely varies by the first argument's runtime type and open registration is an intentional extension point.
  • Use a narrow Protocol for genuine replaceable behavior. Do not use property-only protocols to describe internal result data; return a concrete frozen dataclass unless callers truly require structural interoperability.
  • Use a concrete class when identity, cohesive mutable state, lifecycle, or resource ownership requires one.
  • Use an abstract base class only when runtime-enforced subclassing or shared skeletal behavior is part of the current design.

Do not replace one class hierarchy with clever functional machinery. Prefer the form with the fewest concepts, hidden rules, and call hops.

Keep Reasoning Local

  • Keep a straightforward workflow together when top-to-bottom reading is clearest.
  • Extract a helper only when its name captures a domain operation, it isolates a side effect or constraint, it removes meaningful duplication, or it forms a cohesive testable computation.
  • Do not extract helpers merely to shorten a function.
  • Treat a class dominated by private methods as a signal that behavior may belong in explicit module-level functions operating on typed values.
  • Treat long chains of _prepare_*, _resolve_*, _apply_*, and _build_* calls as a prompt to reconsider the data flow or name one meaningful phase object.
  • Avoid manager, factory, base, adapter, strategy, and registry abstractions with only one real implementation.
  • Avoid dynamic registration, metaprogramming, and decorator-driven control flow unless the product currently needs that extension mechanism.

If extracting a helper makes the reader navigate more but understand no less, keep the logic local.

Write Explicit Python

  • Name values after the domain concept they carry.
  • Use full annotations and narrow types. Do not hide uncertainty with Any, broad casts, speculative getattr, or unstructured dictionaries.
  • Use frozen dataclasses for internal domain values and Pydantic at validation and serialization boundaries. A Pydantic model is not automatically the best internal state representation.
  • Prefer direct iteration, context managers, standard-library building blocks, and simple comprehensions where their meaning is immediate.
  • Distinguish absence from falsiness; use truth-value testing only when empty values share the intended meaning.
  • Keep async work, resource ownership, cancellation, and cleanup visible.
  • Fail fast with specific errors when an invariant or external operation fails. Do not use exceptions for ordinary domain branching, or add silent fallbacks and broad exception handling.
  • Comment decisions and constraints, not mechanics.
  • Optimize measured hot paths; do not trade readability for hypothetical performance.

Match The Requested Mode

Write

Establish the valid states, outcomes, and boundary parser first. Implement the direct path, make closed variants exhaustive, then add only the abstractions required by real variation, state, or boundaries.

Refactor

Preserve observable behavior, keep the diff focused, and add or update a regression test when the behavior is risky. Look for status strings coupled to optional fields, repeated validation, "should never happen" branches, and expected outcomes carried by exceptions. Replace them only when a smaller constructive model removes a real unsupported state. Do not mechanically rewrite already-clear code, convert I/O failures to Result types, or reshape persisted data before tracing its writers.

Review

Report concrete readability, abstraction, typing, lifecycle, and domain-model risks. Explain the smallest practical improvement. Ask which invalid state or unhandled obligation a proposed type actually removes; stronger-looking types without a concrete payoff are not an improvement. Do not edit unless the user asks for fixes.

Verify The Result

Run the narrowest command that proves the change, then widen according to risk:

  1. Focused tests for the changed behavior.
  2. Formatter, linter, and type checker configured by the project. Use the type checker to prove exhaustive consumers where the domain is a closed union.
  3. Repository health, package, integration, or full gates when boundaries are affected.

Lead the final response with the outcome and verification. Explain design choices only when they are non-obvious or materially affect future work.

Frequently asked questions about Pythonic Code

Similar skills