{"id":"a5991acd-9472-4a78-a91d-fcdda16a244c","shortId":"F3kxkB","kind":"skill","title":"golang-safety","tagline":"Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use whenever writing or reviewing Go code that involves nil-prone types (pointers, interfaces, maps, slices, channels), numeric conversions, resource lifecycle (defer in loops), or defensi","description":"**Persona:** You are a defensive Go engineer. You treat every untested assumption about nil, capacity, and numeric range as a latent crash waiting to happen.\n\n# Go Safety: Correctness & Defensive Coding\n\nPrevents programmer mistakes — bugs, panics, and silent data corruption in normal (non-adversarial) code. Security handles attackers; safety handles ourselves.\n\n## Best Practices Summary\n\n1. **Prefer generics over `any`** when the type set is known — compiler catches mismatches instead of runtime panics\n2. **Always use comma-ok for type assertions** — bare assertions panic on mismatch\n3. **Typed nil pointer in an interface is not `== nil`** — the type descriptor makes it non-nil\n4. **Writing to a nil map panics** — always initialize before use\n5. **`append` may reuse the backing array** — both slices share memory if capacity allows, silently corrupting each other\n6. **Return defensive copies** from exported functions — otherwise callers mutate your internals\n7. **`defer` runs at function exit, not loop iteration** — extract loop body to a function\n8. **Integer conversions truncate silently** — `int64` to `int32` wraps without error\n9. **Float arithmetic is not exact** — use epsilon comparison or `math/big`\n10. **Design useful zero values** — nil map fields panic on first write; use lazy init\n11. **Use `sync.Once` for lazy init** — guarantees exactly-once even under concurrency\n\n## Nil Safety\n\nNil-related panics are the most common crash in Go.\n\n### The nil interface trap\n\nInterfaces store (type, value). An interface is `nil` only when both are nil. Returning a typed nil pointer sets the type descriptor, making it non-nil:\n\n```go\n// ✗ Dangerous — interface{type: *MyHandler, value: nil} is not == nil\nfunc getHandler() http.Handler {\n    var h *MyHandler // nil pointer\n    if !enabled {\n        return h // interface{type: *MyHandler, value: nil} != nil\n    }\n    return h\n}\n\n// ✓ Good — return nil explicitly\nfunc getHandler() http.Handler {\n    if !enabled {\n        return nil // interface{type: nil, value: nil} == nil\n    }\n    return &MyHandler{}\n}\n```\n\n### Nil map, slice, and channel behavior\n\n| Type | Read from nil | Write to nil | Len/Cap of nil | Range over nil |\n| --- | --- | --- | --- | --- |\n| Map | Zero value | **panic** | 0 | 0 iterations |\n| Slice | **panic** (index) | **panic** (index) | 0 | 0 iterations |\n| Channel | Blocks forever | Blocks forever | 0 | Blocks forever |\n\n```go\n// ✗ Bad — nil map panics on write\nvar m map[string]int\nm[\"key\"] = 1\n\n// ✓ Good — initialize or lazy-init in methods\nm := make(map[string]int)\n\nfunc (r *Registry) Add(name string, val int) {\n    if r.items == nil { r.items = make(map[string]int) }\n    r.items[name] = val\n}\n```\n\nSee **[Nil Safety Deep Dive](./references/nil-safety.md)** for nil receivers, nil in generics, and nil interface performance.\n\n## Slice & Map Safety\n\n### Slice aliasing — the append trap\n\n`append` reuses the backing array if capacity allows. Both slices then share memory:\n\n```go\n// ✗ Dangerous — a and b share backing array\na := make([]int, 3, 5)\nb := append(a, 4)\nb[0] = 99 // also modifies a[0]\n\n// ✓ Good — full slice expression forces new allocation\nb := append(a[:len(a):len(a)], 4)\n```\n\n### Map concurrent access\n\nMaps MUST NOT be accessed concurrently — → see `samber/cc-skills-golang@golang-concurrency` for sync primitives.\n\nSee **[Slice and Map Deep Dive](./references/slice-map-safety.md)** for range pitfalls, subslice memory retention, and `slices.Clone`/`maps.Clone`.\n\n## Numeric Safety\n\n### Implicit type conversions truncate silently\n\n```go\n// ✗ Bad — silently wraps around if val > math.MaxInt32 (3B becomes -1.29B)\nvar val int64 = 3_000_000_000\ni32 := int32(val) // -1294967296 (silent wraparound)\n\n// ✓ Good — check before converting\nif val > math.MaxInt32 || val < math.MinInt32 {\n    return fmt.Errorf(\"value %d overflows int32\", val)\n}\ni32 := int32(val)\n```\n\n### Float comparison\n\n```go\n// ✗ Bad — floating point arithmetic is not exact\n0.1+0.2 == 0.3 // false\n\n// ✓ Good — use epsilon comparison\nconst epsilon = 1e-9\nmath.Abs((0.1+0.2)-0.3) < epsilon // true\n```\n\n### Division by zero\n\nInteger division by zero panics. Float division by zero produces `+Inf`, `-Inf`, or `NaN`.\n\n```go\nfunc avg(total, count int) (int, error) {\n    if count == 0 {\n        return 0, errors.New(\"division by zero\")\n    }\n    return total / count, nil\n}\n```\n\nFor integer overflow as a security vulnerability, see the `samber/cc-skills-golang@golang-security` skill section.\n\n## Resource Safety\n\n### defer in loops — resource accumulation\n\n`defer` runs at _function_ exit, not loop iteration. Resources accumulate until the function returns:\n\n```go\n// ✗ Bad — all files stay open until function returns\nfor _, path := range paths {\n    f, _ := os.Open(path)\n    defer f.Close() // deferred until function exits\n    process(f)\n}\n\n// ✓ Good — extract to function so defer runs per iteration\nfor _, path := range paths {\n    if err := processOne(path); err != nil { return err }\n}\nfunc processOne(path string) error {\n    f, err := os.Open(path)\n    if err != nil { return err }\n    defer f.Close()\n    return process(f)\n}\n```\n\n### Goroutine leaks\n\n→ See `samber/cc-skills-golang@golang-concurrency` for goroutine lifecycle and leak prevention.\n\n## Immutability & Defensive Copying\n\nExported functions returning slices/maps SHOULD return defensive copies.\n\n### Protecting struct internals\n\n```go\n// ✗ Bad — exported slice field, anyone can mutate\ntype Config struct {\n    Hosts []string\n}\n\n// ✓ Good — unexported field with accessor returning a copy\ntype Config struct {\n    hosts []string\n}\n\nfunc (c *Config) Hosts() []string {\n    return slices.Clone(c.hosts)\n}\n```\n\n## Initialization Safety\n\n### Zero-value design\n\nDesign types so `var x MyType` is safe — prevents \"forgot to initialize\" bugs:\n\n```go\nvar mu sync.Mutex   // ✓ usable at zero value\nvar buf bytes.Buffer // ✓ usable at zero value\n\n// ✗ Bad — nil map panics on write\ntype Cache struct { data map[string]any }\n```\n\n### sync.Once for lazy initialization\n\n```go\ntype DB struct {\n    once sync.Once\n    conn *sql.DB\n}\n\nfunc (db *DB) connection() *sql.DB {\n    db.once.Do(func() {\n        db.conn, _ = sql.Open(\"postgres\", connStr)\n    })\n    return db.conn\n}\n```\n\n### init() function pitfalls\n\n→ See `samber/cc-skills-golang@golang-design-patterns` for why init() should be avoided in favor of explicit constructors.\n\n## Enforce with Linters\n\nMany safety pitfalls are caught automatically by linters: `errcheck`, `forcetypeassert`, `nilerr`, `govet`, `staticcheck`. See the `samber/cc-skills-golang@golang-lint` skill for configuration and usage.\n\n## Cross-References\n\n- → See `samber/cc-skills-golang@golang-concurrency` skill for concurrent access patterns and sync primitives\n- → See `samber/cc-skills-golang@golang-data-structures` skill for slice/map internals, capacity growth, and container/ packages\n- → See `samber/cc-skills-golang@golang-error-handling` skill for nil error interface trap\n- → See `samber/cc-skills-golang@golang-security` skill for security-relevant safety issues (memory safety, integer overflow)\n- → See `samber/cc-skills-golang@golang-troubleshooting` skill for debugging panics and race conditions\n\n## Common Mistakes\n\n| Mistake | Fix |\n| --- | --- |\n| Bare type assertion `v := x.(T)` | Panics on type mismatch, crashing the program. Use `v, ok := x.(T)` to handle gracefully |\n| Returning typed nil in interface function | Interface holds (type, nil) which is != nil. Return untyped `nil` for the nil case |\n| Writing to a nil map | Nil maps have no backing storage — write panics. Initialize with `make(map[K]V)` or lazy-init |\n| Assuming `append` always copies | If capacity allows, both slices share the backing array. Use `s[:len(s):len(s)]` to force a copy |\n| `defer` in a loop | `defer` runs at function exit, not loop iteration — resources accumulate. Extract body to a separate function |\n| `int64` to `int32` without bounds check | Values wrap silently (3B → -1.29B). Check against `math.MaxInt32`/`math.MinInt32` first |\n| Comparing floats with `==` | IEEE 754 representation is not exact (`0.1+0.2 != 0.3`). Use `math.Abs(a-b) < epsilon` |\n| Integer division without zero check | Integer division by zero panics. Guard with `if divisor == 0` before dividing |\n| Returning internal slice/map reference | Callers can mutate your struct's internals through the shared backing array. Return a defensive copy |\n| Multiple `init()` with ordering assumptions | `init()` execution order across files is unspecified. → See `samber/cc-skills-golang@golang-design-patterns` — use explicit constructors |\n| Blocking forever on nil channel | Nil channels block on both send and receive. Always initialize before use |\n\n## Cross-References\n\n- → See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines","tags":["golang","safety","skills","samber","agent","agent-skills","antigravity","claude","claude-code","code","codex","coding"],"capabilities":["skill","source-samber","skill-golang-safety","topic-agent","topic-agent-skills","topic-antigravity","topic-claude","topic-claude-code","topic-code","topic-codex","topic-coding","topic-copilot","topic-cursor","topic-gemini","topic-gemini-cli-extension"],"categories":["cc-skills-golang"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/samber/cc-skills-golang/golang-safety","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add samber/cc-skills-golang","source_repo":"https://github.com/samber/cc-skills-golang","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 1725 github stars · SKILL.md body (8,877 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-18T18:53:02.054Z","embedding":null,"createdAt":"2026-04-18T20:32:11.094Z","updatedAt":"2026-05-18T18:53:02.054Z","lastSeenAt":"2026-05-18T18:53:02.054Z","tsv":"'+0.2':607,619,1150 '-0.3':620 '-1.29':562,1133 '-1294967296':574 '/references/nil-safety.md':441 '/references/slice-map-safety.md':535 '0':370,371,378,379,386,491,496,650,652,1172 '0.1':606,618,1149 '0.3':608,1151 '000':568,569,570 '1':98,403 '10':226 '11':241 '1e-9':616 '2':116 '3':130,484,567 '3b':560,1132 '4':148,489,511 '5':159,485 '6':177 '7':189 '754':1144 '8':204 '9':215 '99':492 'a-b':1154 'access':514,519,952 'accessor':805 'accumul':682,692,1116 'across':1203 'add':420 'adversari':87 'ai':1246 'ai-driven':1245 'alias':456 'alloc':503 'allow':172,467,1086 'also':493 'alway':117,155,1082,1229 'anyon':793 'append':160,458,460,487,505,1081 'arithmet':217,602 'around':556 'array':165,464,480,1092,1190 'assert':124,126,1018 'assum':1080 'assumpt':55,1199 'attack':91 'autom':1244 'automat':922 'avg':642 'avoid':908 'b':477,486,490,504,563,1134,1156 'back':164,463,479,1066,1091,1189 'bad':390,553,599,698,789,856 'bare':125,1016 'becom':561 'behavior':352 'best':95 'block':382,384,387,1216,1223 'bodi':200,1118 'bound':1127 'buf':850 'bug':16,77,840 'bytes.buffer':851 'c':815 'c.hosts':821 'cach':863 'caller':185,1179 'capac':58,171,466,967,1085 'case':1056 'catch':110 'caught':921 'channel':34,351,381,1220,1222 'check':578,1128,1135,1162 'ci':1251 'code':6,23,73,88,1248 'comma':120 'comma-ok':119 'common':263,1012 'compar':1140 'comparison':223,597,613 'compil':109 'concurr':253,513,520,525,767,948,951 'condit':1011 'config':797,810,816 'configur':938 'conn':879 'connect':884 'connstr':891 'const':614 'constructor':913,1215 'contain':970 'continu':1240 'convers':36,206,549 'convert':580 'copi':180,776,784,808,1083,1102,1194 'correct':71 'corrupt':12,82,174 'count':644,649,659 'crash':65,264,1026 'cross':942,1234 'cross-refer':941,1233 'd':589 'danger':299,474 'data':11,81,865,961 'db':875,882,883 'db.conn':888,893 'db.once.do':886 'debug':1007 'deep':439,533 'defens':4,48,72,179,775,783,1193 'defensi':43 'defer':39,190,678,683,713,715,726,756,1103,1107 'descriptor':142,292 'design':227,827,828,901,1211 'dive':440,534 'divid':1174 'divis':623,627,632,654,1159,1164 'divisor':1171 'driven':1247 'enabl':317,336 'enforc':914 'engin':50 'epsilon':222,612,615,621,1157 'err':735,738,741,748,752,755 'errcheck':925 'error':214,647,746,976,981 'errors.new':653 'even':251 'everi':53 'exact':220,249,605,1148 'exactly-onc':248 'execut':1201 'exit':194,687,718,1111 'explicit':331,912,1214 'export':182,777,790 'express':500 'extract':198,722,1117 'f':710,720,747,760 'f.close':714,757 'fals':609 'favor':910 'field':233,792,803 'file':700,1204 'first':236,1139 'fix':1015 'float':216,596,600,631,1141 'fmt.errorf':587 'forc':501,1100 'forcetypeassert':926 'forev':383,385,388,1217 'forgot':837 'full':498 'func':308,332,417,641,742,814,881,887 'function':183,193,203,686,695,704,717,724,778,895,1042,1110,1122 'generic':100,447 'gethandl':309,333 'go':22,49,69,266,298,389,473,552,598,640,697,788,841,873 'golang':2,5,524,672,766,900,934,947,960,975,987,1003,1210,1239 'golang-concurr':523,765,946 'golang-continuous-integr':1238 'golang-data-structur':959 'golang-design-pattern':899,1209 'golang-error-handl':974 'golang-lint':933 'golang-safeti':1 'golang-secur':671,986 'golang-troubleshoot':1002 'good':328,404,497,577,610,721,801 'goroutin':761,769 'govet':928 'grace':1036 'growth':968 'guarante':247 'guard':1168 'guidelin':1254 'h':312,319,327 'handl':90,93,977,1035 'happen':68 'hold':1044 'host':799,812,817 'http.handler':310,334 'i32':571,593 'ieee':1143 'immut':774 'implicit':547 'index':375,377 'inf':636,637 'init':240,246,409,894,905,1079,1196,1200 'initi':156,405,822,839,872,1070,1230 'instead':112 'int':400,416,424,432,483,645,646 'int32':211,572,591,594,1125 'int64':209,566,1123 'integ':205,626,662,998,1158,1163 'integr':1241 'interfac':31,136,269,271,276,300,320,339,450,982,1041,1043 'intern':188,787,966,1176,1185 'involv':25 'issu':995 'iter':197,372,380,690,729,1114 'k':1074 'key':402 'known':108 'latent':64 'lazi':239,245,408,871,1078 'lazy-init':407,1077 'leak':762,772 'len':507,509,1095,1097 'len/cap':360 'lifecycl':38,770 'lint':935 'linter':916,924 'loop':41,196,199,680,689,1106,1113 'm':397,401,412 'make':143,293,413,429,482,1072 'mani':917 'map':32,153,232,348,366,392,398,414,430,453,512,515,532,858,866,1061,1063,1073 'maps.clone':544 'math.abs':617,1153 'math.maxint32':559,583,1137 'math.minint32':585,1138 'math/big':225 'may':161 'memori':169,472,540,996 'method':411 'mismatch':111,129,1025 'mistak':76,1013,1014 'modifi':494 'mu':843 'multipl':1195 'must':516 'mutat':186,795,1181 'myhandl':302,313,322,346 'mytyp':833 'name':421,434 'nan':639 'new':502 'nil':27,57,132,139,147,152,231,254,257,268,278,283,287,297,304,307,314,324,325,330,338,341,343,344,347,356,359,362,365,391,427,437,443,445,449,660,739,753,857,980,1039,1046,1049,1052,1055,1060,1062,1219,1221 'nil-pron':26 'nil-rel':256 'nilerr':927 'non':86,146,296 'non-adversari':85 'non-nil':145,295 'normal':84 'numer':35,60,545 'ok':121,1031 'open':702 'order':1198,1202 'os.open':711,749 'otherwis':184 'overflow':590,663,999 'packag':971 'panic':9,78,115,127,154,234,259,369,374,376,393,630,859,1008,1022,1069,1167 'path':707,709,712,731,733,737,744,750 'pattern':902,953,1212 'per':728 'perform':451 'persona':44 'pitfal':538,896,919 'point':601 'pointer':30,133,288,315 'postgr':890 'practic':96 'prefer':99 'prevent':8,74,773,836 'primit':528,956 'process':719,759 'processon':736,743 'produc':635 'program':1028 'programm':75 'prone':28 'protect':785 'r':418 'r.items':426,428,433 'race':1010 'rang':61,363,537,708,732 'read':354 'receiv':444,1228 'refer':943,1178,1235 'registri':419 'relat':258 'relev':993 'represent':1145 'resourc':37,676,681,691,1115 'retent':541 'return':178,284,318,326,329,337,345,586,651,657,696,705,740,754,758,779,782,806,819,892,1037,1050,1175,1191 'reus':162,461 'review':21,1249 'run':191,684,727,1108 'runtim':15,114 'safe':835 'safeti':3,70,92,255,438,454,546,677,823,918,994,997 'samber/cc-skills-golang':522,670,764,898,932,945,958,973,985,1001,1208,1237 'section':675 'secur':89,666,673,988,992 'security-relev':991 'see':436,521,529,668,763,897,930,944,957,972,984,1000,1207,1236 'send':1226 'separ':1121 'set':106,289 'share':168,471,478,1089,1188 'silent':10,80,173,208,551,554,575,1131 'skill':674,936,949,963,978,989,1005,1242 'skill-golang-safety' 'slice':33,167,349,373,452,455,469,499,530,791,1088 'slice/map':965,1177 'slices.clone':543,820 'slices/maps':780 'source-samber' 'sql.db':880,885 'sql.open':889 'staticcheck':929 'stay':701 'storag':1067 'store':272 'string':399,415,422,431,745,800,813,818,867 'struct':786,798,811,864,876,1183 'structur':962 'subslic':539 'subtl':14 'summari':97 'sync':527,955 'sync.mutex':844 'sync.once':243,869,878 'topic-agent' 'topic-agent-skills' 'topic-antigravity' 'topic-claude' 'topic-claude-code' 'topic-code' 'topic-codex' 'topic-coding' 'topic-copilot' 'topic-cursor' 'topic-gemini' 'topic-gemini-cli-extension' 'total':643,658 'trap':270,459,983 'treat':52 'troubleshoot':1004 'true':622 'truncat':207,550 'type':29,105,123,131,141,273,286,291,301,321,340,353,548,796,809,829,862,874,1017,1024,1038,1045 'unexport':802 'unspecifi':1206 'untest':54 'untyp':1051 'usabl':845,852 'usag':940 'use':17,118,158,221,228,238,242,611,1029,1093,1152,1213,1232,1252 'v':1019,1030,1075 'val':423,435,558,565,573,582,584,592,595 'valu':230,274,303,323,342,368,588,826,848,855,1129 'var':311,396,564,831,842,849 'vulner':667 'wait':66 'whenev':18 'without':213,1126,1160 'wrap':212,555,1130 'wraparound':576 'write':19,149,237,357,395,861,1057,1068 'x':832,1020,1032 'zero':229,367,625,629,634,656,825,847,854,1161,1166 'zero-valu':824","prices":[{"id":"81236fad-c70d-43ac-83ed-8776d8d995c0","listingId":"a5991acd-9472-4a78-a91d-fcdda16a244c","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"samber","category":"cc-skills-golang","install_from":"skills.sh"},"createdAt":"2026-04-18T20:32:11.094Z"}],"sources":[{"listingId":"a5991acd-9472-4a78-a91d-fcdda16a244c","source":"github","sourceId":"samber/cc-skills-golang/golang-safety","sourceUrl":"https://github.com/samber/cc-skills-golang/tree/main/skills/golang-safety","isPrimary":false,"firstSeenAt":"2026-04-18T21:55:17.267Z","lastSeenAt":"2026-05-18T18:53:02.054Z"},{"listingId":"a5991acd-9472-4a78-a91d-fcdda16a244c","source":"skills_sh","sourceId":"samber/cc-skills-golang/golang-safety","sourceUrl":"https://skills.sh/samber/cc-skills-golang/golang-safety","isPrimary":true,"firstSeenAt":"2026-04-18T20:32:11.094Z","lastSeenAt":"2026-05-07T22:40:27.043Z"}],"details":{"listingId":"a5991acd-9472-4a78-a91d-fcdda16a244c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"samber","slug":"golang-safety","github":{"repo":"samber/cc-skills-golang","stars":1725,"topics":["agent","agent-skills","ai","antigravity","claude","claude-code","code","codex","coding","copilot","cursor","gemini","gemini-cli-extension","openclaw","opencode","plugin","skills","skillsmp","vibe-coding"],"license":"mit","html_url":"https://github.com/samber/cc-skills-golang","pushed_at":"2026-05-18T17:36:00Z","description":"🧑‍🎨 A collection of Golang agentic skills that works","skill_md_sha":"56d3c5fd753851074cd89757f23844e183dc967f","skill_md_path":"skills/golang-safety/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/samber/cc-skills-golang/tree/main/skills/golang-safety"},"layout":"multi","source":"github","category":"cc-skills-golang","frontmatter":{"name":"golang-safety","license":"MIT","description":"Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use whenever writing or reviewing Go code that involves nil-prone types (pointers, interfaces, maps, slices, channels), numeric conversions, resource lifecycle (defer in loops), or defensive copying. Also triggers on questions about nil panics, append aliasing, map concurrent access, float comparison, or zero-value design.","compatibility":"Designed for Claude Code or similar AI coding agents, and for projects using Golang."},"skills_sh_url":"https://skills.sh/samber/cc-skills-golang/golang-safety"},"updatedAt":"2026-05-18T18:53:02.054Z"}}