{"id":"0d62eb46-cf12-49b8-b24f-bc78561ae094","shortId":"yfjZ43","kind":"skill","title":"go-interfaces","tagline":"Use when defining or implementing Go interfaces, designing abstractions, creating mockable boundaries for testing, or composing types through embedding. Also use when deciding whether to accept an interface or return a concrete type, or using type assertions or type switches, eve","description":"# Go Interfaces and Composition\n\n## Available Scripts\n\n- **`scripts/check-interface-compliance.sh`** — Finds exported interfaces missing compile-time compliance checks (`var _ I = (*T)(nil)`). Run `bash scripts/check-interface-compliance.sh --help` for options.\n\n---\n\n## Accept Interfaces, Return Concrete Types\n\nInterfaces belong in the package that **consumes** values, not the package that\n**implements** them. Return concrete (usually pointer or struct) types from\nconstructors so new methods can be added without refactoring.\n\n```go\n// Good: consumer defines the interface it needs\npackage consumer\n\ntype Thinger interface { Thing() bool }\n\nfunc Foo(t Thinger) string { ... }\n```\n\n```go\n// Good: producer returns concrete type\npackage producer\n\ntype Thinger struct{ ... }\nfunc (t Thinger) Thing() bool { ... }\nfunc NewThinger() Thinger { return Thinger{ ... } }\n```\n\n```go\n// Bad: producer defines and returns its own interface\npackage producer\n\ntype Thinger interface { Thing() bool }\ntype defaultThinger struct{ ... }\nfunc NewThinger() Thinger { return defaultThinger{ ... } }\n```\n\n**Do not define interfaces before they are used.** Without a realistic example\nof usage, it is too difficult to see whether an interface is even necessary.\n\n---\n\n## Generality: Hide Implementation, Expose Interface\n\nIf a type exists only to implement an interface with no exported methods beyond\nthat interface, return the interface from constructors to hide the implementation:\n\n```go\nfunc NewHash() hash.Hash32 {\n    return &myHash{}  // unexported type\n}\n```\n\nBenefits: implementation can change without affecting callers, substituting\nalgorithms requires only changing the constructor call.\n\n---\n\n## Type Assertions: Comma-Ok Idiom\n\nWithout checking, a failed assertion causes a runtime panic. Always use the\ncomma-ok idiom to test safely:\n\n```go\nstr, ok := value.(string)\nif ok {\n    fmt.Printf(\"string value is: %q\\n\", str)\n}\n```\n\nTo check if a value implements an interface:\n\n```go\nif _, ok := val.(json.Marshaler); ok {\n    fmt.Printf(\"value %v implements json.Marshaler\\n\", val)\n}\n```\n\n---\n\n## Type Switch\n\nIt's idiomatic to reuse the variable name (`t := t.(type)`) — the variable has\nthe correct type in each case branch. When a case lists multiple types\n(`case int, int64:`), the variable has the interface type.\n\n---\n\n## Embedding\n\nAvoid embedding types in public structs — the inner type's full method set\nbecomes part of your public API. Use unexported fields instead.\n\n> Read [references/EMBEDDING.md](references/EMBEDDING.md) when using struct embedding for composition, overriding embedded methods, resolving name conflicts, applying the HandlerFunc adapter pattern, or deciding whether to embed in public API types.\n\n---\n\n## Interface Satisfaction Checks\n\nUse a blank identifier assignment to verify a type implements an interface at\ncompile time:\n\n```go\nvar _ json.Marshaler = (*RawMessage)(nil)\n```\n\nThis causes a compile error if `*RawMessage` doesn't implement `json.Marshaler`.\n\nUse this pattern when:\n- There are no static conversions that would verify the interface automatically\n- The type must satisfy an interface for correct behavior (e.g., custom JSON\n  marshaling)\n- Interface changes should break compilation, not silently degrade\n\n**Don't** add these checks for every interface — only when no other static\nconversion would catch the error.\n\n> **Validation**: After defining interfaces or implementations, run `bash scripts/check-interface-compliance.sh` to verify all concrete types have compile-time `var _ I = (*T)(nil)` checks.\n\n---\n\n## Receiver Type\n\nIf in doubt, use a pointer receiver. Don't mix receiver types on a single\ntype — if any method needs a pointer, use pointers for all methods. Use value\nreceivers only for small, immutable types (`Point`, `time.Time`) or basic types.\n\n> Read [references/RECEIVER-TYPE.md](references/RECEIVER-TYPE.md) when deciding between pointer and value receivers for a new type, especially for types with sync primitives or large structs.\n\n---\n\n## Quick Reference\n\n| Concept | Pattern | Notes |\n|---------|---------|-------|\n| Consumer owns interface | Define interfaces where used | Not in the implementing package |\n| Safe type assertion | `v, ok := x.(Type)` | Returns zero value + false |\n| Type switch | `switch v := x.(type)` | Variable has correct type per case |\n| Interface embedding | `type RW interface { Reader; Writer }` | Union of methods |\n| Struct embedding | `type S struct { *T }` | Promotes T's methods |\n| Interface check | `var _ I = (*T)(nil)` | Compile-time verification |\n| Generality | Return interface from constructor | Hide implementation |\n\n---\n\n## Related Skills\n\n- **Interface naming**: See [go-naming](../go-naming/SKILL.md) when naming interfaces (the `-er` suffix convention) or choosing receiver names\n- **Error types**: See [go-error-handling](../go-error-handling/SKILL.md) when implementing the `error` interface, custom error types, or `errors.As` matching\n- **Generics vs interfaces**: See [go-generics](../go-generics/SKILL.md) when deciding whether generics are needed or an interface already suffices\n- **Functional options**: See [go-functional-options](../go-functional-options/SKILL.md) when using an interface-based Option pattern for flexible constructors\n- **Compile-time checks**: See [go-defensive](../go-defensive/SKILL.md) when adding `var _ I = (*T)(nil)` satisfaction checks at API boundaries","tags":["interfaces","golang","skills","cxuu","agent-skills","ai-agent","ai-assistant","claude","claude-code","codex","cursor","llm"],"capabilities":["skill","source-cxuu","skill-go-interfaces","topic-agent-skills","topic-ai-agent","topic-ai-assistant","topic-claude","topic-claude-code","topic-codex","topic-cursor","topic-golang","topic-llm"],"categories":["golang-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cxuu/golang-skills/go-interfaces","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cxuu/golang-skills","source_repo":"https://github.com/cxuu/golang-skills","install_from":"skills.sh"}},"qualityScore":"0.491","qualityRationale":"deterministic score 0.49 from registry signals: · indexed on github topic:agent-skills · 82 github stars · SKILL.md body (5,412 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T12:55:18.602Z","embedding":null,"createdAt":"2026-04-18T22:13:16.625Z","updatedAt":"2026-05-02T12:55:18.602Z","lastSeenAt":"2026-05-02T12:55:18.602Z","tsv":"'/go-defensive/skill.md':740 '/go-error-handling/skill.md':682 '/go-functional-options/skill.md':720 '/go-generics/skill.md':701 '/go-naming/skill.md':663 'abstract':12 'accept':29,71 'ad':104,742 'adapt':391 'add':474 'affect':241 'algorithm':244 'alreadi':711 'also':23 'alway':266 'api':368,400,750 'appli':388 'assert':40,252,261,597 'assign':409 'automat':450 'avail':49 'avoid':350 'bad':149 'base':726 'bash':66,497 'basic':553 'becom':363 'behavior':459 'belong':77 'benefit':236 'beyond':216 'blank':407 'bool':121,142,163 'boundari':15,751 'branch':333 'break':467 'call':250 'caller':242 'case':332,336,340,617 'catch':487 'caus':262,426 'chang':239,247,465 'check':60,258,291,404,476,512,639,735,748 'choos':672 'comma':254,270 'comma-ok':253,269 'compil':57,418,428,468,506,645,733 'compile-tim':56,505,644,732 'complianc':59 'compos':19 'composit':48,381 'concept':580 'concret':35,74,91,131,502 'conflict':387 'constructor':98,223,249,652,731 'consum':82,109,116,583 'convent':670 'convers':444,485 'correct':328,458,614 'creat':13 'custom':461,688 'decid':26,394,559,703 'defaultthing':165,171 'defens':739 'defin':6,110,151,174,492,586 'degrad':471 'design':11 'difficult':189 'doesn':432 'doubt':517 'e.g':460 'emb':397 'embed':22,349,351,379,383,619,629 'er':668 'error':429,489,675,680,686,689 'errors.as':692 'especi':569 'eve':44 'even':196 'everi':478 'exampl':183 'exist':206 'export':53,214 'expos':201 'fail':260 'fals':605 'field':371 'find':52 'flexibl':730 'fmt.printf':283,304 'foo':123 'full':360 'func':122,138,143,167,229 'function':713,718 'general':198,648 'generic':694,700,705 'go':2,9,45,107,127,148,228,276,298,420,661,679,699,717,738 'go-defens':737 'go-error-handl':678 'go-functional-opt':716 'go-gener':698 'go-interfac':1 'go-nam':660 'good':108,128 'handl':681 'handlerfunc':390 'hash.hash32':231 'help':68 'hide':199,225,653 'identifi':408 'idiom':256,272 'idiomat':315 'immut':548 'implement':8,88,200,209,227,237,295,307,414,434,495,593,654,684 'inner':357 'instead':372 'int':341 'int64':342 'interfac':3,10,31,46,54,72,76,112,119,156,161,175,194,202,211,218,221,297,347,402,416,449,456,464,479,493,585,587,618,622,638,650,657,666,687,696,710,725 'interface-bas':724 'json':462 'json.marshaler':302,308,422,435 'larg':576 'list':337 'marshal':463 'match':693 'method':101,215,361,384,533,541,627,637 'miss':55 'mix':524 'mockabl':14 'multipl':338 'must':453 'myhash':233 'n':288,309 'name':320,386,658,662,665,674 'necessari':197 'need':114,534,707 'new':100,567 'newhash':230 'newthing':144,168 'nil':64,424,511,643,746 'note':582 'ok':255,271,278,282,300,303,599 'option':70,714,719,727 'overrid':382 'own':584 'packag':80,86,115,133,157,594 'panic':265 'part':364 'pattern':392,438,581,728 'per':616 'point':550 'pointer':93,520,536,538,561 'primit':574 'produc':129,134,150,158 'promot':634 'public':354,367,399 'q':287 'quick':578 'rawmessag':423,431 'read':373,555 'reader':623 'realist':182 'receiv':513,521,525,544,564,673 'refactor':106 'refer':579 'references/embedding.md':374,375 'references/receiver-type.md':556,557 'relat':655 'requir':245 'resolv':385 'return':33,73,90,130,146,153,170,219,232,602,649 'reus':317 'run':65,496 'runtim':264 'rw':621 'safe':275,595 'satisfact':403,747 'satisfi':454 'script':50 'scripts/check-interface-compliance.sh':51,67,498 'see':191,659,677,697,715,736 'set':362 'silent':470 'singl':529 'skill':656 'skill-go-interfaces' 'small':547 'source-cxuu' 'static':443,484 'str':277,289 'string':126,280,284 'struct':95,137,166,355,378,577,628,632 'substitut':243 'suffic':712 'suffix':669 'switch':43,312,607,608 'sync':573 'test':17,274 'thing':120,141,162 'thinger':118,125,136,140,145,147,160,169 'time':58,419,507,646,734 'time.time':551 'topic-agent-skills' 'topic-ai-agent' 'topic-ai-assistant' 'topic-claude' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-golang' 'topic-llm' 'type':20,36,39,42,75,96,117,132,135,159,164,205,235,251,311,323,329,339,348,352,358,401,413,452,503,514,526,530,549,554,568,571,596,601,606,611,615,620,630,676,690 'unexport':234,370 'union':625 'usag':185 'use':4,24,38,179,267,369,377,405,436,518,537,542,589,722 'usual':92 'v':306,598,609 'val':301,310 'valid':490 'valu':83,279,285,294,305,543,563,604 'var':61,421,508,640,743 'variabl':319,325,344,612 'verif':647 'verifi':411,447,500 'vs':695 'whether':27,192,395,704 'without':105,180,240,257 'would':446,486 'writer':624 'x':600,610 'zero':603","prices":[{"id":"950984c1-5224-4e76-aa2f-36f181f4a25b","listingId":"0d62eb46-cf12-49b8-b24f-bc78561ae094","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cxuu","category":"golang-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:13:16.625Z"}],"sources":[{"listingId":"0d62eb46-cf12-49b8-b24f-bc78561ae094","source":"github","sourceId":"cxuu/golang-skills/go-interfaces","sourceUrl":"https://github.com/cxuu/golang-skills/tree/main/skills/go-interfaces","isPrimary":false,"firstSeenAt":"2026-04-18T22:13:16.625Z","lastSeenAt":"2026-05-02T12:55:18.602Z"}],"details":{"listingId":"0d62eb46-cf12-49b8-b24f-bc78561ae094","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cxuu","slug":"go-interfaces","github":{"repo":"cxuu/golang-skills","stars":82,"topics":["agent-skills","ai-agent","ai-assistant","claude","claude-code","codex","cursor","go","golang","llm"],"license":"apache-2.0","html_url":"https://github.com/cxuu/golang-skills","pushed_at":"2026-03-15T19:32:10Z","description":"AI Agent Skills for idiomatic, production-ready Go code, distilled from Google, Uber, Community","skill_md_sha":"9c29e1906c046298b25ca1dde86ddab257e0241e","skill_md_path":"skills/go-interfaces/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cxuu/golang-skills/tree/main/skills/go-interfaces"},"layout":"multi","source":"github","category":"golang-skills","frontmatter":{"name":"go-interfaces","license":"Apache-2.0","description":"Use when defining or implementing Go interfaces, designing abstractions, creating mockable boundaries for testing, or composing types through embedding. Also use when deciding whether to accept an interface or return a concrete type, or using type assertions or type switches, even if the user doesn't explicitly mention interfaces. Does not cover generics-based polymorphism (see go-generics)."},"skills_sh_url":"https://skills.sh/cxuu/golang-skills/go-interfaces"},"updatedAt":"2026-05-02T12:55:18.602Z"}}