Before:
def find_git_repo() -> Path:
"""Find the git repository root"""
# No runtime validation that returned Path is actually a git repoAfter:
GitPath = Annotated[Path, Is[lambda p: (p / ".git").exists()]]
@beartype
def find_git_repo() -> GitPath:
"""Find the git repository root with type safety"""
# beartype ensures returned Path has .git directory at runtimeBefore:
def _start_vibing_impl() -> str:
# 250+ lines of nested if/else logic handling different states
if session.is_vibing:
return "Already vibing!"
# Check for uncommitted changes
has_changes = ...
if has_changes:
return "Warning about changes..."
# More branching logic...After:
@dispatch
def start_vibing_from_state(state: IdleState, repo_path: GitPath) -> str:
"""Clean state - start normally"""
@dispatch
def start_vibing_from_state(state: VibingState, repo_path: GitPath) -> str:
"""Already vibing - return status"""
@dispatch
def start_vibing_from_state(state: DirtyState, repo_path: GitPath) -> str:
"""Dirty state - return instructions"""Before:
@dataclass
class VibeSession:
branch_name: str | None = None
is_vibing: bool = False
observer: Observer | None = None
commit_event: Event | None = None
# Multiple nullable fields, unclear valid combinationsAfter:
@dataclass(frozen=True)
class IdleState:
pass
@dataclass(frozen=True)
class VibingState:
branch_name: BranchName
observer: Observer
commit_event: Event
# All fields required when vibing
SessionState = IdleState | VibingState | DirtyState
@dataclass
class VibeSession:
state: SessionState = IdleState()
@beartype
def transition_to(self, new_state: SessionState) -> None:
self.state = new_stateBefore:
def get_current_branch(repo_path: Path) -> str | None:
# Returns raw string, could be anythingAfter:
BranchName = NewType("BranchName", str)
CommitHash = NewType("CommitHash", str)
@beartype
def get_current_branch(repo_path: GitPath) -> BranchName | None:
# Clear semantic meaning, type-checked at runtimeBefore:
def run_command(args: list[str], cwd: Path | None = None) -> tuple[bool, str]:
# Only accepts list of stringsAfter:
@dispatch
def run_command(args: list[str], cwd: Path | None = None) -> CommandResult:
"""List version"""
@dispatch
def run_command(command: str, cwd: Path | None = None) -> CommandResult:
"""String version - splits automatically"""
return run_command(command.split(), cwd)- Catch Bugs Earlier: beartype catches type errors at runtime before they cause issues
- Cleaner Code: plum dispatch eliminates deeply nested if/else chains
- Better Documentation: Types serve as inline documentation
- Safer Refactoring: Type system catches breaking changes
- Explicit States: No more checking multiple boolean flags
- Add dependencies to pyproject.toml ✓
- Gradually add @beartype decorators to existing functions
- Replace complex branching logic with @dispatch
- Convert implicit states to explicit state types
- Add semantic types (NewType) for domain concepts