@sun-asterisk/sunlint 1.3.40 → 1.3.41

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 (67) hide show
  1. package/core/rule-selection-service.js +11 -0
  2. package/package.json +1 -1
  3. package/skill-assets/sunlint-code-quality/rules/go/C006-verb-noun-functions.md +45 -0
  4. package/skill-assets/sunlint-code-quality/rules/go/C013-no-dead-code.md +48 -0
  5. package/skill-assets/sunlint-code-quality/rules/go/C014-dependency-injection.md +85 -0
  6. package/skill-assets/sunlint-code-quality/rules/go/C017-no-constructor-logic.md +67 -0
  7. package/skill-assets/sunlint-code-quality/rules/go/C018-generic-errors.md +63 -0
  8. package/skill-assets/sunlint-code-quality/rules/go/C019-error-log-level.md +50 -0
  9. package/skill-assets/sunlint-code-quality/rules/go/C020-no-unused-imports.md +45 -0
  10. package/skill-assets/sunlint-code-quality/rules/go/C022-no-unused-variables.md +34 -0
  11. package/skill-assets/sunlint-code-quality/rules/go/C023-no-duplicate-names.md +41 -0
  12. package/skill-assets/sunlint-code-quality/rules/go/C024-centralize-constants.md +55 -0
  13. package/skill-assets/sunlint-code-quality/rules/go/C029-catch-log-root-cause.md +56 -0
  14. package/skill-assets/sunlint-code-quality/rules/go/C030-custom-error-classes.md +69 -0
  15. package/skill-assets/sunlint-code-quality/rules/go/C033-separate-data-access.md +68 -0
  16. package/skill-assets/sunlint-code-quality/rules/go/C035-error-context-logging.md +48 -0
  17. package/skill-assets/sunlint-code-quality/rules/go/C041-no-hardcoded-secrets.md +45 -0
  18. package/skill-assets/sunlint-code-quality/rules/go/C042-boolean-naming.md +42 -0
  19. package/skill-assets/sunlint-code-quality/rules/go/C052-controller-parsing.md +62 -0
  20. package/skill-assets/sunlint-code-quality/rules/go/C060-superclass-logic.md +60 -0
  21. package/skill-assets/sunlint-code-quality/rules/go/C067-no-hardcoded-config.md +51 -0
  22. package/skill-assets/sunlint-code-quality/rules/go/S003-open-redirect.md +80 -0
  23. package/skill-assets/sunlint-code-quality/rules/go/S004-no-log-credentials.md +66 -0
  24. package/skill-assets/sunlint-code-quality/rules/go/S005-server-authorization.md +55 -0
  25. package/skill-assets/sunlint-code-quality/rules/go/S006-default-credentials.md +47 -0
  26. package/skill-assets/sunlint-code-quality/rules/go/S007-output-encoding.md +50 -0
  27. package/skill-assets/sunlint-code-quality/rules/go/S009-approved-crypto.md +63 -0
  28. package/skill-assets/sunlint-code-quality/rules/go/S010-csprng.md +53 -0
  29. package/skill-assets/sunlint-code-quality/rules/go/S011-encrypted-client-hello.md +34 -0
  30. package/skill-assets/sunlint-code-quality/rules/go/S012-secrets-management.md +49 -0
  31. package/skill-assets/sunlint-code-quality/rules/go/S013-tls-connections.md +61 -0
  32. package/skill-assets/sunlint-code-quality/rules/go/S016-no-sensitive-query-string.md +42 -0
  33. package/skill-assets/sunlint-code-quality/rules/go/S017-parameterized-queries.md +36 -0
  34. package/skill-assets/sunlint-code-quality/rules/go/S019-email-input-sanitization.md +44 -0
  35. package/skill-assets/sunlint-code-quality/rules/go/S020-eval-code-execution.md +47 -0
  36. package/skill-assets/sunlint-code-quality/rules/go/S022-context-escaping.md +49 -0
  37. package/skill-assets/sunlint-code-quality/rules/go/S023-dynamic-js-encoding.md +51 -0
  38. package/skill-assets/sunlint-code-quality/rules/go/S025-server-validation.md +57 -0
  39. package/skill-assets/sunlint-code-quality/rules/go/S026-tls-encryption.md +46 -0
  40. package/skill-assets/sunlint-code-quality/rules/go/S027-mtls-validation.md +52 -0
  41. package/skill-assets/sunlint-code-quality/rules/go/S028-upload-limits.md +58 -0
  42. package/skill-assets/sunlint-code-quality/rules/go/S029-csrf-protection.md +53 -0
  43. package/skill-assets/sunlint-code-quality/rules/go/S030-directory-browsing.md +53 -0
  44. package/skill-assets/sunlint-code-quality/rules/go/S031-secure-cookie-flag.md +48 -0
  45. package/skill-assets/sunlint-code-quality/rules/go/S032-httponly-cookie.md +42 -0
  46. package/skill-assets/sunlint-code-quality/rules/go/S033-samesite-cookie.md +49 -0
  47. package/skill-assets/sunlint-code-quality/rules/go/S034-host-prefix-cookie.md +44 -0
  48. package/skill-assets/sunlint-code-quality/rules/go/S035-app-hostnames.md +50 -0
  49. package/skill-assets/sunlint-code-quality/rules/go/S036-internal-file-paths.md +56 -0
  50. package/skill-assets/sunlint-code-quality/rules/go/S037-anti-cache-headers.md +43 -0
  51. package/skill-assets/sunlint-code-quality/rules/go/S039-tls-certificate-validation.md +41 -0
  52. package/skill-assets/sunlint-code-quality/rules/go/S041-logout-invalidation.md +46 -0
  53. package/skill-assets/sunlint-code-quality/rules/go/S042-long-lived-sessions.md +58 -0
  54. package/skill-assets/sunlint-code-quality/rules/go/S044-critical-changes-reauth.md +53 -0
  55. package/skill-assets/sunlint-code-quality/rules/go/S045-brute-force-protection.md +55 -0
  56. package/skill-assets/sunlint-code-quality/rules/go/S047-oauth-csrf-protection.md +51 -0
  57. package/skill-assets/sunlint-code-quality/rules/go/S048-oauth-redirect-validation.md +58 -0
  58. package/skill-assets/sunlint-code-quality/rules/go/S049-auth-code-expiry.md +52 -0
  59. package/skill-assets/sunlint-code-quality/rules/go/S050-token-entropy.md +53 -0
  60. package/skill-assets/sunlint-code-quality/rules/go/S051-password-length.md +49 -0
  61. package/skill-assets/sunlint-code-quality/rules/go/S052-otp-entropy.md +48 -0
  62. package/skill-assets/sunlint-code-quality/rules/go/S053-generic-error-messages.md +51 -0
  63. package/skill-assets/sunlint-code-quality/rules/go/S054-no-default-admin.md +43 -0
  64. package/skill-assets/sunlint-code-quality/rules/go/S055-content-type-validation.md +52 -0
  65. package/skill-assets/sunlint-code-quality/rules/go/S056-log-injection.md +40 -0
  66. package/skill-assets/sunlint-code-quality/rules/go/S057-synchronized-time.md +40 -0
  67. package/skill-assets/sunlint-code-quality/rules/go/S058-ssrf-protection.md +70 -0
@@ -126,6 +126,17 @@ class RuleSelectionService {
126
126
  }
127
127
  }
128
128
 
129
+ // Filter out disabled rules from config (rules set to 'off', false, or 0)
130
+ const disabledRules = new Set(
131
+ Object.entries(allRules)
132
+ .filter(([, value]) => value === 'off' || value === false || value === 0)
133
+ .map(([ruleId]) => ruleId)
134
+ );
135
+
136
+ if (disabledRules.size > 0) {
137
+ selectedRules = selectedRules.filter(ruleId => !disabledRules.has(ruleId));
138
+ }
139
+
129
140
  // Convert to rule objects
130
141
  return selectedRules.map(ruleId => {
131
142
  const adapterRule = this.ruleAdapter.getRuleById(ruleId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.40",
3
+ "version": "1.3.41",
4
4
  "description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Function Names Verb-Noun
3
+ impact: LOW
4
+ impactDescription: makes code self-documenting
5
+ tags: naming, functions, readability, conventions, quality
6
+ ---
7
+
8
+ ## Function Names Verb-Noun
9
+
10
+ Functions do things. Action verbs make purpose clear.
11
+
12
+ **Incorrect (vague names):**
13
+
14
+ ```go
15
+ func user() { } // Noun only
16
+ func userData() { } // Noun only
17
+ func doSomething() { } // Vague
18
+ func handleStuff() { } // Vague
19
+ func manager() { } // Noun only
20
+ ```
21
+
22
+ **Correct (action verbs):**
23
+
24
+ ```go
25
+ func GetUser() { }
26
+ func CreateUserAccount() { }
27
+ func ValidateEmailFormat() { }
28
+ func CalculateTotalPrice() { }
29
+ func SendConfirmationEmail() { }
30
+ func ConvertCurrencyToUSD() { }
31
+ ```
32
+
33
+ **Verb categories:**
34
+
35
+ | Category | Verbs |
36
+ |----------|-------|
37
+ | Retrieval | `Get`, `Fetch`, `Find`, `Load`, `Query` |
38
+ | Creation | `Create`, `Build`, `Make`, `Generate` |
39
+ | Modification | `Set`, `Update`, `Modify`, `Change` |
40
+ | Deletion | `Delete`, `Remove`, `Destroy`, `Clear` |
41
+ | Validation | `Validate`, `Verify`, `Check`, `Ensure` |
42
+ | Computation | `Calculate`, `Compute`, `Parse`, `Format` |
43
+ | Boolean | `Is`, `Has`, `Can`, `Should`, `Will` |
44
+
45
+ **Tools:** PR review, GolangCI-Lint
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Do Not Use Dead Code
3
+ impact: LOW
4
+ impactDescription: reduces codebase noise and maintenance burden
5
+ tags: dead-code, cleanup, maintenance, quality
6
+ ---
7
+
8
+ ## Do Not Use Dead Code
9
+
10
+ Dead code confuses readers and increases cognitive load. Git history preserves deleted code.
11
+
12
+ **Incorrect (keeping dead code):**
13
+
14
+ ```go
15
+ func ProcessOrder(order *Order) float64 {
16
+ // Old implementation - keeping for reference
17
+ // total := 0.0
18
+ // for _, item := range order.Items {
19
+ // total += item.Price * float64(item.Quantity)
20
+ // }
21
+ // return total
22
+
23
+ total := calculateTotal(order)
24
+ return total
25
+ }
26
+
27
+ // Unused function - someone might need it later
28
+ func legacyCalculation() { }
29
+ ```
30
+
31
+ **Correct (clean code):**
32
+
33
+ ```go
34
+ func ProcessOrder(order *Order) float64 {
35
+ return calculateTotal(order)
36
+ }
37
+
38
+ // Delete unused functions - git history preserves them
39
+ // Delete commented code - git history preserves it
40
+ ```
41
+
42
+ **Types of dead code:**
43
+ - Commented-out code
44
+ - Unused functions/structs/methods
45
+ - Unreachable code
46
+ - Unused variables (Go compiler will error on these locally, but they may exist in long-lived branches)
47
+
48
+ **Tools:** gofmt, govet, staticcheck, GolangCI-Lint
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Use Dependency Injection
3
+ impact: HIGH
4
+ impactDescription: enables testability and loose coupling
5
+ tags: dependency-injection, testing, coupling, architecture, quality
6
+ ---
7
+
8
+ ## Use Dependency Injection
9
+
10
+ Direct instantiation creates tight coupling, making testing difficult and changes risky. DI enables mockability, replaceability, and testability.
11
+
12
+ **Incorrect (hardcoded dependencies):**
13
+
14
+ ```go
15
+ type OrderService struct {
16
+ db *PostgresDatabase
17
+ mailer *SendGridMailer
18
+ }
19
+
20
+ func NewOrderService() *OrderService {
21
+ return &OrderService{
22
+ db: NewPostgresDatabase(), // Hardcoded dependency
23
+ mailer: NewSendGridMailer(), // Hardcoded dependency
24
+ }
25
+ }
26
+
27
+ func (s *OrderService) CreateOrder(data OrderData) error {
28
+ order, err := s.db.Insert("orders", data)
29
+ if err != nil {
30
+ return err
31
+ }
32
+ return s.mailer.Send(data.Email, "Order created")
33
+ }
34
+ ```
35
+
36
+ **Correct (injected dependencies via interfaces):**
37
+
38
+ ```go
39
+ type Database interface {
40
+ Insert(table string, data any) (any, error)
41
+ }
42
+
43
+ type Mailer interface {
44
+ Send(to string, message string) error
45
+ }
46
+
47
+ type OrderService struct {
48
+ db Database
49
+ mailer Mailer
50
+ }
51
+
52
+ func NewOrderService(db Database, mailer Mailer) *OrderService {
53
+ return &OrderService{
54
+ db: db,
55
+ mailer: mailer,
56
+ }
57
+ }
58
+
59
+ func (s *OrderService) CreateOrder(data OrderData) error {
60
+ order, err := s.db.Insert("orders", data)
61
+ if err != nil {
62
+ return err
63
+ }
64
+ return s.mailer.Send(data.Email, "Order created")
65
+ }
66
+
67
+ // Usage
68
+ service := NewOrderService(
69
+ NewPostgresDatabase(connString),
70
+ NewSendGridMailer(apiKey),
71
+ )
72
+
73
+ // Testing
74
+ mockDb := new(MockDatabase)
75
+ mockMailer := new(MockMailer)
76
+ testService := NewOrderService(mockDb, mockMailer)
77
+ ```
78
+
79
+ **Benefits:**
80
+ - Easy mocking for unit tests
81
+ - Swappable implementations
82
+ - Clear dependencies visible in constructor/factory
83
+ - Supports interface-based design (accept interfaces, return structs)
84
+
85
+ **Tools:** GolangCI-Lint, Manual review
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: No Business Logic In Factory Functions
3
+ impact: HIGH
4
+ impactDescription: ensures predictable object initialization
5
+ tags: factory, initialization, side-effects, patterns, quality
6
+ ---
7
+
8
+ ## No Business Logic In Factory Functions
9
+
10
+ Factory functions (e.g., `NewService`) should only initialize state. Side effects in factory functions are unexpected and make testing difficult.
11
+
12
+ **Incorrect (logic in factory function):**
13
+
14
+ ```go
15
+ type UserService struct {
16
+ config Config
17
+ }
18
+
19
+ func NewUserService(configPath string) (*UserService, error) {
20
+ // BAD: Reading files in factory function
21
+ rawConfig, err := os.ReadFile(configPath)
22
+ if err != nil {
23
+ return nil, err
24
+ }
25
+ var config Config
26
+ json.Unmarshal(rawConfig, &config)
27
+
28
+ // BAD: Network calls/API initialization here
29
+ resp, _ := http.Get("https://api.example.com/init")
30
+
31
+ // BAD: Logging/side effects
32
+ log.Println("UserService initialized")
33
+
34
+ return &UserService{config: config}, nil
35
+ }
36
+ ```
37
+
38
+ **Correct (clear separation):**
39
+
40
+ ```go
41
+ type UserService struct {
42
+ config Config
43
+ httpClient *http.Client
44
+ }
45
+
46
+ func NewUserService(config Config, httpClient *http.Client) *UserService {
47
+ // Only assignment - no side effects
48
+ return &UserService{
49
+ config: config,
50
+ httpClient: httpClient,
51
+ }
52
+ }
53
+
54
+ // Service initialization in main or a dedicated bootstrapper
55
+ func InitializeApp() {
56
+ rawConfig, _ := os.ReadFile("./config.json")
57
+ var config Config
58
+ json.Unmarshal(rawConfig, &config)
59
+
60
+ httpClient := &http.Client{Timeout: 10 * time.Second}
61
+
62
+ service := NewUserService(config, httpClient)
63
+ log.Println("UserService ready")
64
+ }
65
+ ```
66
+
67
+ **Tools:** PR review, Manual review
@@ -0,0 +1,63 @@
1
+ ---
2
+ title: Do Not Return Generic Errors
3
+ impact: HIGH
4
+ impactDescription: enables proper error handling and monitoring
5
+ tags: error-handling, custom-errors, debugging, quality
6
+ ---
7
+
8
+ ## Do Not Return Generic Errors
9
+
10
+ Generic errors (like `errors.New("error")`) lack context needed for debugging. They make it impossible to distinguish between error types for proper handling.
11
+
12
+ **Incorrect (generic errors):**
13
+
14
+ ```go
15
+ if user == nil {
16
+ return errors.New("error")
17
+ }
18
+
19
+ if !isValid {
20
+ return fmt.Errorf("invalid")
21
+ }
22
+ ```
23
+
24
+ **Correct (specific custom errors or sentinel errors):**
25
+
26
+ ```go
27
+ // Sentinel errors for simple checks
28
+ var ErrUserNotFound = errors.New("user not found")
29
+
30
+ func (s *Service) GetUser(id string) (*User, error) {
31
+ if user == nil {
32
+ return nil, fmt.Errorf("%w: user with ID %s not found", ErrUserNotFound, id)
33
+ }
34
+ return user, nil
35
+ }
36
+
37
+ // Custom error types for complex context
38
+ type ValidationError struct {
39
+ Field string
40
+ Message string
41
+ Value any
42
+ }
43
+
44
+ func (e *ValidationError) Error() string {
45
+ return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
46
+ }
47
+
48
+ if !isValid {
49
+ return &ValidationError{
50
+ Field: "email",
51
+ Message: "invalid format",
52
+ Value: email,
53
+ }
54
+ }
55
+ ```
56
+
57
+ Custom errors should include:
58
+ - Descriptive message with context (use `%w` for wrapping)
59
+ - Error codes or types for programmatic handling (using `errors.Is` or `errors.As`)
60
+ - Relevant data for debugging
61
+ - Appropriate mapping to status codes in the transport layer (e.g., HTTP 404)
62
+
63
+ **Tools:** GolangCI-Lint (errname, goerr113), Manual review
@@ -0,0 +1,50 @@
1
+ ---
2
+ title: Do Not Use Error Log For Non-critical
3
+ impact: HIGH
4
+ impactDescription: prevents alert fatigue and log noise
5
+ tags: logging, log-levels, error, observability, quality
6
+ ---
7
+
8
+ ## Do Not Use Error Log For Non-critical
9
+
10
+ Incorrect log levels cause alert fatigue and hide real issues. When everything is an "error", nothing is.
11
+
12
+ **Incorrect (overusing error level):**
13
+
14
+ ```go
15
+ // NOT an error - expected business case
16
+ slog.Error("User entered wrong password")
17
+
18
+ // NOT an error - validation failure
19
+ slog.Error("Email format invalid")
20
+
21
+ // NOT an error - temporary network issue
22
+ slog.Error("Retry attempt 2 of 5")
23
+ ```
24
+
25
+ **Correct (appropriate log levels using slog):**
26
+
27
+ ```go
28
+ // WARN - recoverable, may need attention
29
+ slog.Warn("Payment retry attempt", "attempt", 2, "max_attempts", 5)
30
+
31
+ // INFO - normal business events
32
+ slog.Info("Login failed - invalid password", "user_id", userID, "attempts", 3)
33
+
34
+ // DEBUG - detailed troubleshooting
35
+ slog.Debug("Validation failed", "field", "email", "value", maskedEmail)
36
+
37
+ // ERROR - only for actual system failures
38
+ slog.Error("Database connection lost", "host", dbHost, "error", err)
39
+ ```
40
+
41
+ **Log Level Guide:**
42
+
43
+ | Level | Use For |
44
+ |-------|---------|
45
+ | ERROR | System failures, crashes, unrecoverable |
46
+ | WARN | Potential issues, degraded performance |
47
+ | INFO | Business events, state changes |
48
+ | DEBUG | Detailed troubleshooting |
49
+
50
+ **Tools:** Log linter, Custom rule, `slog` (standard library)
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: No Unused Imports
3
+ impact: LOW
4
+ impactDescription: reduces codebase noise and compilation time
5
+ tags: imports, cleanup, maintenance, quality
6
+ ---
7
+
8
+ ## No Unused Imports
9
+
10
+ Unused imports increase binary size and slow down compilation. Go compiler strictly enforces this at compile time.
11
+
12
+ **Incorrect (unused imports):**
13
+
14
+ ```go
15
+ package main
16
+
17
+ import (
18
+ "fmt"
19
+ "os" // UNUSED
20
+ "time" // UNUSED
21
+ )
22
+
23
+ func main() {
24
+ fmt.Println("Hello")
25
+ }
26
+ ```
27
+
28
+ **Correct (clean imports):**
29
+
30
+ ```go
31
+ package main
32
+
33
+ import (
34
+ "fmt"
35
+ )
36
+
37
+ func main() {
38
+ fmt.Println("Hello")
39
+ }
40
+
41
+ // Side-effect only imports use the blank identifier
42
+ import _ "net/http/pprof"
43
+ ```
44
+
45
+ **Tools:** `goimports`, `golines`, Go Compiler (enforced)
@@ -0,0 +1,34 @@
1
+ ---
2
+ title: No Unused Variables
3
+ impact: LOW
4
+ impactDescription: reduces codebase noise and potential bugs
5
+ tags: variables, cleanup, quality
6
+ ---
7
+
8
+ ## No Unused Variables
9
+
10
+ Unused variables clutter the code and often indicate a bug (missing logic). Go compiler strictly enforces this for local variables.
11
+
12
+ **Incorrect (unused variables):**
13
+
14
+ ```go
15
+ func Calculate(a, b int) int {
16
+ result := a + b
17
+ temp := result * 2 // UNUSED
18
+
19
+ return result
20
+ }
21
+ ```
22
+
23
+ **Correct (clean code):**
24
+
25
+ ```go
26
+ func Calculate(a, b int) int {
27
+ return a + b
28
+ }
29
+
30
+ // If a variable is needed for its side effect but the value is not, use _
31
+ _, err := os.Open("file.txt")
32
+ ```
33
+
34
+ **Tools:** Go Compiler, `go vet`, GolangCI-Lint (unused)
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: No Variable Shadowing
3
+ impact: HIGH
4
+ impactDescription: prevents subtle bugs where inner variables hide outer ones
5
+ tags: variables, shadowing, scope, quality
6
+ ---
7
+
8
+ ## No Variable Shadowing
9
+
10
+ Variable shadowing occurs when a variable declared within a certain scope has the same name as a variable in an outer scope. This can lead to subtle and hard-to-debug logic errors.
11
+
12
+ **Incorrect (shadowed variables):**
13
+
14
+ ```go
15
+ func processItems(items []string) {
16
+ user := "admin"
17
+
18
+ for _, item := range items {
19
+ user := findUser(item) // Shadowing outer 'user' with :=
20
+ fmt.Println("Processing for user:", user)
21
+ }
22
+
23
+ // Outer 'user' is still "admin", which might be unexpected if the loop
24
+ // was intended to update it.
25
+ }
26
+ ```
27
+
28
+ **Correct (unique names or explicit assignment):**
29
+
30
+ ```go
31
+ func processItems(items []string) {
32
+ globalUser := "admin"
33
+
34
+ for _, item := range items {
35
+ currentUser := findUser(item)
36
+ fmt.Println("Processing for user:", currentUser)
37
+ }
38
+ }
39
+ ```
40
+
41
+ **Tools:** `go vet -shadow`, GolangCI-Lint (shadow)
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Centralize Constants
3
+ impact: HIGH
4
+ impactDescription: makes values easy to find and update
5
+ tags: constants, magic-numbers, configuration, quality
6
+ ---
7
+
8
+ ## Centralize Constants
9
+
10
+ Magic numbers or strings scattered throughout the code are hard to find and update. Centralizing them improves maintainability.
11
+
12
+ **Incorrect (magic numbers/strings):**
13
+
14
+ ```go
15
+ func ValidateUser(password string) bool {
16
+ if len(password) < 8 {
17
+ return false
18
+ }
19
+ return true
20
+ }
21
+
22
+ func (s *Service) Retry() {
23
+ for i := 0; i < 3; i++ {
24
+ // retry logic
25
+ }
26
+ }
27
+ ```
28
+
29
+ **Correct (centralized constants):**
30
+
31
+ ```go
32
+ package constants
33
+
34
+ const (
35
+ MaxRetryAttempts = 3
36
+ MinPasswordLength = 8
37
+ DefaultTimeoutSecs = 30
38
+ )
39
+
40
+ const (
41
+ StatusPending = iota
42
+ StatusApproved
43
+ StatusCancelled
44
+ )
45
+
46
+ // Usage
47
+ if len(password) < constants.MinPasswordLength { ... }
48
+ ```
49
+
50
+ **Benefits:**
51
+ - Single source of truth
52
+ - Self-documenting
53
+ - Easy to update across the entire codebase
54
+
55
+ **Tools:** GolangCI-Lint (gocritic, gomnd), Code Review
@@ -0,0 +1,56 @@
1
+ ---
2
+ title: All Error Handling Must Log Root Cause
3
+ impact: HIGH
4
+ impactDescription: enables debugging and incident response
5
+ tags: error-handling, logging, debugging, observability, quality
6
+ ---
7
+
8
+ ## All Error Handling Must Log Root Cause
9
+
10
+ Silent failures make debugging impossible. Without proper logging, you cannot trace issues in production.
11
+
12
+ **Incorrect (silent or minimal logging):**
13
+
14
+ ```go
15
+ func processPayment(order order.Order) {
16
+ err := paymentGateway.Charge(order)
17
+ if err != nil {
18
+ // Silent failure!
19
+ return
20
+ }
21
+ }
22
+
23
+ func saveUser(user *User) error {
24
+ if err := db.Save(user); err != nil {
25
+ return nil // No logging, no context
26
+ }
27
+ return nil
28
+ }
29
+ ```
30
+
31
+ **Correct (comprehensive error logging/wrapping):**
32
+
33
+ ```go
34
+ func processPayment(ctx context.Context, order order.Order) error {
35
+ if err := paymentGateway.Charge(order); err != nil {
36
+ slog.Error("payment processing failed",
37
+ "order_id", order.ID,
38
+ "user_id", order.UserID,
39
+ "amount", order.Amount,
40
+ "error", err,
41
+ "request_id", ctx.Value("request_id"),
42
+ )
43
+ return fmt.Errorf("charging order %s: %w", order.ID, err)
44
+ }
45
+ return nil
46
+ }
47
+ ```
48
+
49
+ **Log context should include:**
50
+ - Error message (and stack trace if available)
51
+ - Relevant entity IDs (order, user, etc.)
52
+ - Request/correlation ID
53
+ - Input that caused the error
54
+ - Timing information
55
+
56
+ **Tools:** GolangCI-Lint, PR review, `slog`
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Use Custom Error Types
3
+ impact: HIGH
4
+ impactDescription: enables proper error categorization and handling
5
+ tags: error-handling, custom-errors, patterns, quality
6
+ ---
7
+
8
+ ## Use Custom Error Types
9
+
10
+ Custom error types enable proper error handling, categorization, and monitoring. They provide clear contracts for API consumers.
11
+
12
+ **Incorrect (generic errors):**
13
+
14
+ ```go
15
+ return errors.New("user not found")
16
+ return errors.New("invalid input")
17
+ return errors.New("database connection failed")
18
+ ```
19
+
20
+ **Correct (custom error structs):**
21
+
22
+ ```go
23
+ // Base application error type can be useful but Go often uses simple structs
24
+ type AppError struct {
25
+ Code string
26
+ Message string
27
+ StatusCode int
28
+ Context map[string]any
29
+ }
30
+
31
+ func (e *AppError) Error() string {
32
+ return e.Message
33
+ }
34
+
35
+ // Specific errors
36
+ type UserNotFoundError struct {
37
+ UserID string
38
+ }
39
+
40
+ func (e UserNotFoundError) Error() string {
41
+ return fmt.Sprintf("user %s not found", e.UserID)
42
+ }
43
+
44
+ type ValidationError struct {
45
+ Field string
46
+ Message string
47
+ }
48
+
49
+ func (e ValidationError) Error() string {
50
+ return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
51
+ }
52
+
53
+ // Usage
54
+ return UserNotFoundError{UserID: "123"}
55
+ return ValidationError{Field: "email", Message: "invalid format"}
56
+
57
+ // Handling
58
+ if errors.As(err, &UserNotFoundError{}) {
59
+ // handle 404
60
+ }
61
+ ```
62
+
63
+ **Benefits:**
64
+ - Type-safe error handling using `errors.As`
65
+ - Automatic HTTP status mapping
66
+ - Structured error context
67
+ - Consistent error format
68
+
69
+ **Tools:** GolangCI-Lint, Code Convention