From grimoire
Designs software in small, focused units with minimal surface areas that can be combined freely without knowing each other's internals. Applicable when units are hard to reuse independently or replacements require touching many files.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-composable-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Design software in small, focused units with minimal surface areas that can be combined freely without knowing each other's internals.
Design software in small, focused units with minimal surface areas that can be combined freely without knowing each other's internals.
Adopted by: Unix (foundational design philosophy); Go standard library (small interfaces, single-method where possible); AWS Lambda / serverless ecosystem (each function does one thing); HashiCorp tools (each binary is self-contained and composable via APIs). The composability property is a prerequisite of microservices, serverless, and plugin architectures — all dominant patterns in modern software. Impact: Composable units are independently replaceable. When a unit can be swapped without touching its consumers, teams can evolve components at different speeds, conduct A/B experiments at the module level, and limit blast radius of failures. Google's Site Reliability Engineering documentation identifies tight coupling as the primary cause of cascading failures — composable units contain those failures at boundaries. Why best: Non-composable software grows through accretion — every new feature reaches into existing units, widening surface areas and deepening coupling. The only escape is a rewrite. Composable design prevents accretion: each unit's surface area is a contract; internals change freely as long as the contract holds. This is distinct from the OOP "composition over inheritance" pattern — composability is a property of any unit (functions, services, CLIs, modules) not just classes.
Sources: Dan North, "CUPID—for joyful coding" (2022); Eric Raymond, "The Art of Unix Programming" (2003); Google, "Site Reliability Engineering" (O'Reilly, 2016)
Status: Emerging — gaining adoption since Dan North's 2022 publication; not yet majority-adopted across all engineering organizations.
Every exported function, method, or endpoint is a commitment. Each one must be maintained, versioned, and explained.
// Bad — many entry points, callers must know which to call
func SaveUser(u User) error { ... }
func SaveUserWithAudit(u User, by string) error { ... }
func SaveUserToShardDB(u User, shard int) error { ... }
// Good — one entry point, options injected
type UserStore interface {
Save(ctx context.Context, u User) error
}
Hidden dependencies make composition impossible — you can't combine units when their needs are invisible.
time.Now() inside# Bad — hidden dependency on time; impossible to test or compose predictably
def is_expired(token):
return token.expiry < datetime.now() # grabs global time
# Good — time is explicit; callers control it
def is_expired(token, now):
return token.expiry < now
A composable unit works standalone — it does not require its siblings to be present.
Test: can you import and use this module in a blank project with no other modules from this codebase?
// Bad — UserService requires OrderService requires UserService (circular)
class UserService {
constructor(private orders: OrderService) {}
getProfile(id: string) {
return { user: this.findUser(id), orders: this.orders.forUser(id) }
}
}
// Good — profile assembly is a separate unit; each service works alone
class UserService { findUser(id: string): User { ... } }
class OrderService { forUser(id: string): Order[] { ... } }
class ProfileService {
constructor(private users: UserService, private orders: OrderService) {}
getProfile(id: string) { ... }
}
Composable units know each other's contracts, not internals.
// Bad — consumer hardwired to concrete type
func ProcessReport(r *CSVReader) error { ... }
// Good — consumer defines what it needs; any reader works
type LineReader interface {
ReadLines() ([]string, error)
}
func ProcessReport(r LineReader) error { ... }
If you cannot describe a unit's purpose in one sentence without using "and", it is not composable — it is two units fused together.
Split at the "and": each half becomes its own composable unit.
Confusing composable with composed. A class that uses composition internally (has-a relationships) is not necessarily composable from the outside. Composability is an external property — can consumers combine this unit freely? Internal structure is separate.
Micro-splitting to absurdity. Composability does not mean every function stands alone. Units that always appear together and have no independent use case should stay together. Split only when independent reuse exists or is imminent.
Wide interfaces. An interface with 8 methods is not composable — consumers must implement or mock all 8 to use it. Narrow interfaces (1–3 methods) are more composable.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireApplies the Unix philosophy (single responsibility, clean interfaces) when designing tools, services, CLIs, or modules. Helps refactor coupled components and improve testability.
Provides shared vocabulary and principles for designing deep modules with small interfaces and rich implementations, improving testability and AI-navigability.
Applies Python design principles like KISS, SRP, composition over inheritance, and Rule of Three for designing services, refactoring monoliths, reducing coupling, and improving testability.