From software-design
Opinionated, entity-driven software design principles for structuring code, naming, picking abstractions, choosing project layout, and reviewing existing architecture. Use this skill whenever the user is designing, restructuring, naming, or reviewing code — even if they don't explicitly ask for "design help." Triggers include: "how should I structure X", "where does this go", "what should I call this", "is this the right abstraction", "review my package layout", deciding between monolith/microservice/module split, API route design, repository or interface decisions, refactoring questions, choosing between a function and a method, naming a package or type, designing entity/storage layers, organizing routes, and any new module/file/package being created from scratch. Apply by default for design discussions; do not wait for the user to invoke it by name.
How this skill is triggered — by the user, by Claude, or both
Slash command
/software-design:software-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Entity-driven, OOP-leaning, composition-first design. Goal: rocksolid, maintainable, minimal code. Every line is debt. Folder structure documents the system.
Entity-driven, OOP-leaning, composition-first design. Goal: rocksolid, maintainable, minimal code. Every line is debt. Folder structure documents the system.
When designing or reviewing, call out the principle being applied inline so the reasoning is visible. Format:
(principle: <short name> — <one-line why>)
Example:
Drop the
ExchangePairtype. Put it in packageexchangeasPair.(principle: namespace-over-prefix — multi-word type means bad namespace or bad name)
Apply principles as a lens, not a checklist. When two principles conflict, surface the tradeoff explicitly rather than picking silently. State the cost of the choice.
Service entity + a Services collection is normal and good — both can carry behavior.ExchangePair → Good: exchange.Pairbook.BookShelf → Good: book.Shelfbook.Book + book.Books, Service + Services./events, /markets, /orders.event, market, order.POST /calculation over /calculate. CRUD covers the majority./auth/{in,out,register} is acceptable.Structure by dependency direction. Independent stuff lives lower, domain stuff higher, composition at the top.
lib/ or pkg/ — independent, transferable: clients (pg, http), tooling, math
cmd/ or bin/ — entrypoints; composition root; where everything wires together
<domain>/ — entities, collections, storage implementations
usecases/ or services/ — combine domains + libs to deliver a capability
src/bin/. Go: cmd/. Don't fight idioms.identity, payments, markets).lib/ may contain things domain uses. What's forbidden is lib/ depending on domain/. Dependency direction must stay clean: cmd → usecase → domain → lib. Never the other way.helpers/ or utils/ dumping grounds. That's "logic without a classification" — all-catch-garbage.
utils/utils.sin()utils/math.sin()lib/math.sin()Let DB schema drive entity layout. Prefix in DB collapses into a folder.
DB tables: Code:
identity_user → identity/user/{entity, collection, storage}
identity_picture → identity/picture/...
identity_token → identity/token/...
identity_twofa → identity/twofa/...
identity.Profile = user + picture. DB is source of truth; generate models from schema where possible.Abstraction is a tradeoff, never a default.
Repository interface if it buys testability. Skip it if only one driver will ever exist and is already implemented. Direct implementation beats ceremonial interface.Don't create a function for one caller. A one-use function is usually deletable inline. Extract only when at least one holds:
Server.from_config_path → Server.from_config → Server{...}).main/composition roots are the exception.)Layer them. Each level adds one concern:
Server.from_config_path(path) → reads file, calls
Server.from_config(cfg) → validates, calls
Server { ... } → raw struct literal
Services as list of Service, and Process as child of Service all need log_to_file. Define one tiny trait/interface so concat(Services), concat([Service, Process]), etc. all work. This kind of abstraction is hard to spot and usually 1–2 function signatures wide.io.Reader / io.Writer are the standard to aim for. One method, universal applicability.fmt, io, iteration protocols) before inventing new ones.Books type can sort, filter, paginate, and persist. Don't scatter that logic into free functions.cmd//main). Wire what you have. Push logic down only when the entrypoint gets cluttered.Same problem, different surface. Distinguish:
Button, Input, Modal) — reusable, no domain knowledge.HomepageHero, OrderConfirmation) — composed of generics, carry domain meaning.Draw the line early. Generic components stay in something like components/ui/; domain components live with the feature/page they belong to.
Often here wins following:
/src
/pages
/home
_components
page.tsx
/another_page
page.tsx
_components/
generic
modal
button
/eslint
Its good practice to force singular names, and underscore. page, layout, button, chart, card. Usually any more complicated name means folder for namespacing.
POST /calculation over /calculate. The noun is the resource; HTTP is the verb.Following pattern is often useful:
func begin(conn, fn) {
if conn == tx {retrun fn(conn)} else {
begin tx
try: fn(tx); commit;
except: rollback
}
}
func c(x) {
begin(x, () => ...)
}
func a(x) {
begin(x, c)
}
call c or a - both will run just one tx
Keeping thing stateless is usually good practice. Avoid in memory/file state. Better to use db. This has serval asterisks, but generally applies.
Very useful in practice to clean things up to carry tracing ids etc. If serverside is being build good to have and use if there are alternatives to go context use it.
When asked "how should I structure X" or "review this":
(principle: <name> — <why>).User: "I have ExchangePairManager that holds pairs and syncs them with the exchange API. Where does this go?"
Response shape:
Restructure as
exchange/package withPair,Pairs, and a sync method onPairs.(principle: namespace-over-prefix —ExchangePairManageris three words because the namespace is missing)(principle: collection-encapsulates — sync belongs on thePairscollection, not a separate "Manager")exchange/ pair.go // type Pair struct {...} pairs.go // type Pairs []Pair; func (Pairs) Sync(ctx) error client.go // HTTP client to the exchangeSkip
Manager.(principle: no-helpers-no-managers — "Manager" is a classification-less name; the behavior lives on the collection)
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub ricccrd/software-design --plugin software-design