Developer Style Guide

This guide defines coding conventions for contributing to tik.maya and TikWorks. Following these patterns keeps the codebase consistent, predictable, and Pythonic.

Philosophy

tik.maya’s API should feel like Python but behave like Maya.

  • APIs read naturally in Python

  • Scene state remains the source of truth

  • Semantic intent is explicit, not inferred

  • Every abstraction has a clear responsibility

When designing an API:

  • Think about what makes sense to the user, not just the implementer

  • Prefer explicit intent over implicit magic

  • Consistency across modules is more important than individual taste

Properties vs. Methods

Use properties for state and methods for actions. Be explicit when something performs work or has side effects.

Use @property for:

  • Data-like access to state or computed values

  • Simple queries with no side effects

  • Booleans, numbers, or small objects representing current state

@property
def locked(self) -> bool:
    """Whether the attribute is locked."""
    return cmds.getAttr(f"{self.path}.lock")

@locked.setter
def locked(self, state: bool):
    cmds.setAttr(f"{self.path}.lock", state)

# Usage
if not attr.locked:
    attr.locked = True

Use methods for:

  • Operations or actions (verbs)

  • Functions that take parameters

  • Anything that performs significant work

def lock(self):
    """Lock the attribute."""
    self.locked = True

def unlock(self):
    """Unlock the attribute."""
    self.locked = False

Avoid:

  • Properties that perform heavy operations or cause side effects

  • Mixing boolean state and action in one name (e.g., lock as both noun and verb)

Naming Conventions

  • Use noun-like names for properties: visible, matrix, locked

  • Use verb-like names for methods: lock(), connect(), freeze()

  • Avoid get_ and set_ prefixes - they are unnecessary in Pythonic APIs

  • Keep names short and consistent across modules

# Good
@property
def visible(self): ...

def show(self): ...
def hide(self): ...

# Avoid
def get_visibility(self): ...
def set_visibility(self, state): ...

Class Structure

Classes should follow a consistent structure that reads top-down like a story.

Recommended order:

  1. Docstring and class variables

  2. __init__ and lifecycle methods

  3. Public properties (with setters)

  4. Primary public methods (main user-facing verbs)

  5. Secondary / helper methods

  6. Private utilities (_-prefixed)

  7. Static / class methods

Keep related properties and methods grouped together - not alphabetically.

class MayaAttribute:
    """Represents a Maya attribute."""

    # === Init / lifecycle ===
    def __init__(self, node, name):
        self.node = node
        self.name = name

    # === Properties ===
    @property
    def locked(self):
        return cmds.getAttr(f"{self.path}.lock")

    @locked.setter
    def locked(self, state):
        cmds.setAttr(f"{self.path}.lock", state)

    @property
    def value(self):
        return cmds.getAttr(self.path)

    @value.setter
    def value(self, val):
        cmds.setAttr(self.path, val)

    # === Public methods ===
    def lock(self):
        self.locked = True

    def unlock(self):
        self.locked = False

    def connect(self, target):
        cmds.connectAttr(self.path, target.path, f=True)

    # === Private helpers ===
    def _exists(self):
        return cmds.objExists(self.path)

Section Headers

For large classes, group related parts visually with section headers:

# === Properties ===
# === Public Methods ===
# === Private Helpers ===

These are purely cosmetic but help navigation in editors.

Docstrings

Follow PEP 257. Every public class, method, and property should have a docstring.

def snap_to(self, target, position=True, rotation=True, scale=False):
    """Snap this transform to another transform's position, rotation, and/or scale.

    Args:
        target: The transform to snap to (Transform or string name).
        position: Whether to match position. Defaults to True.
        rotation: Whether to match rotation. Defaults to True.
        scale: Whether to match scale. Defaults to False.

    Raises:
        TypeError: If target is not a Transform node.
    """

Type Hints

Use type hints for function signatures. They enable IDE support and documentation.

def connect(self, other: "Plug", force: bool = True) -> None:
    """Connect this plug to another plug."""

@property
def locked(self) -> bool:
    """Check if the attribute is locked."""

Consistency Checklist

Rule

Example

Properties for state

ctrl.locked, mesh.visible

Methods for actions

ctrl.lock(), mesh.freeze()

Group by purpose

Keep lock() near unlock()

Keep properties/setters together

Avoid separating them

Use section headers

For readability in large classes

No get_ / set_ prefixes

Use direct property syntax

Same structure across all classes

Predictability matters

For architectural concepts (Types, Roles, Constructs), see TikWorks Package Structure and Responsibilities.