opencode-skills-collection 3.0.49 → 3.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +27 -1
- package/bundled-skills/ab-test-setup/SKILL.md +14 -0
- package/bundled-skills/apple-notes-search/SKILL.md +122 -0
- package/bundled-skills/astropy/references/coordinates.md +9 -0
- package/bundled-skills/astropy/references/cosmology.md +8 -0
- package/bundled-skills/astropy/references/fits.md +8 -0
- package/bundled-skills/astropy/references/tables.md +8 -0
- package/bundled-skills/astropy/references/time.md +7 -0
- package/bundled-skills/astropy/references/units.md +9 -0
- package/bundled-skills/astropy/references/wcs_and_other_modules.md +9 -0
- package/bundled-skills/browser-extension-builder/SKILL.md +1 -1
- package/bundled-skills/ckw-design/SKILL.md +129 -0
- package/bundled-skills/deterministic-design/SKILL.md +56 -0
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/lint-and-validate/SKILL.md +3 -1
- package/bundled-skills/lookdev/SKILL.md +229 -0
- package/bundled-skills/lookdev-auto/SKILL.md +102 -0
- package/bundled-skills/macos-screen-recorder/SKILL.md +59 -0
- package/bundled-skills/pr-merge-champion/SKILL.md +116 -0
- package/bundled-skills/programmatic-seo/SKILL.md +11 -0
- package/bundled-skills/schema-markup/SKILL.md +11 -0
- package/bundled-skills/screenstudio-alt/SKILL.md +91 -0
- package/bundled-skills/super-code/SKILL.md +209 -0
- package/bundled-skills/super-code/bash/SKILL.md +292 -0
- package/bundled-skills/super-code/c/SKILL.md +263 -0
- package/bundled-skills/super-code/cpp/SKILL.md +271 -0
- package/bundled-skills/super-code/csharp/SKILL.md +276 -0
- package/bundled-skills/super-code/dart/SKILL.md +327 -0
- package/bundled-skills/super-code/elixir/SKILL.md +366 -0
- package/bundled-skills/super-code/go/SKILL.md +234 -0
- package/bundled-skills/super-code/java/SKILL.md +230 -0
- package/bundled-skills/super-code/kotlin/SKILL.md +281 -0
- package/bundled-skills/super-code/php/SKILL.md +316 -0
- package/bundled-skills/super-code/python/SKILL.md +315 -0
- package/bundled-skills/super-code/ruby/SKILL.md +306 -0
- package/bundled-skills/super-code/rust/SKILL.md +289 -0
- package/bundled-skills/super-code/scala/SKILL.md +302 -0
- package/bundled-skills/super-code/swift/SKILL.md +299 -0
- package/bundled-skills/super-code/typescript/SKILL.md +286 -0
- package/bundled-skills/web-media-getter/SKILL.md +119 -0
- package/bundled-skills/youtube-seo-optimizer/SKILL.md +9 -9
- package/package.json +1 -1
- package/skills_index.json +574 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: elixir
|
|
3
|
+
description: "Language-specific super-code guidelines for elixir."
|
|
4
|
+
risk: safe
|
|
5
|
+
source: community
|
|
6
|
+
date_added: "2026-06-16"
|
|
7
|
+
---
|
|
8
|
+
# Elixir / Erlang: Idiomatic Efficiency Reference
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
1. [Pattern Matching & Guards](#patterns)
|
|
12
|
+
2. [Pipe Operator & Transforms](#pipes)
|
|
13
|
+
3. [Processes & OTP](#otp)
|
|
14
|
+
4. [Error Handling](#errors)
|
|
15
|
+
5. [Collections & Enum](#collections)
|
|
16
|
+
6. [Structs & Protocols](#structs)
|
|
17
|
+
7. [Anti-patterns specific to Elixir/Erlang](#antipatterns)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Pattern Matching & Guards {#patterns}
|
|
22
|
+
|
|
23
|
+
```elixir
|
|
24
|
+
# ❌ Extracting with Map.get then checking
|
|
25
|
+
value = Map.get(map, :key)
|
|
26
|
+
if value != nil do
|
|
27
|
+
process(value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# ✅ — pattern match directly
|
|
31
|
+
case map do
|
|
32
|
+
%{key: value} -> process(value)
|
|
33
|
+
_ -> :noop
|
|
34
|
+
end
|
|
35
|
+
# or with if:
|
|
36
|
+
if value = map[:key], do: process(value)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```elixir
|
|
40
|
+
# ❌ Nested case for multiple conditions
|
|
41
|
+
case fetch_user(id) do
|
|
42
|
+
{:ok, user} ->
|
|
43
|
+
case validate(user) do
|
|
44
|
+
{:ok, valid_user} -> save(valid_user)
|
|
45
|
+
{:error, reason} -> {:error, reason}
|
|
46
|
+
end
|
|
47
|
+
{:error, reason} -> {:error, reason}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# ✅ — with clause
|
|
51
|
+
with {:ok, user} <- fetch_user(id),
|
|
52
|
+
{:ok, valid_user} <- validate(user) do
|
|
53
|
+
save(valid_user)
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```elixir
|
|
58
|
+
# ❌ if/else for known shapes
|
|
59
|
+
def area(shape) do
|
|
60
|
+
if shape.type == :circle do
|
|
61
|
+
:math.pi() * shape.radius * shape.radius
|
|
62
|
+
else
|
|
63
|
+
shape.width * shape.height
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# ✅ — multi-clause function with pattern match
|
|
68
|
+
def area(%{type: :circle, radius: r}), do: :math.pi() * r * r
|
|
69
|
+
def area(%{type: :rect, width: w, height: h}), do: w * h
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```elixir
|
|
73
|
+
# ❌ Checking type at runtime
|
|
74
|
+
def process(x) do
|
|
75
|
+
if is_integer(x) and x > 0 do
|
|
76
|
+
x * 2
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# ✅ — guard clause
|
|
81
|
+
def process(x) when is_integer(x) and x > 0, do: x * 2
|
|
82
|
+
def process(_), do: {:error, :invalid_input}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 2. Pipe Operator & Transforms {#pipes}
|
|
88
|
+
|
|
89
|
+
```elixir
|
|
90
|
+
# ❌ Nested function calls
|
|
91
|
+
String.trim(String.downcase(String.replace(input, ~r/\s+/, " ")))
|
|
92
|
+
|
|
93
|
+
# ✅
|
|
94
|
+
input
|
|
95
|
+
|> String.replace(~r/\s+/, " ")
|
|
96
|
+
|> String.downcase()
|
|
97
|
+
|> String.trim()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```elixir
|
|
101
|
+
# ❌ Pipe into anonymous function awkwardly
|
|
102
|
+
data
|
|
103
|
+
|> (fn x -> x * 2 end).()
|
|
104
|
+
|
|
105
|
+
# ✅ — use then/1 or named function
|
|
106
|
+
data
|
|
107
|
+
|> then(&(&1 * 2))
|
|
108
|
+
# or better: extract a named function
|
|
109
|
+
data |> double()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```elixir
|
|
113
|
+
# ❌ Single-step pipe (no gain in readability)
|
|
114
|
+
result = list |> Enum.count()
|
|
115
|
+
|
|
116
|
+
# ✅ — direct call for single operation
|
|
117
|
+
result = Enum.count(list)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Pipe when 2+ transforms. Direct call for single operation. First arg flows through pipe.**
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 3. Processes & OTP {#otp}
|
|
125
|
+
|
|
126
|
+
```elixir
|
|
127
|
+
# ❌ Raw spawn for stateful process
|
|
128
|
+
pid = spawn(fn -> loop(%{count: 0}) end)
|
|
129
|
+
send(pid, {:increment})
|
|
130
|
+
|
|
131
|
+
# ✅ — GenServer for stateful processes
|
|
132
|
+
defmodule Counter do
|
|
133
|
+
use GenServer
|
|
134
|
+
|
|
135
|
+
def start_link(init \\ 0), do: GenServer.start_link(__MODULE__, init)
|
|
136
|
+
def increment(pid), do: GenServer.call(pid, :increment)
|
|
137
|
+
|
|
138
|
+
@impl true
|
|
139
|
+
def init(count), do: {:ok, count}
|
|
140
|
+
|
|
141
|
+
@impl true
|
|
142
|
+
def handle_call(:increment, _from, count), do: {:reply, count + 1, count + 1}
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```elixir
|
|
147
|
+
# ❌ Spawning without linking (orphan process on crash)
|
|
148
|
+
spawn(fn -> do_work() end)
|
|
149
|
+
|
|
150
|
+
# ✅ — Task for fire-and-forget with supervision
|
|
151
|
+
Task.start(fn -> do_work() end)
|
|
152
|
+
# or for awaitable result:
|
|
153
|
+
task = Task.async(fn -> do_work() end)
|
|
154
|
+
result = Task.await(task)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```elixir
|
|
158
|
+
# ❌ Manual process registry
|
|
159
|
+
Process.register(self(), :my_worker)
|
|
160
|
+
|
|
161
|
+
# ✅ — use Registry or named GenServer
|
|
162
|
+
{:ok, _} = Registry.start_link(keys: :unique, name: MyRegistry)
|
|
163
|
+
GenServer.start_link(Worker, arg, name: {:via, Registry, {MyRegistry, :my_worker}})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```elixir
|
|
167
|
+
# ❌ try/catch in GenServer (breaks supervision)
|
|
168
|
+
def handle_call(:work, _from, state) do
|
|
169
|
+
try do
|
|
170
|
+
result = risky_operation()
|
|
171
|
+
{:reply, result, state}
|
|
172
|
+
catch
|
|
173
|
+
_ -> {:reply, :error, state}
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# ✅ — let it crash; supervisor restarts
|
|
178
|
+
def handle_call(:work, _from, state) do
|
|
179
|
+
result = risky_operation()
|
|
180
|
+
{:reply, result, state}
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**"Let it crash" — supervisors handle recovery. Don't defensively catch inside GenServers.**
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 4. Error Handling {#errors}
|
|
189
|
+
|
|
190
|
+
```elixir
|
|
191
|
+
# ❌ Raising for expected failures
|
|
192
|
+
def find_user(id) do
|
|
193
|
+
case Repo.get(User, id) do
|
|
194
|
+
nil -> raise "User not found"
|
|
195
|
+
user -> user
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# ✅ — tagged tuples for expected outcomes
|
|
200
|
+
def find_user(id) do
|
|
201
|
+
case Repo.get(User, id) do
|
|
202
|
+
nil -> {:error, :not_found}
|
|
203
|
+
user -> {:ok, user}
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```elixir
|
|
209
|
+
# ❌ Ignoring error tuple
|
|
210
|
+
{:ok, result} = might_fail() # crashes on {:error, _}
|
|
211
|
+
|
|
212
|
+
# ✅ — handle both cases
|
|
213
|
+
case might_fail() do
|
|
214
|
+
{:ok, result} -> process(result)
|
|
215
|
+
{:error, reason} -> Logger.error("Failed: #{inspect(reason)}")
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```elixir
|
|
220
|
+
# ❌ String errors
|
|
221
|
+
{:error, "something went wrong"}
|
|
222
|
+
|
|
223
|
+
# ✅ — atom or struct errors (matchable, cheap)
|
|
224
|
+
{:error, :timeout}
|
|
225
|
+
{:error, %ValidationError{field: :email, reason: :invalid_format}}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```elixir
|
|
229
|
+
# ❌ Deep nesting of ok/error checks
|
|
230
|
+
case step1() do
|
|
231
|
+
{:ok, a} ->
|
|
232
|
+
case step2(a) do
|
|
233
|
+
{:ok, b} ->
|
|
234
|
+
case step3(b) do
|
|
235
|
+
{:ok, c} -> {:ok, c}
|
|
236
|
+
error -> error
|
|
237
|
+
end
|
|
238
|
+
error -> error
|
|
239
|
+
end
|
|
240
|
+
error -> error
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# ✅
|
|
244
|
+
with {:ok, a} <- step1(),
|
|
245
|
+
{:ok, b} <- step2(a),
|
|
246
|
+
{:ok, c} <- step3(b) do
|
|
247
|
+
{:ok, c}
|
|
248
|
+
else
|
|
249
|
+
{:error, reason} -> {:error, reason}
|
|
250
|
+
end
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 5. Collections & Enum {#collections}
|
|
256
|
+
|
|
257
|
+
```elixir
|
|
258
|
+
# ❌ Multiple passes when one suffices
|
|
259
|
+
items
|
|
260
|
+
|> Enum.filter(&(&1.active))
|
|
261
|
+
|> Enum.map(&(&1.name))
|
|
262
|
+
|
|
263
|
+
# ✅ — for comprehension when filter + transform
|
|
264
|
+
for %{active: true, name: name} <- items, do: name
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```elixir
|
|
268
|
+
# ❌ Enum.count for empty check (traverses whole list)
|
|
269
|
+
if Enum.count(list) == 0, do: :empty
|
|
270
|
+
|
|
271
|
+
# ✅
|
|
272
|
+
if Enum.empty?(list), do: :empty
|
|
273
|
+
# or pattern match:
|
|
274
|
+
case list do
|
|
275
|
+
[] -> :empty
|
|
276
|
+
_ -> :has_items
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
```elixir
|
|
281
|
+
# ❌ Building map with Enum.reduce when Map.new works
|
|
282
|
+
Enum.reduce(users, %{}, fn user, acc -> Map.put(acc, user.id, user) end)
|
|
283
|
+
|
|
284
|
+
# ✅
|
|
285
|
+
Map.new(users, &{&1.id, &1})
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```elixir
|
|
289
|
+
# ❌ Enum on large dataset (eager — builds intermediate lists)
|
|
290
|
+
huge_list
|
|
291
|
+
|> Enum.map(&transform/1)
|
|
292
|
+
|> Enum.filter(&valid?/1)
|
|
293
|
+
|> Enum.take(10)
|
|
294
|
+
|
|
295
|
+
# ✅ — Stream for lazy evaluation
|
|
296
|
+
huge_list
|
|
297
|
+
|> Stream.map(&transform/1)
|
|
298
|
+
|> Stream.filter(&valid?/1)
|
|
299
|
+
|> Enum.take(10)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Use `Stream` when chaining transforms on large/infinite collections. `Enum` for small or final step.**
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 6. Structs & Protocols {#structs}
|
|
307
|
+
|
|
308
|
+
```elixir
|
|
309
|
+
# ❌ Plain map for domain entities
|
|
310
|
+
user = %{name: "Alice", email: "a@b.com", age: 30}
|
|
311
|
+
# typo in key goes unnoticed: user.emaail
|
|
312
|
+
|
|
313
|
+
# ✅ — struct enforces keys
|
|
314
|
+
defmodule User do
|
|
315
|
+
@enforce_keys [:name, :email]
|
|
316
|
+
defstruct [:name, :email, age: 0]
|
|
317
|
+
end
|
|
318
|
+
user = %User{name: "Alice", email: "a@b.com"}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```elixir
|
|
322
|
+
# ❌ Protocol with only one implementation (over-abstraction)
|
|
323
|
+
defprotocol Renderable do
|
|
324
|
+
def render(data)
|
|
325
|
+
end
|
|
326
|
+
defimpl Renderable, for: HtmlPage do ... end
|
|
327
|
+
|
|
328
|
+
# ✅ — just a function until you need polymorphism
|
|
329
|
+
def render(%HtmlPage{} = page), do: ...
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
```elixir
|
|
333
|
+
# ❌ Updating nested struct manually
|
|
334
|
+
updated = %{user | address: %{user.address | city: "NYC"}}
|
|
335
|
+
|
|
336
|
+
# ✅
|
|
337
|
+
updated = put_in(user.address.city, "NYC")
|
|
338
|
+
# or Kernel.update_in/3 for transforms
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 7. Anti-patterns specific to Elixir/Erlang {#antipatterns}
|
|
344
|
+
|
|
345
|
+
| Anti-pattern | Preferred |
|
|
346
|
+
|---|---|
|
|
347
|
+
| `spawn` without link/monitor | `Task.start_link` or `GenServer` |
|
|
348
|
+
| `try/catch` inside GenServer | let it crash; supervisor restarts |
|
|
349
|
+
| String error reasons | atom or struct errors |
|
|
350
|
+
| `Enum.count(x) == 0` | `Enum.empty?(x)` or `match?([], x)` |
|
|
351
|
+
| Mutable-style accumulator | `Enum.reduce` / recursion |
|
|
352
|
+
| `if/else` chain on data shape | multi-clause function + pattern match |
|
|
353
|
+
| Nested `case` for ok/error | `with` expression |
|
|
354
|
+
| `IO.inspect` left in prod | `Logger` with levels |
|
|
355
|
+
| Single-step pipe | direct function call |
|
|
356
|
+
| `Enum` on huge/infinite data | `Stream` |
|
|
357
|
+
| Raw PID passing | named processes / Registry |
|
|
358
|
+
| Boolean returns for success/fail | `{:ok, val}` / `{:error, reason}` tuples |
|
|
359
|
+
| `length(list) > 0` (O(n)) | pattern match `[_ | _]` |
|
|
360
|
+
| Shared mutable state via ETS without wrapper | GenServer or Agent as access layer |
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
## Limitations
|
|
365
|
+
- These are language-specific guidelines and do not cover overall architectural decisions.
|
|
366
|
+
- Over-compression might reduce readability; apply judgement.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: go
|
|
3
|
+
description: "Language-specific super-code guidelines for go."
|
|
4
|
+
risk: safe
|
|
5
|
+
source: community
|
|
6
|
+
date_added: "2026-06-16"
|
|
7
|
+
---
|
|
8
|
+
# Go: Idiomatic Efficiency Reference
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
1. [Error Handling](#errors)
|
|
12
|
+
2. [Slices & Maps](#slices)
|
|
13
|
+
3. [Goroutines & Channels](#concurrency)
|
|
14
|
+
4. [Structs & Interfaces](#structs)
|
|
15
|
+
5. [Functions & Closures](#functions)
|
|
16
|
+
6. [Anti-patterns specific to Go](#antipatterns)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Error Handling {#errors}
|
|
21
|
+
|
|
22
|
+
```go
|
|
23
|
+
// ❌ Ignoring errors
|
|
24
|
+
result, _ := os.Open(path)
|
|
25
|
+
|
|
26
|
+
// ✅ — always handle; only use _ when error is provably irrelevant
|
|
27
|
+
result, err := os.Open(path)
|
|
28
|
+
if err != nil {
|
|
29
|
+
return fmt.Errorf("open %s: %w", path, err)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```go
|
|
34
|
+
// ❌ Redundant error variable
|
|
35
|
+
err := doA()
|
|
36
|
+
if err != nil { return err }
|
|
37
|
+
err = doB()
|
|
38
|
+
if err != nil { return err }
|
|
39
|
+
|
|
40
|
+
// ✅ — each :=/: is fine; this is idiomatic Go. Don't try to "fix" it.
|
|
41
|
+
// What you CAN simplify: collapsing to one-liners where the if body is a single return
|
|
42
|
+
if err := doA(); err != nil { return err }
|
|
43
|
+
if err := doB(); err != nil { return err }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```go
|
|
47
|
+
// ❌ Custom error type with no added value
|
|
48
|
+
type MyError struct{ msg string }
|
|
49
|
+
func (e MyError) Error() string { return e.msg }
|
|
50
|
+
|
|
51
|
+
// ✅ — use errors.New or fmt.Errorf unless callers need to inspect type
|
|
52
|
+
var ErrNotFound = errors.New("not found")
|
|
53
|
+
return fmt.Errorf("lookup %q: %w", key, ErrNotFound)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Wrap errors with `%w` (not `%v`) so callers can use `errors.Is` / `errors.As`.**
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. Slices & Maps {#slices}
|
|
61
|
+
|
|
62
|
+
```go
|
|
63
|
+
// ❌ Growing a slice without pre-allocation when size is known
|
|
64
|
+
var result []string
|
|
65
|
+
for _, item := range items {
|
|
66
|
+
result = append(result, item.Name)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ✅
|
|
70
|
+
result := make([]string, 0, len(items))
|
|
71
|
+
for _, item := range items {
|
|
72
|
+
result = append(result, item.Name)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```go
|
|
77
|
+
// ❌ Manual existence check before map write
|
|
78
|
+
if _, ok := m[key]; !ok {
|
|
79
|
+
m[key] = []string{}
|
|
80
|
+
}
|
|
81
|
+
m[key] = append(m[key], value)
|
|
82
|
+
|
|
83
|
+
// ✅ — append to nil slice is valid Go
|
|
84
|
+
m[key] = append(m[key], value)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```go
|
|
88
|
+
// ❌ Copying a map by assignment (copies reference)
|
|
89
|
+
copy := original
|
|
90
|
+
|
|
91
|
+
// ✅
|
|
92
|
+
copy := make(map[K]V, len(original))
|
|
93
|
+
for k, v := range original { copy[k] = v }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 3. Goroutines & Channels {#concurrency}
|
|
99
|
+
|
|
100
|
+
```go
|
|
101
|
+
// ❌ Fire-and-forget goroutine with no lifecycle
|
|
102
|
+
go doWork()
|
|
103
|
+
|
|
104
|
+
// ✅ — use errgroup or WaitGroup to track completion
|
|
105
|
+
var wg sync.WaitGroup
|
|
106
|
+
wg.Add(1)
|
|
107
|
+
go func() {
|
|
108
|
+
defer wg.Done()
|
|
109
|
+
doWork()
|
|
110
|
+
}()
|
|
111
|
+
wg.Wait()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```go
|
|
115
|
+
// ❌ Unbuffered channel causing unnecessary goroutine block
|
|
116
|
+
ch := make(chan Result)
|
|
117
|
+
go func() { ch <- compute() }()
|
|
118
|
+
result := <-ch
|
|
119
|
+
|
|
120
|
+
// ✅ — for single-result, buffered channel avoids goroutine leak if receiver exits early
|
|
121
|
+
ch := make(chan Result, 1)
|
|
122
|
+
go func() { ch <- compute() }()
|
|
123
|
+
result := <-ch
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```go
|
|
127
|
+
// ❌ select with a busy-wait default
|
|
128
|
+
for {
|
|
129
|
+
select {
|
|
130
|
+
case v := <-ch:
|
|
131
|
+
process(v)
|
|
132
|
+
default:
|
|
133
|
+
// spin
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ✅ — blocking select unless you genuinely need non-blocking
|
|
138
|
+
for v := range ch {
|
|
139
|
+
process(v)
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Use `golang.org/x/sync/errgroup` for fan-out with error collection.**
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 4. Structs & Interfaces {#structs}
|
|
148
|
+
|
|
149
|
+
```go
|
|
150
|
+
// ❌ Large interface
|
|
151
|
+
type Storage interface {
|
|
152
|
+
Get(key string) ([]byte, error)
|
|
153
|
+
Set(key string, val []byte) error
|
|
154
|
+
Delete(key string) error
|
|
155
|
+
List(prefix string) ([]string, error)
|
|
156
|
+
// ... 10 more methods
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ✅ — small, composable interfaces
|
|
160
|
+
type Getter interface { Get(key string) ([]byte, error) }
|
|
161
|
+
type Setter interface { Set(key string, val []byte) error }
|
|
162
|
+
type Storage interface { Getter; Setter }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```go
|
|
166
|
+
// ❌ Returning concrete struct from constructor (ties callers to implementation)
|
|
167
|
+
func NewStore() *RedisStore { ... }
|
|
168
|
+
|
|
169
|
+
// ✅ — return interface when you have or anticipate multiple implementations
|
|
170
|
+
func NewStore() Storage { return &RedisStore{...} }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```go
|
|
174
|
+
// ❌ Pointer receiver for tiny value types
|
|
175
|
+
func (p *Point) X() float64 { return p.x }
|
|
176
|
+
|
|
177
|
+
// ✅ — value receiver for small immutable types
|
|
178
|
+
func (p Point) X() float64 { return p.x }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Rule: pointer receiver when method mutates state OR struct is large (>3 fields of non-trivial size). Value receiver otherwise.**
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 5. Functions & Closures {#functions}
|
|
186
|
+
|
|
187
|
+
```go
|
|
188
|
+
// ❌ Named return values used just to avoid a variable declaration
|
|
189
|
+
func divide(a, b float64) (result float64, err error) {
|
|
190
|
+
result = a / b
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ✅ — named returns are worth it only for deferred mutation or documentation
|
|
195
|
+
func divide(a, b float64) (float64, error) {
|
|
196
|
+
if b == 0 { return 0, errors.New("division by zero") }
|
|
197
|
+
return a / b, nil
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```go
|
|
202
|
+
// ❌ Closure capturing loop variable (classic Go bug, fixed in Go 1.22+)
|
|
203
|
+
// Pre-1.22: each goroutine captures the same i
|
|
204
|
+
for i := 0; i < n; i++ {
|
|
205
|
+
go func() { use(i) }()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ✅ (Go <1.22 — pass as parameter)
|
|
209
|
+
for i := 0; i < n; i++ {
|
|
210
|
+
go func(i int) { use(i) }(i)
|
|
211
|
+
}
|
|
212
|
+
// Go 1.22+: loop variable scoped per iteration, so the original is safe
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 6. Anti-patterns specific to Go {#antipatterns}
|
|
218
|
+
|
|
219
|
+
| Anti-pattern | Preferred |
|
|
220
|
+
|---|---|
|
|
221
|
+
| `if err != nil { return err }` repeated 5+ times | acceptable — it's idiomatic Go |
|
|
222
|
+
| `panic` for expected errors | `return err` |
|
|
223
|
+
| `init()` with side effects | explicit initialization in `main` or constructors |
|
|
224
|
+
| `interface{}` / `any` without generics | use generics (Go 1.18+) or typed interfaces |
|
|
225
|
+
| Mutex field not adjacent to the data it protects | put `mu` directly above the field it guards |
|
|
226
|
+
| Channel of channels | usually a sign of over-engineering; redesign |
|
|
227
|
+
| `time.Sleep` in tests | use `testing` hooks or channels for synchronization |
|
|
228
|
+
| Exported types with unexported fields (when fields are the whole point) | `record`-style structs with all-exported fields |
|
|
229
|
+
| `log.Fatal` outside `main` | return errors up the stack |
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
## Limitations
|
|
233
|
+
- These are language-specific guidelines and do not cover overall architectural decisions.
|
|
234
|
+
- Over-compression might reduce readability; apply judgement.
|