From nw
Guides usability in functional code design: naming conventions, pipeline-friendly APIs, lifecycle types, error naming, and feature-oriented organization.
npx claudepluginhub nwave-ai/nwave --plugin nwThis skill uses the workspace's default tool permissions.
Make functional code usable. The developer is the user of your design. Apply usability thinking to code organization, naming, and architecture.
Reframe code designs with functional programming principles for agent-assisted development, separating pure functions from effects to boost agent effectiveness and human reviewability. Use for module planning and agent-friendly structures.
Applies clean code decisions for writing or reviewing functions, classes, naming, and non-trivial logic (≥3 lines). Promotes simplicity by removal, short reasoning blocks, and domain examples.
Guides writing and refactoring code via property-driven design, correctness principles, error handling, and sub-skills for TypeScript, PostgreSQL, React, and testing.
Share bugs, ideas, or general feedback.
Make functional code usable. The developer is the user of your design. Apply usability thinking to code organization, naming, and architecture.
Cross-references: fp-domain-modeling | fp-hexagonal-architecture | fp-algebra-driven-design
[STARTER]
The user of software design is the developer, not the end user. When developers struggle, blame the design, not the people. Pressure, team churn, unclear specs, and noisy environments produce unusable designs. Improve the design.
[STARTER]
| Goal | What It Means | How to Assess |
|---|---|---|
| Learnability | How quickly a new developer becomes productive | Timed tasks for unfamiliar developers |
| Efficiency | How fast common tasks are performed | Count unnecessary navigation and decisions |
| Memorability | How easily proficiency returns after time away | What do returning developers re-learn? |
| Error resistance | How many bugs the design induces | Track bug locations; ask "what design change prevents this category?" |
| Satisfaction | How pleasant to work in the codebase | Developer interviews, retrospectives |
[STARTER]
Use domain expert's vocabulary, not mathematical conventions or technical jargon. No OrderFactory, OrderManager, OrderHelper.
rawOrder
|> validateOrderFields
|> enrichWithCustomerData
|> calculateOrderTotal
|> applyDiscountRules
|> generateOrderConfirmation
isActiveCustomer -- not: checkActive, activeP
hasShippingAddress -- not: shippingAddr, addrCheck
canPlaceOrder -- not: orderOk, validateOrderBool
Single-letter names appropriate only in: library internals and generic utilities | lambda parameters in trivial operations | type variables | mathematical domains where convention IS domain language.
Types at different workflow stages get prefixed: UnvalidatedOrder, ValidatedOrder, PricedOrder. Stage immediately visible in any type signature.
Named after what went wrong, scoped to context: ValidationError, PricingError. Pipeline-level: PlaceOrderError (choice type unifying step errors).
[STARTER]
Organize by feature domain rather than technical layer.
Instead of (technical layers):
controllers/
services/
models/
Use (feature domains):
auth/
speakers/
speakers/profile/
order/
When: Developers frequently need to find and modify all code related to a specific feature. Why: When something changes in "call for speakers," it's obvious where to look. Technical layering scatters related code across entire codebase.
[STARTER]
Typical developer workflow (find, navigate, read, write, test) executes hundreds of times per day. Small improvements compound.
Eliminate: Long methods requiring scrolling | unclear method names forcing reading implementations | tests unrelatable to features | inconsistent placement of similar logic.
Naming as navigation: Consistent patterns where suffix/prefix indicates design role. Names precise enough that search yields 3-4 results at most. IDE-friendly casing enables fast jump-to.
Why: Navigability is quasi-ignored with enormous cumulative impact.
[INTERMEDIATE]
Define constrained roles for your codebase. Each role specifies: responsibilities, allowed collaborators, and testing approach.
| Role | Responsibilities | Can Call | Test With |
|---|---|---|---|
| Controller | Delegates operations, renders views | Services, Command Objects | Component tests |
| Command Object | Validates input, delegates writes | Database services, queries | Unit tests |
| Application Service | Orchestrates business logic | Commands, other services | Integration tests |
| Database Query | Read-only data access | Database only | Unit tests |
| View Model | Shapes data for presentation | Nothing | Unit tests |
Why: Makes design "easy to use correctly, hard to use incorrectly." Controller can't write to database, so database bugs can't originate in Controllers.
Rules for design elements:
[INTERMEDIATE]
Deliberately restrict what each component can do. Constrain both behavior (what a thing does) and collaboration (what it can call).
Why: Unconstrained components express too much. Good abstractions restrict possibilities to make remaining ones clearer. Immutability is a prime example.
When a use case doesn't fit: Extract into separate module with its own consistency rules rather than polluting main design.
[INTERMEDIATE]
Ask "what design change prevents this category of bug?" rather than fixing individual instances. Track bug-prone areas and redesign them.
Fear of making changes signals design problem: unclear side effects | missing tests | tangled responsibilities. Fixing individual bugs without addressing design guarantees recurrence.
See fp-algebra-driven-design section 5 for algebraic contradiction analysis.
[INTERMEDIATE]
Assign testing approach to each design element role. Controllers get component tests. Command objects get unit tests. Removes "how should I test this?" decision entirely.
Why: Reduces decision fatigue. Tests also document and enforce what each role should do.
[ADVANCED]
Apply UX research techniques to your codebase:
| Technique | Application |
|---|---|
| Developer interviews | What was hard? Confusing? Risky? |
| Personas | Developer profiles (skill level, domain knowledge, tool familiarity) |
| Flow analysis | Map steps for common tasks; identify bottlenecks |
| Timed tasks | Give unfamiliar developers specific tasks; observe friction |
| Continuous feedback | Retrospectives and root-cause analysis as ongoing design feedback |
Does this module represent a feature domain?
YES --> Feature-oriented folder (auth/, order/, speakers/)
Does the feature have sub-features?
YES --> Nested feature folders (speakers/profile/)
NO --> Single feature folder
NO --> Is it shared infrastructure?
YES --> Infrastructure folder, organized by capability
NO --> Is it a cross-cutting concern?
YES --> Shared module, referenced by feature modules
NO --> Evaluate whether it belongs in an existing feature
Design Elements + Algebraic Rules: Each element's constraints ("a Controller can only call Services") are formalizable as algebraic rules, making constraints machine-checkable. See fp-algebra-driven-design. Example: imports(controllerModule) intersect dbModules == empty.
Navigability + Simple Rules: Algebraic decomposition produces small, orthogonal operations with simple rules. Small operations are easy to name well. Example: applyDiscount and calculateTax are instantly searchable; processOrder is not.
Feature Organization + Domain Modeling: Feature folders align with bounded contexts. Each feature owns its domain types and workflows. See fp-domain-modeling. Example: order/types.fs, order/validate.fs, order/price.fs.
Prescribed Testing + Property-Based Testing: Design elements prescribe WHAT kind of test. PBT prescribes HOW to write those tests as properties. Example: Command objects get conservation properties (forAll(order -> totalOf(applyDiscount(order)) <= totalOf(order))). Controllers get round-trip properties.