@coralai/sps-cli 0.42.0 → 0.43.0

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.
Files changed (109) hide show
  1. package/README.md +34 -3
  2. package/dist/commands/projectInit.d.ts.map +1 -1
  3. package/dist/commands/projectInit.js +40 -53
  4. package/dist/commands/projectInit.js.map +1 -1
  5. package/dist/commands/skillCommand.d.ts +2 -0
  6. package/dist/commands/skillCommand.d.ts.map +1 -0
  7. package/dist/commands/skillCommand.js +235 -0
  8. package/dist/commands/skillCommand.js.map +1 -0
  9. package/dist/core/skillStore.d.ts +46 -0
  10. package/dist/core/skillStore.d.ts.map +1 -0
  11. package/dist/core/skillStore.js +197 -0
  12. package/dist/core/skillStore.js.map +1 -0
  13. package/dist/core/skillStore.test.d.ts +2 -0
  14. package/dist/core/skillStore.test.d.ts.map +1 -0
  15. package/dist/core/skillStore.test.js +190 -0
  16. package/dist/core/skillStore.test.js.map +1 -0
  17. package/dist/main.js +19 -17
  18. package/dist/main.js.map +1 -1
  19. package/package.json +1 -1
  20. package/skills/architecture-decision-records/SKILL.md +207 -0
  21. package/skills/backend/SKILL.md +62 -0
  22. package/skills/backend/references/api-design.md +168 -0
  23. package/skills/backend/references/caching.md +181 -0
  24. package/skills/backend/references/data-access.md +173 -0
  25. package/skills/backend/references/layering.md +181 -0
  26. package/skills/backend/references/observability.md +190 -0
  27. package/skills/backend/references/resilience.md +201 -0
  28. package/skills/backend/references/security.md +186 -0
  29. package/skills/backend-architect/SKILL.md +119 -0
  30. package/skills/code-reviewer/SKILL.md +143 -0
  31. package/skills/coding-standards/SKILL.md +60 -0
  32. package/skills/coding-standards/references/clean-code.md +258 -0
  33. package/skills/coding-standards/references/code-review.md +192 -0
  34. package/skills/coding-standards/references/commits-and-prs.md +226 -0
  35. package/skills/coding-standards/references/error-strategy.md +193 -0
  36. package/skills/coding-standards/references/naming.md +185 -0
  37. package/skills/coding-standards/references/tdd.md +171 -0
  38. package/skills/database/SKILL.md +53 -0
  39. package/skills/database/references/indexing.md +190 -0
  40. package/skills/database/references/migrations.md +199 -0
  41. package/skills/database/references/nosql.md +185 -0
  42. package/skills/database/references/queries.md +295 -0
  43. package/skills/database/references/scaling.md +203 -0
  44. package/skills/database/references/schema.md +191 -0
  45. package/skills/database-optimizer/SKILL.md +168 -0
  46. package/skills/debugging-workflow/SKILL.md +244 -0
  47. package/skills/devops/SKILL.md +55 -0
  48. package/skills/devops/references/ci-cd.md +204 -0
  49. package/skills/devops/references/containers.md +272 -0
  50. package/skills/devops/references/deploy.md +201 -0
  51. package/skills/devops/references/iac.md +252 -0
  52. package/skills/devops/references/observability.md +228 -0
  53. package/skills/devops/references/secrets.md +178 -0
  54. package/skills/devops-automator/SKILL.md +164 -0
  55. package/skills/frontend/SKILL.md +52 -0
  56. package/skills/frontend/references/accessibility.md +222 -0
  57. package/skills/frontend/references/components.md +206 -0
  58. package/skills/frontend/references/performance.md +219 -0
  59. package/skills/frontend/references/routing.md +209 -0
  60. package/skills/frontend/references/state.md +190 -0
  61. package/skills/frontend/references/testing.md +216 -0
  62. package/skills/frontend-developer/SKILL.md +115 -0
  63. package/skills/git-workflow/SKILL.md +355 -0
  64. package/skills/golang/SKILL.md +49 -0
  65. package/skills/golang/references/concurrency.md +284 -0
  66. package/skills/golang/references/errors.md +241 -0
  67. package/skills/golang/references/idioms.md +285 -0
  68. package/skills/golang/references/testing.md +238 -0
  69. package/skills/java/SKILL.md +50 -0
  70. package/skills/java/references/concurrency.md +194 -0
  71. package/skills/java/references/idioms.md +283 -0
  72. package/skills/java/references/testing.md +228 -0
  73. package/skills/kotlin/SKILL.md +47 -0
  74. package/skills/kotlin/references/coroutines.md +240 -0
  75. package/skills/kotlin/references/idioms.md +268 -0
  76. package/skills/kotlin/references/testing.md +219 -0
  77. package/skills/mobile/SKILL.md +50 -0
  78. package/skills/mobile/references/architecture.md +204 -0
  79. package/skills/mobile/references/navigation.md +158 -0
  80. package/skills/mobile/references/performance.md +152 -0
  81. package/skills/mobile/references/platform.md +166 -0
  82. package/skills/mobile/references/state-and-data.md +174 -0
  83. package/skills/python/SKILL.md +51 -0
  84. package/skills/python/THIRD_PARTY.md +14 -0
  85. package/skills/python/references/async.md +218 -0
  86. package/skills/python/references/error-handling.md +254 -0
  87. package/skills/python/references/idioms.md +279 -0
  88. package/skills/python/references/packaging.md +233 -0
  89. package/skills/python/references/testing.md +269 -0
  90. package/skills/python/references/typing.md +292 -0
  91. package/skills/qa-tester/SKILL.md +186 -0
  92. package/skills/rust/SKILL.md +50 -0
  93. package/skills/rust/references/async.md +224 -0
  94. package/skills/rust/references/errors.md +240 -0
  95. package/skills/rust/references/ownership.md +263 -0
  96. package/skills/rust/references/testing.md +274 -0
  97. package/skills/rust/references/traits.md +250 -0
  98. package/skills/security-engineer/SKILL.md +157 -0
  99. package/skills/swift/SKILL.md +48 -0
  100. package/skills/swift/references/concurrency.md +280 -0
  101. package/skills/swift/references/idioms.md +334 -0
  102. package/skills/swift/references/testing.md +229 -0
  103. package/skills/typescript/SKILL.md +51 -0
  104. package/skills/typescript/references/async.md +241 -0
  105. package/skills/typescript/references/errors.md +208 -0
  106. package/skills/typescript/references/idioms.md +246 -0
  107. package/skills/typescript/references/testing.md +225 -0
  108. package/skills/typescript/references/tooling.md +208 -0
  109. package/skills/typescript/references/types.md +259 -0
@@ -0,0 +1,285 @@
1
+ # Go — Idioms
2
+
3
+ Package layout, naming, zero values, struct embedding, slices.
4
+
5
+ ## Package layout
6
+
7
+ ```
8
+ myapp/
9
+ ├── go.mod
10
+ ├── cmd/
11
+ │ └── myapp/
12
+ │ └── main.go # only package main; minimal
13
+ ├── internal/ # not importable from outside
14
+ │ ├── user/
15
+ │ │ ├── user.go
16
+ │ │ ├── service.go
17
+ │ │ └── service_test.go
18
+ │ └── http/
19
+ │ └── handler.go
20
+ ├── pkg/ # public, importable (rare for apps)
21
+ └── api/ # protobufs, openapi
22
+ ```
23
+
24
+ Rules:
25
+ - **`internal/`** forbids import from other modules — use it generously.
26
+ - **`pkg/`** is for libraries you intend to publish. Most apps don't need it.
27
+ - **No `util/`, `common/`, `helpers/`.** Name packages by what they provide: `auth`, `billing`, `httpx`.
28
+
29
+ ## Package names
30
+
31
+ Lowercase, short, singular, no underscores, no mixed case.
32
+
33
+ ```
34
+ ✅ auth, billing, httpx, db, user
35
+ ❌ auth_package, userService, UTILS, libCommon
36
+ ```
37
+
38
+ Import name = package name. If the directory name differs, aliasing breaks grep. Keep them aligned.
39
+
40
+ ## Exported vs. unexported
41
+
42
+ Capital letter = exported. Lowercase = package-private.
43
+
44
+ ```go
45
+ type User struct { // exported
46
+ ID string // exported field
47
+ email string // unexported
48
+ }
49
+
50
+ func (u *User) Email() string { return u.email }
51
+ ```
52
+
53
+ Don't expose fields if an accessor keeps an invariant. Don't add accessors for pure data types.
54
+
55
+ ## Zero values are usable
56
+
57
+ Design types so the zero value is safe and meaningful.
58
+
59
+ ```go
60
+ var m sync.Mutex // ready to use
61
+ var b bytes.Buffer // ready to use
62
+
63
+ type Counter struct { // design for zero-value usability
64
+ n int
65
+ }
66
+ func (c *Counter) Inc() { c.n++ }
67
+ ```
68
+
69
+ Avoid "must call New()" types. If construction requires validation, make `New` return an error and keep the exported type private if needed.
70
+
71
+ ## Struct literals — use field names
72
+
73
+ ```go
74
+ // ❌
75
+ u := User{"u1", "a@x.com", true}
76
+
77
+ // ✅
78
+ u := User{
79
+ ID: "u1",
80
+ Email: "a@x.com",
81
+ Active: true,
82
+ }
83
+ ```
84
+
85
+ Positional literals break silently on field reorders or additions.
86
+
87
+ ## Composition over inheritance — embedding
88
+
89
+ ```go
90
+ type Logger struct { /* ... */ }
91
+ func (l *Logger) Log(msg string) { /* ... */ }
92
+
93
+ type Service struct {
94
+ *Logger // embedded; Service.Log is promoted
95
+ db *sql.DB
96
+ }
97
+ ```
98
+
99
+ Good for "is-a" composition and interface satisfaction. Don't use embedding to shortcut field access across unrelated types — that's confusion dressed as reuse.
100
+
101
+ ## Interfaces — small, at the call site
102
+
103
+ ```go
104
+ // ✅ in the caller's package
105
+ package service
106
+
107
+ type userRepo interface {
108
+ Find(ctx context.Context, id string) (*User, error)
109
+ }
110
+
111
+ type Service struct {
112
+ repo userRepo
113
+ }
114
+ ```
115
+
116
+ The *io.Reader* lesson: one-method interfaces are the norm. `Stringer`, `io.Reader`, `io.Closer`, `error` — all one method.
117
+
118
+ ## Accept interfaces, return structs
119
+
120
+ ```go
121
+ // ✅
122
+ func NewService(r Reader) *Service { ... } // accepts an interface
123
+ func (s *Service) Get() (*User, error) { ... } // returns a concrete type
124
+
125
+ // ❌
126
+ func NewService(r *os.File) ... // over-constrained
127
+ func (s *Service) Get() (fmt.Stringer, error) // under-specified return
128
+ ```
129
+
130
+ The caller decides what abstraction they want; you shouldn't force one on them.
131
+
132
+ ## Slices — the common traps
133
+
134
+ ### Share backing array
135
+
136
+ ```go
137
+ a := []int{1, 2, 3, 4}
138
+ b := a[:2] // [1 2]; shares backing with a
139
+ b = append(b, 99) // may overwrite a[2]
140
+ ```
141
+
142
+ When you need independent slices, copy:
143
+
144
+ ```go
145
+ b := append([]int(nil), a[:2]...) // new backing array
146
+ ```
147
+
148
+ ### Preallocate capacity
149
+
150
+ ```go
151
+ // ✅
152
+ out := make([]T, 0, len(xs))
153
+ for _, x := range xs { out = append(out, f(x)) }
154
+
155
+ // ❌ grows repeatedly, each growth may realloc + copy
156
+ var out []T
157
+ for _, x := range xs { out = append(out, f(x)) }
158
+ ```
159
+
160
+ ### Nil slice vs. empty slice
161
+
162
+ Both have length 0. Both work with `range`, `len`, `append`. The difference shows up in JSON (`null` vs `[]`) and reflection-based equality. For public APIs, return `[]T{}` (empty) unless nil is semantically meaningful.
163
+
164
+ ## Maps — iteration order is randomized
165
+
166
+ Never rely on map iteration order. Sort keys first when order matters.
167
+
168
+ ```go
169
+ keys := make([]string, 0, len(m))
170
+ for k := range m { keys = append(keys, k) }
171
+ sort.Strings(keys)
172
+ for _, k := range keys { fmt.Println(k, m[k]) }
173
+ ```
174
+
175
+ ## `for range` pitfalls
176
+
177
+ Until Go 1.22, the loop variable was shared — a common source of goroutine bugs:
178
+
179
+ ```go
180
+ // Go < 1.22 — BUG
181
+ for _, x := range xs {
182
+ go func() { use(x) }() // all goroutines see the last x
183
+ }
184
+
185
+ // Go < 1.22 — FIX
186
+ for _, x := range xs {
187
+ x := x // shadow into a new variable
188
+ go func() { use(x) }()
189
+ }
190
+
191
+ // Go 1.22+ — per-iteration scope; works correctly without shadowing
192
+ ```
193
+
194
+ Check your `go.mod`'s `go` version.
195
+
196
+ ## `defer` — cleanup, not logic
197
+
198
+ ```go
199
+ f, err := os.Open(path)
200
+ if err != nil { return err }
201
+ defer f.Close() // ✅ cleanup
202
+ ```
203
+
204
+ Rules:
205
+ - `defer` runs in reverse order.
206
+ - `defer` captures args at the `defer` site, not at call time.
207
+ - Don't defer in a hot loop — each `defer` costs an allocation; move cleanup into a helper.
208
+ - Check the error of deferred `Close` on writes (`defer f.Close()` hides the last flush error).
209
+
210
+ ```go
211
+ defer func() {
212
+ if cerr := f.Close(); cerr != nil && err == nil {
213
+ err = cerr
214
+ }
215
+ }()
216
+ ```
217
+
218
+ ## Constants & iota
219
+
220
+ ```go
221
+ const (
222
+ StatusPending = "pending"
223
+ StatusActive = "active"
224
+ )
225
+
226
+ type Day int
227
+ const (
228
+ Sun Day = iota
229
+ Mon
230
+ Tue
231
+ )
232
+ ```
233
+
234
+ Prefer typed constants (`type Day int`) for enums so compile catches wrong-type assignment.
235
+
236
+ ## Strings and bytes
237
+
238
+ `string` is immutable bytes. `[]byte` is mutable. Convert deliberately — conversions copy.
239
+
240
+ ```go
241
+ b := []byte(s) // allocates
242
+ s := string(b) // allocates
243
+
244
+ // hot path: avoid repeated conversions; keep the type you need
245
+ ```
246
+
247
+ `strings.Builder` for concatenation in a loop — `+` reallocates every time.
248
+
249
+ ## Formatting — `gofmt` / `goimports` is the law
250
+
251
+ No team style debates. `goimports` groups and sorts imports:
252
+
253
+ ```go
254
+ import (
255
+ "context"
256
+ "fmt"
257
+
258
+ "github.com/example/mod"
259
+
260
+ "myapp/internal/user"
261
+ )
262
+ ```
263
+
264
+ Standard lib → third-party → internal, separated by blank lines.
265
+
266
+ ## Receiver types — pointer vs. value
267
+
268
+ Rules of thumb:
269
+ - If the method modifies the receiver → pointer.
270
+ - If the struct contains a `sync.Mutex` or similar → pointer (never copy a lock).
271
+ - Large struct (say > 64 bytes) → pointer (avoid copy).
272
+ - Otherwise, either works. Be consistent across a type's methods — don't mix.
273
+
274
+ ## Anti-patterns
275
+
276
+ | Anti-pattern | Fix |
277
+ |---|---|
278
+ | Getters/setters for plain data | Exported fields; only add methods when behaviour is needed |
279
+ | `init()` doing work | Do it explicitly in `main` |
280
+ | Variable shadowing across scopes | `go vet` catches the bad cases; pay attention |
281
+ | Using `interface{}` / `any` everywhere | Reach for concrete types first |
282
+ | Long files (>500 lines) | Split by responsibility, not by arbitrary size |
283
+ | `len(s) > 0` to check non-empty | `s != ""` is the idiom |
284
+ | Custom `String()` with side effects | `fmt` calls it at any time; must be pure |
285
+ | "Manager", "Util", "Helper" names | Name by what the type actually is |
@@ -0,0 +1,238 @@
1
+ # Go — Testing
2
+
3
+ `testing` package, table-driven tests, subtests, benchmarks, fuzzing. For general TDD, see `coding-standards/references/tdd.md`.
4
+
5
+ ## Conventions
6
+
7
+ - File: `*_test.go` next to the code it tests.
8
+ - Package: same as the code (`package user`) for white-box; `package user_test` for black-box (public API only).
9
+ - Test function: `func TestXxx(t *testing.T)`.
10
+ - Run: `go test ./...`
11
+
12
+ ```go
13
+ // user_test.go
14
+ package user
15
+
16
+ import "testing"
17
+
18
+ func TestNormalizeEmail(t *testing.T) {
19
+ got := Normalize(" A@X.COM ")
20
+ want := "a@x.com"
21
+ if got != want {
22
+ t.Errorf("Normalize: got %q, want %q", got, want)
23
+ }
24
+ }
25
+ ```
26
+
27
+ `t.Errorf` logs and continues. `t.Fatalf` stops the test.
28
+
29
+ ## Table-driven tests
30
+
31
+ The idiomatic way.
32
+
33
+ ```go
34
+ func TestAdd(t *testing.T) {
35
+ tests := []struct {
36
+ name string
37
+ a, b int
38
+ want int
39
+ }{
40
+ {"two positives", 2, 3, 5},
41
+ {"zero", 0, 0, 0},
42
+ {"negative", -1, 1, 0},
43
+ }
44
+ for _, tt := range tests {
45
+ tt := tt // shadow for Go < 1.22
46
+ t.Run(tt.name, func(t *testing.T) {
47
+ if got := Add(tt.a, tt.b); got != tt.want {
48
+ t.Errorf("got %d, want %d", got, tt.want)
49
+ }
50
+ })
51
+ }
52
+ }
53
+ ```
54
+
55
+ Benefits: `t.Run` gives each case its own name for `-run` filtering and parallel runs.
56
+
57
+ ## `t.Parallel()`
58
+
59
+ Mark tests that can run in parallel.
60
+
61
+ ```go
62
+ func TestX(t *testing.T) {
63
+ t.Parallel()
64
+ ...
65
+ }
66
+ ```
67
+
68
+ Rules:
69
+ - Don't mutate package globals or filesystem fixtures.
70
+ - Subtests inherit parallelism; put `t.Parallel()` in each `t.Run`.
71
+ - If a test is slow or has setup cost, parallel compounds: N tests × 10× speedup = big wins.
72
+
73
+ ## Test helpers — mark with `t.Helper()`
74
+
75
+ ```go
76
+ func requireUser(t *testing.T, got, want *User) {
77
+ t.Helper() // errors report the caller's line
78
+ if got.ID != want.ID { t.Errorf("id: got %s want %s", got.ID, want.ID) }
79
+ }
80
+ ```
81
+
82
+ Without `t.Helper()`, error line points inside the helper instead of the test body.
83
+
84
+ ## `testify` — when assertions get verbose
85
+
86
+ The standard lib is fine for most cases. `testify/require` helps when you have many assertions.
87
+
88
+ ```go
89
+ import "github.com/stretchr/testify/require"
90
+
91
+ func TestUser(t *testing.T) {
92
+ u, err := Get(id)
93
+ require.NoError(t, err)
94
+ require.NotNil(t, u)
95
+ require.Equal(t, "A", u.Name)
96
+ }
97
+ ```
98
+
99
+ Use `require` (stops on fail) vs `assert` (continues). `require` avoids nil-deref panics in the next line after a failure.
100
+
101
+ Don't reach for `testify/mock` by default — prefer real fakes for repos, test HTTP servers, etc.
102
+
103
+ ## Fakes over mocks
104
+
105
+ ```go
106
+ // ✅ fake repo — behaves like the real one, in memory
107
+ type fakeUserRepo struct { users map[string]*User }
108
+ func newFakeUserRepo() *fakeUserRepo { return &fakeUserRepo{users: map[string]*User{}} }
109
+ func (r *fakeUserRepo) Find(_ context.Context, id string) (*User, error) { return r.users[id], nil }
110
+ func (r *fakeUserRepo) Save(_ context.Context, u *User) error { r.users[u.ID] = u; return nil }
111
+ ```
112
+
113
+ For HTTP dependencies, `httptest.NewServer` gives you a real server at a real port.
114
+
115
+ ```go
116
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
117
+ fmt.Fprint(w, `{"id":"u_1"}`)
118
+ }))
119
+ defer srv.Close()
120
+
121
+ client := NewClient(srv.URL)
122
+ u, _ := client.Get(ctx, "u_1")
123
+ ```
124
+
125
+ ## Golden files — for large expected output
126
+
127
+ ```go
128
+ var update = flag.Bool("update", false, "update golden files")
129
+
130
+ func TestRender(t *testing.T) {
131
+ got := Render(input)
132
+ golden := filepath.Join("testdata", "render.golden")
133
+ if *update {
134
+ os.WriteFile(golden, got, 0644)
135
+ }
136
+ want, _ := os.ReadFile(golden)
137
+ if !bytes.Equal(got, want) {
138
+ t.Errorf("render diff; run with -update to regenerate")
139
+ }
140
+ }
141
+ ```
142
+
143
+ Run `go test -update` to regenerate after deliberate changes. Review the diff like any other.
144
+
145
+ ## Benchmarks
146
+
147
+ ```go
148
+ func BenchmarkAdd(b *testing.B) {
149
+ for i := 0; i < b.N; i++ {
150
+ Add(2, 3)
151
+ }
152
+ }
153
+ ```
154
+
155
+ Run: `go test -bench=. -benchmem ./...`.
156
+
157
+ Use `b.ReportAllocs()` or `-benchmem` to track allocations; ns/op matters, allocs/op often matters more.
158
+
159
+ Always benchmark the thing you want to measure. Pre-compute inputs outside the timed loop:
160
+
161
+ ```go
162
+ func BenchmarkHash(b *testing.B) {
163
+ data := make([]byte, 1024)
164
+ rand.Read(data)
165
+ b.ResetTimer() // reset after setup
166
+ for i := 0; i < b.N; i++ {
167
+ _ = sha256.Sum256(data)
168
+ }
169
+ }
170
+ ```
171
+
172
+ ## Fuzzing (Go 1.18+)
173
+
174
+ For parsers, validators, anything with a big input space.
175
+
176
+ ```go
177
+ func FuzzParseURL(f *testing.F) {
178
+ f.Add("http://x.com")
179
+ f.Fuzz(func(t *testing.T, s string) {
180
+ u, err := Parse(s)
181
+ if err == nil && u.String() != s {
182
+ t.Errorf("roundtrip failed: %q != %q", u.String(), s)
183
+ }
184
+ })
185
+ }
186
+ ```
187
+
188
+ Run: `go test -fuzz=FuzzParseURL -fuzztime=30s`. Failing inputs are saved to `testdata/fuzz/<name>/` — commit them to prevent regressions.
189
+
190
+ ## Integration tests
191
+
192
+ Build tag or separate directory to keep them out of the default run.
193
+
194
+ ```go
195
+ //go:build integration
196
+ package integration_test
197
+
198
+ func TestDBReal(t *testing.T) { ... }
199
+ ```
200
+
201
+ Run: `go test -tags=integration ./...`.
202
+
203
+ Or: separate package under `internal/integration/` and a Makefile target.
204
+
205
+ ## Race detector in CI
206
+
207
+ Always. Always. Always.
208
+
209
+ ```
210
+ go test -race ./...
211
+ ```
212
+
213
+ ## Coverage
214
+
215
+ ```
216
+ go test -cover ./...
217
+ go test -coverprofile=c.out ./... && go tool cover -html=c.out
218
+ ```
219
+
220
+ Set a floor in CI:
221
+
222
+ ```
223
+ go test -coverprofile=c.out ./...
224
+ go tool cover -func=c.out | grep total: | awk '{if ($3+0 < 80.0) exit 1}'
225
+ ```
226
+
227
+ ## Anti-patterns
228
+
229
+ | Anti-pattern | Fix |
230
+ |---|---|
231
+ | One `Test*` per file instead of one `TestMain` | Table-driven tests fold the variants |
232
+ | `fmt.Println` in tests for debugging | `t.Logf` — only shows on failure |
233
+ | `time.Sleep` to wait for async | Use channels, `WaitGroup`, or deterministic test clock |
234
+ | Shared package-level state between tests | Reset in each test, or don't use globals |
235
+ | `testify/mock` boilerplate for everything | Hand-write a fake; less code, clearer intent |
236
+ | Snapshot tests for time / UUID output | Inject a clock / uuid generator; test against deterministic output |
237
+ | Benchmarks that don't `ResetTimer` after setup | You're benchmarking setup, not the function |
238
+ | Disabling a failing test to unblock CI | Fix or delete |
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: java
3
+ description: Java language skill — modern idioms (17+/21 LTS), records, streams, concurrency, testing. Pair with `backend` / `mobile` end skills and `coding-standards` for cross-language principles.
4
+ origin: original
5
+ ---
6
+
7
+ # Java
8
+
9
+ Modern Java (17 LTS / 21 LTS+). Records, sealed types, switch patterns, virtual threads. **Language-focused**.
10
+
11
+ ## When to load
12
+
13
+ - Project primary language is Java 17+ (LTS) or 21+ (virtual threads)
14
+ - Reviewing Java code
15
+ - Backend services (Spring Boot, Quarkus, Helidon, Micronaut)
16
+ - Android (where Kotlin isn't used)
17
+ - JVM interop with Kotlin / Scala
18
+
19
+ ## Core principles
20
+
21
+ 1. **Records for data.** Replace `@Data` Lombok classes and hand-written POJOs.
22
+ 2. **Sealed types + pattern switch for sum types.** Replace visitor pattern and `instanceof` chains.
23
+ 3. **`var` for locals only.** Never in method signatures / fields.
24
+ 4. **`Optional<T>` for return values, never for fields or parameters.**
25
+ 5. **Virtual threads (21+) for I/O-bound concurrency.** Replace manual thread pools and reactive chains where they exist only for thread efficiency.
26
+ 6. **Unmodifiable collections by default.** `List.of(...)`, `Map.of(...)`, `Collectors.toUnmodifiableList()`.
27
+ 7. **Checked exceptions for recoverable failures in libraries; runtime exceptions for programmer errors.** Don't catch-and-rethrow just to convert one kind.
28
+ 8. **`NullPointerException` is a bug.** Don't normalize it with defensive `null` checks everywhere; design so the type system documents nullability.
29
+
30
+ ## How to use references
31
+
32
+ | Reference | When to load |
33
+ |---|---|
34
+ | [`references/idioms.md`](references/idioms.md) | Records, sealed types, streams, `Optional`, collections, `var` |
35
+ | [`references/concurrency.md`](references/concurrency.md) | Virtual threads, `CompletableFuture`, `ExecutorService`, structured concurrency (preview) |
36
+ | [`references/testing.md`](references/testing.md) | JUnit 5, AssertJ, Mockito, Testcontainers |
37
+
38
+ ## Forbidden patterns (auto-reject)
39
+
40
+ - Raw types (`List` without `<T>`)
41
+ - `Vector`, `Hashtable`, `Stack` (legacy, synchronized) — use modern collections
42
+ - `new Integer(...)` etc. — use `Integer.valueOf(...)`
43
+ - `null` return where `Optional<T>` would be expressive
44
+ - Catching `Exception` / `Throwable` broadly without re-throw
45
+ - `String.format` for log messages — use parameterized SLF4J (`logger.info("x={}", x)`)
46
+ - Mutable static fields for business data
47
+ - `Thread.sleep` in request handlers
48
+ - `checkedException.wrap(RuntimeException)` without cause chain
49
+ - Anonymous inner classes where a lambda works
50
+ - `Date` / `Calendar` / `SimpleDateFormat` — use `java.time.*` (since Java 8!)