| name | code:common |
| description | Cross-language coding best practices, design patterns, and principles for Ruby, Rust, Python, and TypeScript.
<example>
Context: User asks about general code quality
user: "what's the best way to structure error handling"
</example>
<example>
Context: User wants design pattern guidance
user: "should I use inheritance or composition here"
</example>
<example>
Context: User asks about naming or style
user: "how should I organize this module"
</example>
|
Tools Reference
Built-in Tools
| Tool | Purpose |
|---|
Read | Read source files for analysis |
Write | Create new source files |
Edit | Modify existing code |
Glob | Find source files by pattern |
Grep | Search code for patterns |
Bash | Run linters, formatters, tests |
Related Skills
- Language-specific:
marauder:code:ruby, marauder:code:rust, marauder:code:python, marauder:code:typescript
- Testing:
marauder:code:*-test skills
- Tooling:
marauder:code:*-tooling skills
Common Coding Practices
Cross-language patterns that improve readability and maintainability.
Guard Clauses & Early Returns
Exit early. Keep the happy path unindented.
def process(user)
return unless user
return if user.banned?
raise ArgumentError, "No email" unless user.email
send_notification(user)
end
def process(user)
if user
unless user.banned?
if user.email
send_notification(user)
end
end
end
end
Why: The first reads top-to-bottom. The second requires mental stack management.
Test File Mirroring
Test files mirror source structure exactly:
# Source # Test
lib/something/something_else.rb → spec/something/something_else_spec.rb
src/users/service.py → tests/users/test_service.py
src/orders/validator.ts → src/orders/validator.test.ts
src/parser/mod.rs → src/parser/mod.rs (inline #[cfg(test)])
Why: One-to-one mapping = instant navigation. No hunting.
Directory Structure Over Flat Files
Categorize by domain, not by type:
# Yes - domain-driven
src/
users/
models.py
services.py
orders/
models.py
services.py
# No - flat soup
src/
user_models.py
user_services.py
order_models.py
Why: Domains scale independently. Flat files become unmanageable.
Parse, Don't Validate
Make invalid states unrepresentable:
fn send_email(to: String) { ... }
struct Email(String);
impl Email {
fn parse(s: &str) -> Result<Self, EmailError> { ... }
}
fn send_email(to: Email) { ... }
Applies to all languages:
- Ruby: Value objects with validation in initializer
- Python: Pydantic models or
@dataclass with __post_init__
- TypeScript: Branded types or Zod schemas
- Rust: Newtypes with
TryFrom
Error Handling at Boundaries
Handle errors where they enter your system:
┌─────────────────────────────────────────────┐
│ Your Application │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Service │→ │ Domain │→ │ Service │ │
│ └────┬────┘ └─────────┘ └────┬────┘ │
│ │ VALIDATE HERE │ HANDLE │
└───────┼─────────────────────────┼──────────┘
External External
Why: Core domain logic stays clean. Boundaries handle the messy real world.
Prefer Explicit Over Magic
@auto_inject
@cache(ttl=300)
@retry(3)
def process(data): ...
def process(
data: InputData,
cache: Cache,
logger: Logger,
max_retries: int = 3,
) -> Result: ...
When magic is okay: Framework conventions (Rails, Django) where everyone knows the patterns.
Composition Over Inheritance
Favor mixins and delegation over deep class hierarchies:
class Dog
include Walkable
include Barkable
end
class Animal; end
class Mammal < Animal; end
class Canine < Mammal; end
class Dog < Canine; end
Why: Inheritance is rigid. Composition is flexible.
The 7±2 Rule
Cognitive psychology tells us humans can hold 7±2 items in working memory.
- If understanding a function requires tracking more than 7 things, it's too complex
- This is independent of line count—a 50-line function can be simple, a 10-line function complex
Line Limits: Modern View
| Element | Classic | Modern | Guidance |
|---|
| Method/Function | 10 lines | No hard limit | Single responsibility |
| Class/Module | 100 lines | No hard limit | Single reason to change |
| File | 200-300 lines | No hard limit | One concept per file |
| Line width | 80 chars | 80-120 chars | Don't wrap mid-expression |
Naming is Documentation
Names should reveal intent without requiring comments:
def process(d):
return [x for x in d if x > 0]
def filter_positive_values(numbers: list[int]) -> list[int]:
return [n for n in numbers if n > 0]
Comments Explain Why, Not What
counter += 1
sleep(1)
Summary
| Practice | Why |
|---|
| Guard clauses | Reduces nesting, reads top-to-bottom |
| Test mirroring | Instant navigation |
| Directory structure | Scales with domain complexity |
| Parse don't validate | Invalid states unrepresentable |
| Boundary handling | Clean core, messy edges |
| Explicit over magic | Maintainability wins |
| Composition | Flexible over rigid |
| 7±2 rule | Respect cognitive limits |