From house-style
Guides code writing and refactoring with property-driven design, correctness principles, defense-in-depth validation, error handling, and sub-skills for tests and PostgreSQL.
npx claudepluginhub pbdeuchler/llm-plugins --plugin house-styleThis skill uses the workspace's default tool permissions.
**ALWAYS REQUIRED:**
Guides writing and refactoring code via property-driven design, correctness principles, error handling, and sub-skills for TypeScript, PostgreSQL, React, and testing.
Strategies for handling errors: exceptions, error types, recovery strategies, and error propagation.
Enforces pragmatic clean code standards: SRP, DRY, KISS, YAGNI; intent-revealing names, small functions <=20 lines, guard clauses, anti-pattern fixes, pre-edit dependency checks.
Share bugs, ideas, or general feedback.
ALWAYS REQUIRED:
howto-functional-vs-imperative - Separate pure logic from side effectsdefense-in-depth - Validate at every layer data passes throughCONDITIONAL: Use these sub-skills when applicable:
howto-develop-with-postgres - PostgreSQL database codewriting-good-tests - Writing or reviewing testsproperty-based-testing - Tests for serialization, validation, normalization, pure functionsWhen designing features, think about properties upfront. This surfaces design gaps early.
Discovery questions:
| Question | Property Type | Example |
|---|---|---|
| Does it have an inverse operation? | Roundtrip | decode(encode(x)) == x |
| Is applying it twice the same as once? | Idempotence | f(f(x)) == f(x) |
| What quantities are preserved? | Invariants | Length, sum, count unchanged |
| Is order of arguments irrelevant? | Commutativity | f(a, b) == f(b, a) |
| Can operations be regrouped? | Associativity | f(f(a,b), c) == f(a, f(b,c)) |
| Is there a neutral element? | Identity | f(x, 0) == x |
| Is there a reference implementation? | Oracle | new(x) == old(x) |
| Can output be easily verified? | Easy to verify | is_sorted(sort(x)) |
Common design questions these reveal:
Surface these during design, not during debugging.
Model the full error space. No shortcuts.
Don't:
any or equivalent to bypass type checkingTwo-tier model:
Error message format: Lowercase sentence fragments for "failed to {message}".
Good: failed to connect to database: connection refused
Bad: Failed to Connect to Database: Connection Refused
Good: invalid configuration: missing required field 'apiKey'
Bad: Invalid Configuration: Missing Required Field 'apiKey'
Lowercase fragments compose naturally: "operation failed: " + error.message reads correctly.
Always use proper logging, and errors should always be logged via the language/framework appropriate methods.
The rule of three applies to abstraction: Don't abstract until you've seen the pattern three times. Three similar lines of code is better than a premature abstraction.
Name files by what they contain, not by generic categories.
Don't create:
utils.go - Becomes a dumping ground for unrelated functionshelpers.js - Same problemcommon.rs - What isn't common?misc.go - Actively unhelpfulDo create:
formatter.go - String manipulation utilitiescalendar/math.go - Date calculationsapi/src/error_handling.rs - API error utilitiesuser-input.ts - User input validationWhy this matters:
string-formatting.tsimport { formatDate } from './date-arithmetic' is self-documentingWhen you're tempted to create utils.ts: Stop. Ask what the functions have in common. Name the file after that commonality.
unix.go, windows.go, posix.goDon't emulate Unix on Windows or vice versa. Use each platform's native patterns.
Bad: Trying to make Windows paths behave like Unix paths everywhere.
Good: Accept platform differences, handle them explicitly.
// Platform-specific behavior
if (process.platform === "win32") {
// Windows-native approach
} else {
// POSIX approach
}
When platform differences are significant, use separate files:
process/spawn.go // Shared interface and logic
process/unix.go // Unix-specific implementation
process/windows.go // Windows-specific implementation
When behavior differs by platform, document it in comments:
// On Windows, this returns CRLF line endings.
// On Unix, this returns LF line endings.
// Callers should normalize if consistent output is needed.
function readTextFile(path: string): string { ... }
Don't assume Unix behavior works on Windows. Test explicitly:
| Mistake | Reality | Fix |
|---|---|---|
| "Just put it in utils for now" | utils.ts becomes 2000 lines of unrelated code | Name files by purpose from the start |
| "Edge cases are rare" | Edge cases cause production incidents | Handle them. Model the full error space. |
| "We might need this abstraction later" | Premature abstraction is harder to remove than add | Wait for the third use case |
| "It works on my Mac" | It may not work on Windows or Linux | If deploying to a target, test on it |
| "The type system is too strict" | Strictness catches bugs at compile time | Fix the type error, don't bypass it |
Stop and refactor when you see:
utils.go or helpers.rs file growing beyond 100 linesany) to bypass the type system