FORWARD view over rules/ — domain type design and architectural planning for Go code BEFORE it exists. Use when planning new features, designing self-validating types, preventing primitive obsession, or when refactoring reveals need for new types. Dispatches into the Design guidance sections of rules/R1-R8 and R10-R12.
Backward counterpart (fixing code that already fails lint/review): @refactoring.
Notation
Skill Tool Call
@testing
Skill(go-linter-driven-development:testing)
Scan the codebase structure: vertical (internal/feature/{handler,service}.go) vs
horizontal (internal/{handlers,services}/feature.go)?
Pure vertical → continue the pattern: implement as internal/<new-feature>/.
Pure horizontal → propose starting migration (template in R5's Design
guidance); implement the new feature as the first vertical slice.
Mixed → check docs/architecture/vertical-slice-migration.md, continue as a slice.
Architecture advises, it doesn't veto (R5's advisory posture). Ask the user:
Option A — vertical slice (recommended); Option B — match the existing pattern
(time pressure and team conventions are valid reasons).
Every function that receives another type's data → Tell, don't ask: does the
decision it makes belong on that type?
Every planned interface → The bigger the interface, the weaker the abstraction:
could it be one method?
Every planned type → Make illegal states unrepresentable vs Make the zero
value useful: validated domain type (constructor) or mechanism type (useful
zero)? Pick a family.
Every planned check → Parse, don't validate: does it return a more-typed value
or a boolean someone must remember?
Every shared helper → A little copying is better than a little dependency: is
the third strike actually here?
Answers shape the plan; they are never findings. Maxims propose, evidence disposes —
the review phases convict only via rules (maxims.md, contract section).
Rule
When designing, apply...
../../rules/R1-primitive-obsession.md
Which primitives become types — score every candidate with R1's juiciness scorecard; reject ceremony wrappers (over-abstraction trap).
../../rules/R2-self-validating-types.md
Constructor-only entry, validation ownership, trusting composed values, nil is not a value, no defensive checks in methods.
../../rules/R3-storifying.md
Plan orchestration functions as 3–5 named steps at one conceptual level; honest names for mutators.
../../rules/R4-helper-placement.md
WHERE each helper/type lands — the placement ladder (unexported → feature sub-package → shared domain package).
../../rules/R5-vertical-slice.md
Package structure and naming: feature slices with roles inside, flatcase domain vocabulary, migration template.
../../rules/R6-test-only-interfaces.md
Default dependencies to concrete types; an interface must be earned by a second production implementation or a grep-verified import cycle.
../../rules/R7-test-placement.md
The test plan per type: leaf types 100% unit coverage via public constructors; orchestrators integration-tested over real collaborators.
../../rules/R8-no-globals.md
Dependencies injected via constructors, ctx threaded from callers, globals only at entry points.
../../rules/R10-concurrency-safety.md
Every planned goroutine gets an owner (stop + wait) and an exit path at construction time; shared state designed with its guard on one type — or designed away via handoff/confinement.
../../rules/R11-conditional-dispatch.md
How each kind/variant family dispatches: behavior-heavy or open set → interface chosen once at the boundary; single-behavior variance → strategy map; single-site closed enum → one exhaustive switch (named enum per R1).
../../rules/R12-mutation-discipline.md
Each type's mutation surface: constructors copy slice/map arguments; queries return copies or iterators, never internal references; no setters around validating constructors; query and modifier as separate methods.
No primitive obsession; every proposed type scored, ceremony rejected (R1)
Types are self-validating; composed types trusted, never re-validated (R2)
Orchestration planned as a story; most logic pushed into leaf types (R3, R7)
Placement decided for every helper and type via the ladder — unexported helper vs feature sub-package vs domain package (../../rules/R4-helper-placement.md)
Vertical slice structure; package names are flatcase domain vocabulary, never roles/containers (R5)
No test-only interfaces: every interface has a second production implementation OR breaks a real import cycle, verified by grepping the import direction (detection command in ../../rules/R6-test-only-interfaces.md); otherwise depend on the concrete type
Import direction strictly downward: leaf types ← sub-packages ← parent ← cmd/ (cycle-breaking move in @refactoring <package_decomposition>)
Dependencies constructor-injected and validated; ctx flows down; no new globals (R8, R2)
Every goroutine has an owner and exit path; shared state guarded where it lives, or confined (R10)
Every kind/variant family has ONE dispatch owner — interface, strategy map, or a single exhaustive switch; no discriminator inspected in two places (R11)
Every validated type's mutation surface is closed: slice/map arguments copied in, internal collections never returned by reference, no unvalidated setters (R12)
Feature: [Feature Name]
Core Domain Types (leaf):
[Type] ([underlying]) — invariant it owns; juiciness verdict (R1)
Package Structure:
[feature]/
├── [type].go # each juicy type in its own file
├── service.go
└── handler.go
Placement Decisions (R4):
[helper/type] → rung 1/2/3 and why
Design Decisions:
[decision] — rationale, citing the owning rule
Integration Points:
Consumed by / depends on / events
Next Steps:
Create types with validating constructors
Write unit tests for each leaf type → use @testing skill
Implement orchestrators; integration tests over real collaborators
</output_format>
<success_criteria>
Design phase is complete when ALL are true:
- [ ] Architecture pattern analyzed (vertical/horizontal/mixed) and user chose an option
- [ ] Core domain types identified, each with its validation rules and R1 score
- [ ] Placement decision recorded for every new type/helper (R4 ladder)
- [ ] Package structure follows R5 (slices, naming, downward imports)
- [ ] Design checklist answered satisfactorily (every box cites its rule)
- [ ] Design plan presented in the output format above
</success_criteria>
1---2name: code-designing3description: FORWARD view over rules/ — domain type design and architectural planning for Go code BEFORE it exists. Use when planning new features, designing self-validating types, preventing primitive obsession, or when refactoring reveals need for new types. Dispatches into the Design guidance sections of rules/R1-R8 and R10-R12.4---56<objective>7The design phase applied BEFORE code exists. This skill is a thin directional view:8every design principle lives exactly once in `../../rules/` — this protocol says9which rule to open at which design step, and what shape the output takes.1011Backward counterpart (fixing code that already fails lint/review): @refactoring.12</objective>1314<skill_invocation>15**CRITICAL**: When this skill says "Use @skill-name", you MUST invoke it with the16**Skill tool** — do not just mention it.1718| Notation | Skill Tool Call |19|----------|-----------------|20| @testing | `Skill(go-linter-driven-development:testing)` |21</skill_invocation>2223<when_to_use>24- Planning a new feature (before writing code)25- Refactoring reveals need for new types (@refactoring escalates here)26- Linter failures that need a design decision, not a mechanical fix:27 - `argument-limit` (>4 params) → design an options struct (grouping data that travels together — score it per `../../rules/R1-primitive-obsession.md`)28 - `function-result-limit` (>3 returns) / `confusing-results` → design a named result type (same R1 scoring)29 - `file-length-limit` (>450 lines) → split juicy types into their own files (juiciness per R1; file-per-type per `../../rules/R5-vertical-slice.md`); a single god type routes to @refactoring's god-object decomposition procedure first30 - Package-size yellow/red zone → re-model with sub-packages *before* the zone escalates (@refactoring `<package_decomposition>`)31- A Phase 4 review CLUSTER (≥2 hunters converging on one anchor —32 @linter-driven-development routes it here) → **cluster-scoped mode**: skip33 `<architecture_scan>` and the user-OK step (acceptance was inherited when the34 findings were accepted); design only the one concept the cluster names — its35 type or dispatch shape (R11), constructor (R2), mutation surface (R12), and36 placement (R4) — so every member finding resolves as a consequence of that one37 design. Return the mini DESIGN PLAN to the caller; @refactoring implements it.38</when_to_use>3940<protocol>4142<architecture_scan priority="FIRST_STEP">43**Default: vertical slice architecture** — `../../rules/R5-vertical-slice.md`.4445Scan the codebase structure: vertical (`internal/feature/{handler,service}.go`) vs46horizontal (`internal/{handlers,services}/feature.go`)?47481. **Pure vertical** → continue the pattern: implement as `internal/<new-feature>/`.492. **Pure horizontal** → propose starting migration (template in R5's Design50 guidance); implement the new feature as the first vertical slice.513. **Mixed** → check `docs/architecture/vertical-slice-migration.md`, continue as a slice.5253Architecture advises, it doesn't veto (R5's advisory posture). Ask the user:54Option A — vertical slice (recommended); Option B — match the existing pattern55(time pressure and team conventions are valid reasons).56</architecture_scan>5758<understand_domain>59What is the problem domain? The main concepts/entities? The invariants and rules?60How does this fit the existing architecture?61</understand_domain>6263<maxim_interrogation>64Design happens before a diff exists — no detection command can run yet, so questions65are the tool. Interrogate the plan with `../../maxims.md` (the questions live there,66once; ask them, don't restate them):6768- Every function that receives another type's data → **Tell, don't ask**: does the69 decision it makes belong on that type?70- Every planned interface → **The bigger the interface, the weaker the abstraction**:71 could it be one method?72- Every planned type → **Make illegal states unrepresentable** vs **Make the zero73 value useful**: validated domain type (constructor) or mechanism type (useful74 zero)? Pick a family.75- Every planned check → **Parse, don't validate**: does it return a more-typed value76 or a boolean someone must remember?77- Every shared helper → **A little copying is better than a little dependency**: is78 the third strike actually here?7980Answers shape the plan; they are never findings. Maxims propose, evidence disposes —81the review phases convict only via rules (`maxims.md`, contract section).82</maxim_interrogation>8384<rule_dispatch>85For each concept in the design, open the rule that owns the question and apply its86**Design guidance** section:8788| Rule | When designing, apply... |89|------|--------------------------|90| `../../rules/R1-primitive-obsession.md` | Which primitives become types — score every candidate with R1's juiciness scorecard; reject ceremony wrappers (over-abstraction trap). |91| `../../rules/R2-self-validating-types.md` | Constructor-only entry, validation ownership, trusting composed values, nil is not a value, no defensive checks in methods. |92| `../../rules/R3-storifying.md` | Plan orchestration functions as 3–5 named steps at one conceptual level; honest names for mutators. |93| `../../rules/R4-helper-placement.md` | WHERE each helper/type lands — the placement ladder (unexported → feature sub-package → shared domain package). |94| `../../rules/R5-vertical-slice.md` | Package structure and naming: feature slices with roles inside, flatcase domain vocabulary, migration template. |95| `../../rules/R6-test-only-interfaces.md` | Default dependencies to concrete types; an interface must be earned by a second production implementation or a grep-verified import cycle. |96| `../../rules/R7-test-placement.md` | The test plan per type: leaf types 100% unit coverage via public constructors; orchestrators integration-tested over real collaborators. |97| `../../rules/R8-no-globals.md` | Dependencies injected via constructors, `ctx` threaded from callers, globals only at entry points. |98| `../../rules/R10-concurrency-safety.md` | Every planned goroutine gets an owner (stop + wait) and an exit path at construction time; shared state designed with its guard on one type — or designed away via handoff/confinement. |99| `../../rules/R11-conditional-dispatch.md` | How each kind/variant family dispatches: behavior-heavy or open set → interface chosen once at the boundary; single-behavior variance → strategy map; single-site closed enum → one exhaustive switch (named enum per R1). |100| `../../rules/R12-mutation-discipline.md` | Each type's mutation surface: constructors copy slice/map arguments; queries return copies or iterators, never internal references; no setters around validating constructors; query and modifier as separate methods. |101</rule_dispatch>102103<design_checklist>104Before presenting the plan, verify against the rules (cite, don't restate):105106- [ ] No primitive obsession; every proposed type scored, ceremony rejected (R1)107- [ ] Types are self-validating; composed types trusted, never re-validated (R2)108- [ ] Orchestration planned as a story; most logic pushed into leaf types (R3, R7)109- [ ] **Placement decided** for every helper and type via the ladder — unexported helper vs feature sub-package vs domain package (`../../rules/R4-helper-placement.md`)110- [ ] Vertical slice structure; package names are flatcase domain vocabulary, never roles/containers (R5)111- [ ] No test-only interfaces: every interface has a second production implementation OR breaks a real import cycle, verified by grepping the import direction (detection command in `../../rules/R6-test-only-interfaces.md`); otherwise depend on the concrete type112- [ ] Import direction strictly downward: leaf types ← sub-packages ← parent ← cmd/ (cycle-breaking move in @refactoring `<package_decomposition>`)113- [ ] Dependencies constructor-injected and validated; ctx flows down; no new globals (R8, R2)114- [ ] Every goroutine has an owner and exit path; shared state guarded where it lives, or confined (R10)115- [ ] Every kind/variant family has ONE dispatch owner — interface, strategy map, or a single exhaustive switch; no discriminator inspected in two places (R11)116- [ ] Every validated type's mutation surface is closed: slice/map arguments copied in, internal collections never returned by reference, no unvalidated setters (R12)117</design_checklist>118119</protocol>120121<output_format>122```123DESIGN PLAN124125Feature: [Feature Name]126127Core Domain Types (leaf):128- [Type] ([underlying]) — invariant it owns; juiciness verdict (R1)129130Orchestrating Types:131- [Type] — dependencies (concrete unless R6-justified), methods132133Package Structure:134[feature]/135 ├── [type].go # each juicy type in its own file136 ├── service.go137 └── handler.go138139Placement Decisions (R4):140- [helper/type] → rung 1/2/3 and why141142Design Decisions:143- [decision] — rationale, citing the owning rule144145Integration Points:146- Consumed by / depends on / events147148Next Steps:1491. Create types with validating constructors1502. Write unit tests for each leaf type → use @testing skill1513. Implement orchestrators; integration tests over real collaborators152```153</output_format>154155<success_criteria>156Design phase is complete when ALL are true:157158- [ ] Architecture pattern analyzed (vertical/horizontal/mixed) and user chose an option159- [ ] Core domain types identified, each with its validation rules and R1 score160- [ ] Placement decision recorded for every new type/helper (R4 ladder)161- [ ] Package structure follows R5 (slices, naming, downward imports)162- [ ] Design checklist answered satisfactorily (every box cites its rule)163- [ ] Design plan presented in the output format above164</success_criteria>
Run npx skillmds@latest add buzzdan/code-designing in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
FORWARD view over rules/ — domain type design and architectural planning for Go code BEFORE it exists. Use when planning new features, designing self-validating types, preventing primitive obsession, or when refactoring reveals need for new types. Dispatches into the Design guidance sections of rules/R1-R8 and R10-R12. It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: docs only. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
buzzdan (@buzzdan) published this skill. Their other Agent Skills are listed on their SkillMD profile.