协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
kiss-principle【开发与工程】Detects and prevents over-engineering by enforcing the KISS principle (Keep It Simple, Stupid) through code simplification, abstraction reduction, and preference for straightforward solutions.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: kiss-principle description: Detects and prevents over-engineering by enforcing the KISS principle (Keep It Simple, Stupid) through code simplification, abstraction reduction, and preference for straightforward solutions. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: kiss principle, keep it simple stupid, over-engineering, simplicity, avoid complexity, unnecessary abstraction, how do i simplify my code, reduce boilerplate role: implementation scope: implementation output-format: code content-types: [code, guidance, do-dont, examples] related-skills: yagni, dry-principles, single-responsibility, open-closed-principle, abstraction-design-patterns archetypes: - tactical - diagnostic anti_triggers: - brainstorming - vague ideation response_profile: verbosity: low directive_strength: high abstraction_level: operational
Enforces simplicity by detecting over-engineering, reducing unnecessary abstractions, and preferring straightforward solutions. When reviewing or writing code, the model evaluates whether complexity is justified by actual requirements rather than speculative needs, and refactors toward simpler alternatives that preserve correctness and readability.
if/else chains over state machines when cases ≤ 3Use this skill when:
Avoid this skill for:
open-closed-principle instead)Measure Actual vs Speculated Complexity — Count the number of concrete implementations for each abstraction. If an interface has exactly one implementation and no evidence of a second imminent, flag it for removal. Checkpoint: Verify that removing the abstraction would not break existing polymorphic call sites.
Trace the Indirection Depth — Follow a single logical operation through the codebase counting layer hops: entry point → controller → service → repository → database. If more than 3 layers exist for an operation under 50 lines of actual logic, each intermediate layer must justify its existence with unique behavior beyond pass-through. Checkpoint: Each intermediate layer should have ≥ 2 meaningful lines of business logic, not just delegation.
Apply the Rule of Three — Before creating a generic abstraction (function, class, module), identify three distinct use cases that share the same structure. Two similar use cases should be handled by copying and slight modification; only at three do you factor out commonality. Checkpoint: The proposed abstraction must serve ≥ 3 callers with genuinely different parameterizations, not just 2 nearly identical ones.
Prefer Explicit Over Implicit — Replace magic configuration, metaprogramming, reflection-based dispatch, and dynamic method generation with explicit control flow (if/elif/else, match/case, dispatch tables). Readable branching beats invisible routing. Checkpoint: Every implicit behavior must have a corresponding explicit alternative that is equally short.
Evaluate Built-in First — Before writing custom sorting, parsing, caching, retry logic, or error handling, check if the standard library or a well-tested third-party package already provides it. std::sort, collections.defaultdict, functools.lru_cache beat hand-rolled alternatives every time. Checkpoint: The built-in solution must cover ≥ 90% of use cases without workarounds.
Simplify Refactor — When presenting a simpler alternative, show the original code and the simplified version side by side with specific lines removed or merged. Do not just describe the change — demonstrate it. Checkpoint: The refactored version must have ≤ 75% of the original line count while preserving all behavior.
# ❌ BAD — interface with one implementation, zero polymorphic usage
class PaymentProcessorInterface(Protocol):
def process(self, amount: float, currency: str) -> TransactionResult: ...
class StripePaymentProcessor(PaymentProcessorInterface):
def process(self, amount: float, currency: str) -> TransactionResult:
return stripe.charges.create(amount=amount, currency=currency)
# Another module just wraps it further — double indirection
class PaymentFacade:
def __init__(self) -> None:
self._processor: PaymentProcessorInterface = StripePaymentProcessor()
def charge(self, amount: float, currency: str) -> TransactionResult:
return self._processor.process(amount, currency) # pass-through
# ✅ GOOD — direct function, no unnecessary abstraction layers
def process_stripe_payment(
amount: float,
currency: str,
*,
api_key: str | None = None,
) -> TransactionResult:
"""Process a payment via Stripe.
Simple function that does one thing. If a second provider is needed later,
an abstraction can be introduced with three callers already in place.
"""
return stripe.charges.create(
amount=amount,
currency=currency,
api_key=api_key or os.environ["STRIPE_KEY"],
)
# Usage — explicit, no indirection:
result = process_stripe_payment(100.0, "usd")
# ❌ BAD — factory hierarchy for two concrete types
class BaseHandler(ABC):
@abstractmethod
def handle(self, request: Request) -> Response: ...
class LoginHandler(BaseHandler):
def handle(self, request: Request) -> Response:
return _authenticate(request.body["user"], request.body["pass"])
class LogoutHandler(BaseHandler):
def handle(self, request: Request) -> Response:
return _invalidate_session(request.cookies["session_id"])
class HandlerFactory(ABC):
@abstractmethod
def create(self, handler_type: str) -> BaseHandler: ...
class ConcreteHandlerFactory(HandlerFactory):
def create(self, handler_type: str) -> BaseHandler:
if handler_type == "login":
return LoginHandler()
if handler_type == "logout":
return LogoutHandler()
raise ValueError(f"Unknown handler: {handler_type}")
# ✅ GOOD — simple dispatch table or direct routing
HANDLERS: dict[str, Callable[[Request], Response]] = {
"login": _handle_login,
"logout": _handle_logout,
}
def route_request(handler_key: str, request: Request) -> Response:
handler = HANDLERS.get(handler_key)
if handler is None:
raise ValueError(f"Unknown handler: {handler_key}")
return handler(request)
def _handle_login(request: Request) -> Response:
return _authenticate(request.body["user"], request.body["pass"])
def _handle_logout(request: Request) -> Response:
return _invalidate_session(request.cookies["session_id"])
# ❌ BAD — deep nesting, hard to follow control flow
def calculate_discount(order: Order) -> float:
total = order.total()
if order.user.is_premium:
if total > 100:
if order.items[0].category != "electronics":
return total * 0.20
else:
return total * 0.10
else:
return total * 0.05
elif total > 50:
return total * 0.05
else:
return 0.0
# ✅ GOOD — guard clauses, single level of nesting via early returns
def calculate_discount(order: Order) -> float:
"""Calculate discount based on membership and cart value."""
if not order.user.is_premium and order.total() <= 50:
return 0.0
total = order.total()
base_rate = 0.05 if total > 50 else 0.10
if order.user.is_premium and total > 100:
electronics_first = order.items[0].category == "electronics"
return total * (0.20 if not electronics_first else 0.10)
return total * base_rate
import os
from pathlib import Path
import json
from typing import Any
# ❌ BAD — custom JSON file I/O wrapper for what stdlib handles directly
class FileCache:
def __init__(self, path: str) -> None:
self._path = path
def read(self) -> dict[str, Any]:
if not os.path.exists(self._path):
return {}
with open(self._path, "r") as f:
return json.load(f)
def write(self, data: dict[str, Any]) -> None:
with open(self._path, "w") as f:
json.dump(data, f, indent=2)
# ✅ GOOD — use pathlib + json directly; if wrapping is needed, do it minimally
def read_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text()) if path.exists() else {}
def write_json(path: Path, data: dict[str, Any]) -> None:
path.write_text(json.dumps(data, indent=2))
# Usage — clear and direct:
cache_file = Path("/tmp/config.json")
config = read_json(cache_file) or {}
write_json(cache_file, {"key": "value"})
| Anti-Pattern | What It Looks Like | Simple Fix |
|---|---|---|
| Premature Genericity | class Manager<T, U, V> with no evidence all 3 type params will ever vary independently | Remove generic params one at a time until compilation still passes |
| Facade Anti-Pattern | Service → Repository → DAO → Adapter — four layers doing pass-through | Collapse to two: Service → Database (or whichever layer has real logic) |
| Callback Hell | Nested .then().then().then() or deeply nested callbacks | Use async/await, top-level promises, or a flat chain |
| Configuration Overload | 20+ config flags for a feature used at one setting | Bake defaults that match the common case; expose only exceptions |
| Enum Hell | Enums with 15+ values where most are dead code | Split into focused enums or replace with booleans/flags |
Use these quantitative measures to verify KISS compliance:
When applying the KISS principle review or simplification:
| Skill | Purpose |
|---|---|
yagni | Prevents building features nobody needs — KISS's partner in anti-over-engineering |
dry-principles | DRY can conflict with KISS; this skill clarifies when duplication is the simpler choice |
single-responsibility | SRP and KISS reinforce each other when classes do too many things at once |
abstraction-design-patterns | Shows how to design abstractions that stay simple rather than growing complex |
open-closed-principle | OCP sometimes requires more abstraction; KISS helps decide when it's worth it |