rust-occams-razor
Use when refactoring, simplifying, reviewing, or decomposing an existing Rust codebase while preserving observable behavior, public contracts, concurrency semantics, and performance characteristics.
- License 许可
- MIT
- 所属仓库
- rootazero/Aleph
- 最近更新
- 2026-11-12
- Stars
- ★ 4
- Forks
- 1
01 前言 Front Matter
- name
- rust-occams-razor
- description
- Use when refactoring, simplifying, reviewing, or decomposing an existing Rust codebase while preserving observable behavior, public contracts, concurrency semantics, and performance characteristics.
02 资源文件 2
- 参考文档SOURCE.md185 B
- 参考文档TRUST.auto.yaml2.2 KB
03 SKILL.md 正文
Simplifying Rust with Occam's Razor
Overview
Treat simplicity as the removal of unnecessary concepts, not the reduction of line count. Prefer the smallest design that preserves behavior, invariants, ownership clarity, concurrency semantics, and measured performance.
Do not refactor merely because a construct can be made shorter. Every abstraction, split, merge, iterator chain, macro, generic, allocation, and indirection must earn its existence.
Non-Negotiable Invariants
Before editing, identify the contracts that must remain stable:
- Public API surface, unless the task explicitly permits breaking changes.
- Error semantics: variants, conversion boundaries, propagation behavior, and externally observable messages where relied upon.
- Ownership/lifetime behavior that callers depend on.
Send/Syncproperties and async cancellation behavior.- Lock acquisition/release ordering and critical-section boundaries.
- Ordering, timing-sensitive behavior, retry semantics, side effects, I/O, persistence, and protocol behavior.
- Feature-gated and target-specific behavior.
- Serialization/wire/storage formats and compatibility.
- Performance-sensitive allocation and dispatch characteristics unless benchmarks justify a change.
When uncertain whether behavior is observable, preserve it.
Evidence Before Deletion
Never equate “no obvious reference” with dead code. Before deleting code, check relevant evidence such as:
cargo check --all-targets --all-features
cargo test --all-targets --all-features
cargo clippy --all-targets --all-features -- -D warnings
Also inspect, where applicable:
- integration tests, examples, benches, build scripts and proc macros;
cfg/feature/platform-specific references;- exported APIs used by downstream crates;
- dynamic registration, serialization names, FFI, plugin hooks or generated code.
Delete only when evidence is sufficient for the repository context.
Decision Hierarchy
Apply changes in this order:
- Remove proven dead code and stale dependencies/imports.
- Remove accidental complexity and unnecessary indirection.
- Eliminate unnecessary allocations, clones, conversions and owned parameters.
- Flatten control flow and make invariants explicit.
- Consolidate genuine duplication.
- Split modules only where responsibilities or change boundaries justify it.
- Introduce abstractions only when they reduce total conceptual complexity.
Prefer a clear local duplication over a premature abstraction.
Idiomatic Rust Without Dogma
Ownership and allocation
Prefer borrowed inputs when ownership is unnecessary:
&strover&String.&[T]over&Vec<T>.- references or moves over gratuitous
.clone()/.to_owned(). - concrete stack values over
Box<T>when indirection is unnecessary.
Do not change an owned public parameter to a borrowed one merely for style; first confirm API compatibility and caller semantics.
Treat “zero allocation” as a constraint only on relevant paths. Do not trade obvious, maintainable code for speculative micro-optimization. Benchmark hot paths when the trade-off is material.
Control flow
Prefer ?, let ... else, if let, early returns, and focused helper functions when they reduce nesting and make the happy path obvious.
Do not mechanically replace match: keep it when exhaustiveness, multiple meaningful branches, or state-machine structure is clearer.
Iterators vs loops
Do not mechanically convert for loops into iterator chains. Use iterators when they make transformation intent clearer; use loops when mutation, early exits, stateful control flow, debugging, or performance is clearer.
Prefer readability over “functional-looking” density. Avoid long iterator pipelines that hide state transitions or error semantics.
Functions and inlining
Do not inline solely because a helper is called once or is 1–2 lines long. Inline when the helper has no useful name, invariant, reuse boundary, test seam, or abstraction value and locality improves.
Keep a tiny function when its name explains intent or isolates an invariant.
Compiler inlining is a separate performance decision; source-level inlining does not guarantee faster code.
DRY, generics and macros
Merge code only when duplicated blocks represent the same concept and are expected to evolve together.
Prefer, in order:
- a normal function;
- a small generic function when type variation is real;
- a trait when polymorphic behavior is part of the domain boundary;
- a macro only when syntax-level repetition cannot be expressed cleanly otherwise.
Never introduce a macro merely to reduce line count. Avoid generic abstractions that increase trait bounds, monomorphization, compile time, diagnostics complexity, or cognitive load without a clear payoff.
Dynamic dispatch
Do not introduce Box<dyn Trait>, Arc<dyn Trait>, or other dynamic dispatch merely to unify implementations. Use it only when runtime polymorphism is genuinely required and the cost/ownership model is appropriate.
Large Files and Module Boundaries
Treat 500 lines as a review signal, not an automatic split threshold. Split when a file contains multiple independently understandable responsibilities, changes for unrelated reasons, or forces excessive navigation.
Good extraction boundaries include:
- domain types and invariants;
- parsing/encoding/serialization;
- conversions (
From/TryFrom) when substantial; - external adapters such as database, filesystem, network or platform integration;
- orchestration/state-machine logic;
- implementation-specific helpers.
Prefer modern Rust module layouts (foo.rs plus foo/bar.rs) where practical; use mod.rs only when it fits the repository's established convention.
Keep visibility minimal: private by default, then pub(super) / pub(crate) when justified, and pub only for intended external contracts.
Do not create one-file-per-type fragmentation. A module boundary must reduce conceptual coupling, not merely move lines around.
Refactoring Workflow
1. Establish the baseline
Read repository instructions and determine workspace structure, MSRV/toolchain, features, targets, generated code, CI commands and public crates.
Run the repository's existing verification commands before changing code. Record pre-existing failures instead of silently attributing them to the refactor.
For behavior-preserving refactors, add or strengthen characterization/regression tests when important behavior is not already protected. Do not invent new behavior.
2. Diagnose
Build an evidence-backed inventory of:
- dead or unreachable code;
- unnecessary allocations/clones/conversions;
- duplicate logic;
- deeply nested control flow;
- oversized functions/modules;
- unclear ownership or lifetime boundaries;
- unnecessary traits/generics/macros/dynamic dispatch;
- Clippy/compiler warnings;
- suspicious lock scope or async blocking;
- feature/target-specific paths at risk.
Separate “must fix” from “possible simplification”. Do not rewrite healthy code for stylistic uniformity alone.
3. Plan the smallest safe slices
Order changes so each step is independently reviewable and verifiable. Prefer one semantic concern per patch.
For module decomposition, state the proposed responsibility of each new module and its visibility boundary before moving code.
4. Refactor incrementally
After each meaningful slice:
- format the touched code;
- compile/check the smallest relevant scope;
- run focused tests;
- run relevant Clippy checks;
- inspect the diff for accidental API or semantic changes.
Do not combine broad renaming, file movement, behavior changes, dependency upgrades, and optimization into one refactor unless required.
5. Verify globally
Before claiming completion, run the strongest applicable repository checks, typically including formatting, all relevant feature/target checks, tests, Clippy, docs/tests, and project-specific CI commands.
For concurrency-sensitive changes, explicitly compare lock order, lock lifetime, task spawning, cancellation points, channel behavior and ordering guarantees.
For performance-sensitive changes, compare allocation/benchmark evidence where available. Do not claim a speedup from aesthetics or fewer source lines.
Required Output During Execution
Keep reporting concise and evidence-based. Communicate:
- Baseline: commands run and any pre-existing failures.
- Findings: concrete complexity/debt with file/symbol references.
- Plan: minimal ordered refactoring slices and module boundaries.
- Changes: what was removed, simplified, merged, retained, or deliberately not abstracted.
- Verification: exact commands run and their outcomes.
- Semantic preservation: relevant API, error, concurrency, ordering, serialization and allocation properties checked.
- Residual risk: anything not verified because of environment, platform, unavailable dependency, or missing tests.
Never state that behavior is unchanged merely because the project compiles.
Anti-Patterns
Reject these shortcuts:
- “Unused according to search, therefore delete it.”
- “Iterator chains are always more idiomatic than loops.”
- “One call site means inline.”
- “Duplicate-looking code means DRY it.”
- “A macro is simpler because it removes lines.”
- “Over 500 lines means split immediately.”
- “Fewer files/functions/types means a simpler design.”
- “
clone()is always bad.” - “
Boxor dynamic dispatch is always bad.” - “Compilation proves semantic equivalence.”
- “Clippy is the design authority.”
Completion Standard
A successful refactor leaves the codebase with fewer unnecessary concepts, not merely fewer lines. The result should be easier to reason about locally, preserve established contracts, avoid unjustified runtime costs, and pass the strongest available verification suite.
04 同仓库更多 Skills
invalid-yaml
暂无简介
gh-pr-docker
GitHub PR operations (Aleph enhanced with Docker)
echo-basic
Basic echo command (frontmatter smoke fixture)
review-results
暂无简介
review-results
暂无简介
severed-wire-audit
Find and resolve "severed wires" in a codebase — a producer and a consumer both exist and are fully implemented, but the connecting registration / dispatch arm / event subscription / call site between them is missing, so a feature looks broken though both ends are done. dead-code lints CANNOT catch this: the producer isn't dead (it has tests) and the consumer connects to a stub or reads an inert config. Use when a feature "looks unwired / 没接上 / 两端都在但连线断 / 明明写了却调不到"; after a refactor or middleware removal, to verify producers still reach consumers; when asked to "审接缝 / 找断线 / seam audit / wire audit / connection audit"; or when a method returns NOT_FOUND, a tool the model can't call, an event that never fires, or a config field nothing reads. Triages each finding into CONNECT (fix the wire) / CUT (delete the dead abstraction) / DECIDE (ask the human), enforces read-before-write so dead scaffolding is deleted not reconnected, and installs a grep-diff guard against regression.