Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
15 Sep 2026 · 25 min read ·Article 45 / 208
Go

Claude Code for Golang: Setup, Best Practices, and the Optimal Workflow

A deep dive into Claude Code for Go developers. Set up an optimal CLAUDE.md, daily best practices, strengths and weaknesses versus other tools, and tips to maximize output quality.

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

Claude Code for Golang: Strengths, Weaknesses, Best Practices

Understanding Claude Code best practices for Golang is the key to getting senior-quality output from the AI. We’re already familiar with Claude Code from Topic 1 (SDD) and Topic 2 (Spec Kit). But in this article we look at it from a different angle: as one of the six tools that need to be evaluated critically. What makes it excel? Where are its limits? And how do you use it optimally in everyday Go development?


05.1 Why Claude Code Is Different from Other Tools

Claude Code isn’t just an “AI coding assistant” — it’s an agentic coding environment built on a different principle: reason first, act second. Unlike Cursor, which generates code immediately after a prompt, Claude Code by default thinks before it acts. The session below shows how Claude maps the context and lays out a plan before touching a single line of code.

bash
 1claude
 2> Implement the CancelOrder feature with all edge cases
 3
 4Claude Code:
 5"Before I start the implementation, let me understand the context:
 6
 71. I read CLAUDE.md and see the error handling pattern used
 82. I need to check the existing Order entity to know which methods already exist
 93. Implementation plan:
10   - Entity: add a CanBeCancelled() method
11   - Repository: add CancelWithStockRestore() + interface update
12   - Usecase: CancelOrderUseCase with all ACs
13   - Handler: DELETE /orders/:id with auth middleware
14   - Router: add the route
15   
164. Start from the entity layer first?
17   (yes/no)"

This “plan first, confirm, then execute” pattern feels slow for small tasks, but is very valuable for complex tasks that require coordination across many files.


05.2 Strengths of Claude Code for Go Development

Strength 1: Consistent Go Idiom Correctness

From the benchmark in Article 3, Claude Code scored 94/100 and never violated the rules in CLAUDE.md across 15 runs. This is no accident — the code below shows the error handling pattern Claude always produces, along with the patterns it never produces.

go
 1// Claude Code always generates this for error handling:
 2func (uc *CancelOrderUseCase) Execute(ctx context.Context, input CancelOrderInput) error {
 3    order, err := uc.orderRepo.GetByIDAndUserID(ctx, input.OrderID, input.UserID)
 4    if err != nil {
 5        return fmt.Errorf("cancelOrder.Execute: %w", err)  // ← always wraps
 6    }
 7    if order == nil {
 8        return ErrOrderNotFound  // ← domain error, not a wrapped error
 9    }
10    // ...
11}
12
13// And it NEVER generates this (though other tools sometimes do):
14result, _ := operation()  // ← ignore error
15return err  // ← no context wrap
16if err != nil { panic(err) }  // ← panic in production code

This consistency comes from Claude’s superior CLAUDE.md retention and its ability to “internalize” the rules as part of its generation process, rather than merely remembering an instruction momentarily.

Strength 2: Layered Architecture Awareness

Claude Code is by default very aware of Clean Architecture. The session below shows how Claude checks which layer should handle each concern before writing code.

bash
 1claude
 2> Implement data fetching for CancelOrder
 3
 4Claude Code:
 5"This implementation will touch several layers:
 6
 7Repository Interface (in the domain/usecase layer):
 8  - GetByIDAndUserID: verify ownership
 9  - CancelWithStockRestore: atomic operation
10
11Repository Implementation (in the repository/postgres layer):
12  - Needs a transaction for atomic cancel + restore
13
14I will not import pgx directly in the usecase layer.
15Start from the repository interface definition?"

This layer awareness prevents architecture violations that would otherwise be caught only during code review — other tools sometimes generate code that imports the DB driver in the usecase directly.

Strength 3: Persistent CLAUDE.md Memory

In long sessions (30+ exchanges), Claude Code still follows the rules in CLAUDE.md. The transcript below proves that retention: a rule confirmed at the start of the session is still obeyed 25 exchanges later without needing a reminder.

bash
 1# Start of session:
 2claude > Read CLAUDE.md and confirm the top 3 rules
 3Claude: "Top 3 rules:
 4  1. Error handling: fmt.Errorf('pkg.Method: %w', err) always
 5  2. Repository not-found: return nil, nil (never error)
 6  3. Monetary: int64 cents, NEVER float64"
 7
 8# 25 exchanges later:
 9claude > Implement get product price
10Claude: [generates with int64 cents, no reminder needed]
11# ✅ Still following rule #3

This long-term retention is exactly what sets Claude Code apart from other tools that often “forget” conventions after 10-15 exchanges, forcing you to repeat the same instructions.

Strength 4: Deep Debugging Capability

For debugging complex issues like race conditions, deadlocks, or memory leaks, Claude Code shows very structured reasoning. The session below shows how Claude identifies two race conditions at once from a single go test -race output, complete with two alternative solutions.

bash
 1claude
 2> go test -race ./... produces:
 3> WARN: DATA RACE
 4> Write at 0x00c0001a4b88 by goroutine 45 (ProcessOrders.func1):
 5> Read at 0x00c0001a4b88 by goroutine 12 (ProcessOrders.func1):
 6>
 7> Code:
 8> func ProcessOrders(orders []*Order) []Result {
 9>     results := make([]Result, 0)
10>     for _, order := range orders {
11>         go func() {
12>             result := process(order)
13>             results = append(results, result)
14>         }()
15>     }
16>     wg.Wait()
17>     return results
18> }
19
20Claude Code:
21"There are TWO race conditions in this code, not one:
22
23Race 1: Variable capture in the goroutine
24  Problem: The 'order' variable is captured by reference by all goroutines.
25  All goroutines will process the last 'order' in the loop.
26  Fix: pass an explicit parameter to the goroutine function
27
28Race 2: Concurrent slice append
29  Problem: results = append(results, result) from multiple goroutines
30  without synchronization.
31  Fix: use a channel or a pre-allocated slice with an index
32
33The correct fix implementation:
34
35Option A (channel-based, more idiomatic):
36func ProcessOrders(orders []*Order) []Result {
37    resultCh := make(chan Result, len(orders))
38    var wg sync.WaitGroup
39    
40    for _, order := range orders {
41        wg.Add(1)
42        go func(o *Order) {
43            defer wg.Done()
44            resultCh <- process(o)  // send to channel
45        }(order)  // explicit copy
46    }
47    
48    go func() {
49        wg.Wait()
50        close(resultCh)
51    }()
52    
53    results := make([]Result, 0, len(orders))
54    for r := range resultCh {
55        results = append(results, r)
56    }
57    return results
58}
59
60Option B (pre-allocated slice, simpler):
61func ProcessOrders(orders []*Order) []Result {
62    results := make([]Result, len(orders))  // pre-allocate
63    var wg sync.WaitGroup
64    
65    for i, order := range orders {
66        wg.Add(1)
67        go func(idx int, o *Order) {
68            defer wg.Done()
69            results[idx] = process(o)  // index assignment, no contention
70        }(i, order)
71    }
72    
73    wg.Wait()
74    return results
75}"

This depth is very rare in other tools — Cursor identifies the problem but the explanation and alternative solutions are rarely this comprehensive.

Strength 5: Plan Mode for Complex Tasks

Plan Mode is a unique Claude Code feature that allows review before execution. The session below shows how Claude lays out an entire refactoring plan — the files touched, the type of changes, and the estimated impact — before a single file is changed.

bash
 1claude --plan
 2> Refactor all error handling in the order service
 3
 4Claude Code (Plan Mode):
 5"Plan for refactoring error handling in the order service:
 6
 7Files to be modified (12 total):
 81. internal/usecase/order/create_order.go (8 changes)
 92. internal/usecase/order/cancel_order.go (5 changes)
103. internal/usecase/order/get_order.go (3 changes)
11...
12
13Type of changes:
14- Wrap all 'return err' with fmt.Errorf context
15- Update repository calls that lack an error wrap
16- Ensure domain errors propagate correctly
17
18Estimated impact:
19- No behavior change
20- All existing tests should pass
21- Better debugging trace in production logs
22
23Proceed? [y/n/detail]"
24
25# You review the plan first, then approve

Plan Mode turns large-scale refactoring from a “leap of faith” into a conscious decision — you know exactly what will change before approving it.


05.3 Weaknesses of Claude Code

Weakness 1: CLI-Only — No Visual Diff

This is the biggest weakness for many developers. The session below describes the situation: Claude reports which files it changed, but you don’t know exactly what changed until you open the diff manually.

bash
 1claude
 2> Refactor create_order.go — move validation into the domain entity
 3
 4Claude Code:
 5"I have updated:
 6- internal/domain/order/entity.go (added a Validate() method)
 7- internal/usecase/order/create_order.go (removed inline validation)
 8- internal/usecase/order/create_order_test.go (updated the test)"
 9
10# You don't know exactly what changed until:
11git diff  # or open the files one by one

Compared to Cursor, which shows a unified diff before applying, this is real friction for multi-file refactoring. Fortunately, git can close this gap; the commands below are the standard mitigation before and after a session.

bash
 1# Set up git tracking before the Claude Code session
 2git add -A
 3git commit -m "checkpoint before AI session"
 4
 5# After Claude finishes:
 6git diff HEAD  # see all changes
 7git diff HEAD internal/usecase/order/  # per directory
 8
 9# Or use a difftool:
10git difftool HEAD  # opens a visual diff tool

With a checkpoint commit before the session, git diff HEAD gives you equivalent visual review — plus an instant undo button if the result is unsatisfactory.

Weakness 2: Per-Token Cost That Can Surprise You

Unlike flat-rate, Claude Code’s cost is proportional to tokens. The snippet below shows how to track cost per session along with three strategies for reducing it.

bash
 1# Tracking cost:
 2claude --show-cost
 3# Output: "Session cost: $0.14 (input: 45K tokens, output: 8K tokens)"
 4
 5# Daily estimate for a heavy user:
 6# 10-15 complex tasks × $0.10-0.20 = $1.50-3.00/day
 7# Monthly: $30-60/month (API cost only)
 8
 9# Budget management tips:
10# 1. Use claude-haiku for simple tasks
11claude --model claude-haiku-4-5-20251001 > Add a godoc comment to this function
12
13# 2. Start a new session for unrelated tasks
14# (no carry-over context = less token overhead)
15
16# 3. Budget alert: set an AWS billing alert if using via Bedrock

The key to managing cost is matching the model to the task complexity and separating unrelated sessions so they don’t drag along context you don’t need.

Weakness 3: No IDE Integration

There are no inline suggestions like Copilot, no file tree navigation, no integrated test runner UI. Mitigation: combine with Copilot or VS Code for daily coding, and use Claude Code only for complex tasks — this is the multi-tool strategy many developers use.

Weakness 4: Slow for Small Tasks

To rename a variable, add one import, or fix a typo, Claude Code is too heavyweight because it will read CLAUDE.md and think about the approach first. Mitigation: use the built-in editor or Copilot for small tasks, and save Claude Code for tasks that require reasoning or multi-file coordination.


05.4 An Optimal CLAUDE.md for a Go Project

A good CLAUDE.md is the highest-ROI investment in using Claude Code. The complete template below shows the structure and section order proven to produce the most consistent output for a Go project — from architecture rules to the list of “NEVER"s.

markdown
  1# CLAUDE.md — [Project Name]
  2## Go [version] | [Framework] | [Database] | [Architecture]
  3
  4---
  5
  6## CRITICAL: Architecture (NEVER violate)
  7
  8Layer hierarchy (strict):
  9```
 10delivery/http/handler → usecase → repository → domain
 11```
 12
 13Rules:
 14- handler: ONLY imports usecase interfaces (never concrete types)
 15- usecase: ONLY imports domain types + repository interfaces
 16- repository: ONLY imports domain types + DB/infra drivers  
 17- domain: ZERO external imports (pure Go standard library only)
 18
 19Violation examples (NEVER do these):
 20```go
 21// WRONG — repository imported in usecase:
 22import "github.com/santekno/santekno-shop/internal/repository/postgres"
 23
 24// WRONG — pgx error exposed from repository:
 25return pgx.ErrNoRows  // use nil, nil instead
 26
 27// CORRECT — interface at consumer side:
 28// In usecase package:
 29type OrderRepository interface {
 30    GetByIDAndUserID(ctx context.Context, orderID, userID uuid.UUID) (*Order, error)
 31}
 32```
 33
 34---
 35
 36## CRITICAL: Error Handling (follow exactly)
 37
 38### Repository Layer
 39```go
 40// Not found → return nil, nil (NEVER return error for not-found)
 41if err == pgx.ErrNoRows {
 42    return nil, nil  // CORRECT
 43}
 44// DB error → wrap and return
 45if err != nil {
 46    return nil, fmt.Errorf("orderRepo.GetByIDAndUserID: %w", err)
 47}
 48```
 49
 50### Usecase Layer
 51```go
 52// Check nil first (means not-found from repo):
 53if order == nil {
 54    return ErrOrderNotFound  // domain error, not wrapped
 55}
 56// Infrastructure errors → wrap with usecase context:
 57if err != nil {
 58    return fmt.Errorf("cancelOrder.Execute: %w", err)
 59}
 60```
 61
 62### Handler Layer  
 63```go
 64// Map domain errors to HTTP:
 65switch {
 66case errors.Is(err, domain.ErrOrderNotFound):
 67    return c.JSON(http.StatusNotFound, ErrorResponse{Error: "ORDER_NOT_FOUND"})
 68case errors.As(err, &notCancellable):
 69    return c.JSON(http.StatusConflict, ErrorResponse{Error: "ORDER_NOT_CANCELLABLE"})
 70}
 71// HTTP error codes: ALWAYS UPPERCASE_SNAKE_CASE
 72```
 73
 74---
 75
 76## CRITICAL: Types
 77
 78```go
 79// Monetary: ALWAYS int64 cents
 80type Order struct {
 81    TotalPrice int64  // 50000 = Rp 500.00 (NOT 5.00, NOT 500.00)
 82}
 83// NEVER: float64, float32, decimal for money
 84
 85// IDs: ALWAYS uuid.UUID
 86import "github.com/google/uuid"
 87type Order struct {
 88    ID uuid.UUID
 89}
 90// NEVER: string, int for IDs
 91
 92// Time: time.Time (UTC in DB, local for display)
 93```
 94
 95---
 96
 97## External Packages (exact import paths — no alternatives)
 98
 99```
100PostgreSQL:    github.com/jackc/pgx/v5         (NEVER database/sql)
101Redis:         github.com/redis/go-redis/v9    
102HTTP:          github.com/labstack/echo/v4      
103Mocking:       go.uber.org/mock/gomock          (NEVER testify mock)
104Test suite:    github.com/stretchr/testify/suite
105UUID:          github.com/google/uuid           
106Kafka:         github.com/confluentinc/confluent-kafka-go/v2
107```
108
109---
110
111## Go Version Constraint: Go 1.22
112
113NEVER use features from Go 1.23+:
114- range-over-func (1.23+): NOT available
115- iter.Seq (1.23+): NOT available
116- slices.Collect (1.23+): NOT available
117
118---
119
120## Testing Standards
121
122```go
123// Suite structure:
124type CancelOrderSuite struct {
125    suite.Suite
126    ctrl     *gomock.Controller
127    mockRepo *mock.MockOrderRepository
128    uc       *usecase.CancelOrderUseCase
129}
130
131func (s *CancelOrderSuite) SetupTest() {
132    s.ctrl = gomock.NewController(s.T())
133    s.mockRepo = mock.NewMockOrderRepository(s.ctrl)
134    s.uc = usecase.NewCancelOrderUseCase(s.mockRepo)
135}
136
137func (s *CancelOrderSuite) TearDownTest() {
138    s.ctrl.Finish()
139}
140
141// Test naming: Test{Subject}_{Scenario}_{ExpectedResult}
142// Example: TestCancelOrder_PendingWithinWindow_Success
143//          TestCancelOrder_WindowExpired_ReturnsCancelWindowExpired
144```
145
146---
147
148## HTTP Response Standards
149
150```go
151type ErrorResponse struct {
152    Error string `json:"error"`  // UPPERCASE_SNAKE_CASE always
153}
154
155// Success: HTTP 2xx, body as specified per endpoint
156// Not found: 404 + {"error": "RESOURCE_NOT_FOUND"}
157// Conflict: 409 + {"error": "SPECIFIC_CONFLICT_REASON"}
158// Server error: 500 + {"error": "INTERNAL_SERVER_ERROR"}
159```
160
161---
162
163## NEVER do these (absolute rules)
164
165```go
166// 1. NEVER ignore errors:
167result, _ := operation()  // FORBIDDEN
168
169// 2. NEVER panic in business logic:
170if err != nil { panic(err) }  // FORBIDDEN
171
172// 3. NEVER float64 for money:
173price := 50.00  // FORBIDDEN
174
175// 4. NEVER string for IDs:
176func GetOrder(id string)  // FORBIDDEN — use uuid.UUID
177
178// 5. NEVER raw error return from usecase:
179return err  // FORBIDDEN — always wrap with context
180
181// 6. NEVER import across layers incorrectly:
182import "internal/repository/postgres"  // FORBIDDEN in usecase
183
184// 7. NEVER use a goroutine without a termination condition:
185go func() { doForever() }()  // FORBIDDEN
186```

The more explicit the WRONG/CORRECT examples you write in CLAUDE.md, the fewer corrections you’ll need to make to the generated code — the “NEVER” list at the end acts as a final fence that Claude rarely crosses.


05.5 An Effective Daily Workflow with Claude Code

Pattern 1: Spec-Driven Implementation

The first pattern continues the SDD discipline from Topics 1 & 2: ask Claude to read the spec and tasks first, confirm the scope, then implement. The session below demonstrates it.

bash
 1claude
 2> Let's implement Task 04 from .specify/features/cancel-order/tasks.md.
 3> Read spec.md and tasks.md first, confirm what will be done.
 4
 5Claude: "Task 04: Implement CancelOrderUseCase
 6  - New file: internal/usecase/order/cancel_order.go
 7  - Dependency: OrderRepository interface (from Task 02)
 8  - Covers: AC1-AC10 from spec.md
 9  
10  I'll implement step by step. Start from the struct definition?"
11
12> Yes, go ahead.

By asking for scope confirmation first, you ensure the implementation is anchored to the spec, not to the AI’s assumptions.

Pattern 2: Test-Driven AI Development

The second pattern reverses the order: give the tests that must pass, then ask Claude to implement without changing the tests. The session below shows the structure.

bash
1claude
2> Here are the tests that must pass (don't change the tests, only implement):
3
4@internal/usecase/order/cancel_order_test.go
5
6> Implement CancelOrderUseCase.Execute that makes all 8 of these tests pass.
7
8Claude: [implements based on the very specific test expectations]

Because the tests become an executable specification, Claude’s output is bound to the behavior you want — not a free interpretation.

Pattern 3: Review Before PR

The third pattern makes Claude the first reviewer before the PR is opened. The requested output format makes findings easy to act on.

bash
 1claude
 2> Review the following files before I open a PR.
 3> Check against CLAUDE.md and spec.md:
 4
 5[list the newly implemented files]
 6
 7> Output format:
 8> CRITICAL — file:line — issue — fix
 9> SUGGESTION — file:line — improvement
10> SCORE — READY/NEEDS WORK
11
12Claude: [detailed review with findings]

This automatic pre-review catches convention violations before a human reviewer spends time on mechanical issues.

Pattern 4: Interactive Debugging

The fourth pattern leverages Claude’s reasoning strength for debugging: paste the full error output plus the relevant code, then ask for analysis and a fix.

bash
 1# Copy-paste the error output straight into claude:
 2claude
 3> go test -race ./internal/usecase/order/... failed:
 4>
 5> --- FAIL: TestCancelOrderSuite/TestExecute_AtomicRollback (0.15s)
 6>     cancel_order_test.go:89: Error: expected nil, got:
 7>         cancelOrder.Execute: cancel with stock restore: context deadline exceeded
 8>
 9> Code:
10> @internal/usecase/order/cancel_order.go
11>
12> Analyze and fix.

The more complete the error context you provide, the more accurate Claude’s diagnosis — don’t trim the stack trace or the relevant code.


05.6 Advanced Patterns

Pattern: Cross-Layer Coordination

For a feature that touches many layers, ask Claude to work in order and verify the compile at each layer. The session below defines that order explicitly.

bash
 1claude
 2> A new feature needs coordination across 4 layers:
 3> 
 4> 1. Domain: add a ValidateForCancellation() method to the Order entity
 5> 2. Repository interface: add a CancelWithRestore(ctx, orderID) method
 6> 3. Usecase: implement CancelOrderUseCase using the new method
 7> 4. Handler: DELETE /orders/:id endpoint
 8>
 9> Implement in this order, verify the compile of each layer before continuing.
10> Don't skip verification — the CLAUDE.md architecture must be preserved.

With this instruction, Claude will run the implement-then-go build cycle per layer so errors are caught early and the architecture stays intact.

Pattern: Batch Refactoring

For large-scale refactoring, combine the bash tool with a transformation instruction. The session below uses grep to find targets, then commands a uniform fix.

bash
1claude
2> Run this bash command first and paste the result:
3> grep -rn "return err$" internal/usecase/ --include="*.go" | grep -v "_test.go"
4
5[paste the grep output]
6
7> Update all these instances with proper error wrapping.
8> Format: fmt.Errorf("[package].[function]: %w", err)
9> Use the package and function name from the file context.

This grep-then-transform approach ensures no instance is missed, while keeping the wrapping format consistent across the whole package.

Pattern: Generate Mock and Interface

The final pattern automates the creation of interfaces and mocks for testing. The session below asks Claude to extract the interface on the consumer side and set up the generate directive.

bash
1claude
2> internal/usecase/order/cancel_order.go needs mocking for tests.
3> 
4> 1. Extract an OrderRepository interface from the struct the usecase uses
5>    (define the interface in the usecase package, not the repository package)
6> 2. Generate the mock using a go:generate directive
7> 3. Update the usecase constructor to inject the interface
8>
9> When done, show the go generate command to re-generate the mock.

Placing the interface on the consumer side (usecase) preserves the dependency inversion principle, and the go:generate directive keeps the mock re-syncable whenever the contract changes.


05.7 Cost Management Strategies

Per-token cost can be controlled with simple discipline. The snippet below summarizes four strategies: pick the model to match complexity, batch prompts, monitor usage, and leverage caching.

bash
 1# Strategy 1: Pick the model to match task complexity
 2
 3# Simple task (rename, add comment, simple function):
 4claude --model claude-haiku-4-5-20251001
 5# ~10x cheaper, enough for simple tasks
 6
 7# Medium task (implement a single usecase, write tests):
 8claude  # default claude-sonnet-4
 9
10# Very complex task (architecture decision, complex debugging):
11claude --model claude-opus-4
12# ~3x more expensive than sonnet, for tasks that truly need it
13
14# Strategy 2: Efficient prompting
15
16# INEFFICIENT — multiple small sessions:
17claude > Add error handling
18claude > Add a test
19claude > Add godoc
20# 3 sessions = 3x overhead
21
22# EFFICIENT — batch in one session:
23claude > Add error handling (wrap pattern from CLAUDE.md), 
24        a unit test (testify/suite), and a godoc comment 
25        for the ProcessOrder function
26
27# Strategy 3: Monitor usage
28claude --usage-stats
29# See token consumption per task type
30# Identify bottlenecks and optimize
31
32# Strategy 4: Use prompt caching
33# Claude automatically caches the CLAUDE.md content
34# Start a new session every 50+ exchanges to refresh the cache

The gist: don’t use Opus for a Haiku job, and don’t split related tasks across many sessions — those two habits alone can significantly cut the monthly bill.


05.8 Troubleshooting Common Issues

Issue 1: Claude generates code that doesn’t compile. The flow below shows how to give compile-error feedback effectively so Claude fixes it correctly.

bash
 1# Step 1: Compile check
 2go build ./...
 3
 4# Step 2: Feed the error to Claude
 5claude
 6> Build error:
 7> ./internal/usecase/order/cancel_order.go:45:3:
 8>   undefined: domain.ErrOrderNotFound
 9> 
10> The domain package is in internal/domain/order/errors.go.
11> Import and use the existing one.
12
13# Step 3: If it's still wrong, check the import
14claude > Read internal/domain/order/errors.go and let me know
15        all the error types available.

Telling Claude the location of the correct definition (not just the error message) speeds up the fix and prevents it from hallucinating a new type.

Issue 2: Claude “forgets” the rules after a long session. When float64 starts appearing or errors aren’t wrapped, the two fixes below get Claude back on track.

bash
1# Symptoms: float64 appears, errors not wrapped, etc.
2
3# Fix 1: Explicit reminder
4claude > Remember: CLAUDE.md rule #1: NEVER float64 for monetary.
5        Please fix the function you just made.
6
7# Fix 2: Start a new session with orientation
8claude  # new session
9> Read CLAUDE.md and confirm the 5 most important rules before we start.

If a brief reminder isn’t enough, a new session with re-orientation is the most reliable reset — and it refreshes the prompt cache too.

Issue 3: Output that’s too verbose. Add a code-style section to CLAUDE.md so Claude holds back unnecessary comments.

markdown
1# Add to CLAUDE.md:
2## Code Style
3- Minimal comments: only explain WHY, never WHAT
4- No "// Implementation of X" comments
5- Function names should be self-documenting
6- Godoc only for exported functions that need explanation

These explicit style rules make the output more concise and idiomatic, without filler comments that obscure intent.

Issue 4: Claude refuses to continue over “ethics”. For legitimate security testing, give context that this is an internal test to validate production defenses.

bash
1# This sometimes happens for legitimate security testing code
2
3claude > We're writing an integration test to verify SQL injection prevention.
4        This is an internal test to validate that the parameterized queries
5        we use in production are not vulnerable.
6        Implement this test case:
7        [describe what you need]

Explaining the defensive intent of the test is usually enough to get past a mistaken refusal, because Claude understands the legitimate security context.


05.9 Integrating Claude Code with the Go Toolchain

Claude Code is most effective when chained with automatic verification. The script below runs build, vet, race test, and lint all at once after every session.

bash
 1# A good pre-session setup:
 2alias claude-go='claude && go build ./... && go vet ./... && go test -race ./...'
 3
 4# Or make a script:
 5cat > scripts/ai-verify.sh << 'EOF'
 6#!/bin/bash
 7echo "=== Build Check ==="
 8go build ./... || exit 1
 9
10echo "=== Vet Check ==="
11go vet ./... || exit 1
12
13echo "=== Race Check ==="
14go test -race ./... || exit 1
15
16echo "=== Lint ==="
17golangci-lint run ./...
18
19echo "✅ All checks passed"
20EOF
21chmod +x scripts/ai-verify.sh
22
23# Run after every Claude Code session:
24./scripts/ai-verify.sh

Making verification a mandatory post-session step ensures the generated code always passes the same gate as hand-written code.


05.10 Claude Code for Go Module Management

Claude Code also helps manage dependencies, from upgrades to audits. The session below demonstrates three module-management tasks that come up often.

bash
 1# Update dependencies
 2claude
 3> go.mod shows pgx/v5 is v5.5.0 but the latest is v5.7.0.
 4> Update all pgx-related code to be compatible with v5.7.0.
 5> Check the changelog for breaking changes first.
 6
 7# Resolve a dependency conflict
 8claude
 9> go mod tidy produces:
10> [paste error]
11> Help resolve this.
12
13# Audit dependencies
14claude
15> Check go.sum and identify dependencies that might need updating
16> for security reasons based on your knowledge.

Asking Claude to check the changelog before upgrading is an important habit — it can anticipate breaking changes before your code fails to compile.


05.11 Measuring Claude Code Effectiveness

Tool adoption must be proven with numbers, not feelings. The list of metrics below shows example before/after values you can track to prove Claude Code’s impact.

text
 1Metrics you can track:
 2
 31. Time per feature implementation
 4   Before AI: 4 hours average
 5   After AI: 2.5 hours average
 6   Improvement: 37.5%
 7
 82. PR review round trips
 9   Before: 2.3 round trips average
10   After: 1.4 round trips
11   Improvement: 39%
12
133. Test coverage
14   Before: 72% average
15   After: 88% average
16   Improvement: +16 percentage points
17
184. Error handling consistency
19   (via golangci-lint error handling rules)
20   Before: 8 violations per sprint
21   After: 1-2 violations per sprint
22
235. Time to onboard a new developer
24   Before: 2 weeks to first meaningful PR
25   After: 1 week
26   Improvement: 50%

Metrics like time-to-PR-ready and the number of review round-trips are far more meaningful to the business than “how much code was generated” — track what actually matters.


05.12 Claude Code vs Alternatives: Head-to-Head

After discussing the strengths and weaknesses, the table below places Claude Code alongside Cursor and Copilot on the most common Go scenarios.

ScenarioClaude CodeCursorCopilot
Implement feature from spec⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Go idiom correctness⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Visual diff review❌⭐⭐⭐⭐⭐⭐⭐⭐
Multi-file Composer⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Race condition debug⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
PR review automation❌❌⭐⭐⭐⭐⭐
Inline autocomplete❌⭐⭐⭐⭐⭐⭐⭐⭐⭐
SDD workflow native⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Cost predictability❌ (variable)✅ (flat)✅ (flat)
CLI comfortable✅❌❌

The pattern in this table is clear: Claude Code excels in Go idiom quality, SDD, and deep debugging, but loses on visual diff and inline autocomplete — exactly the areas a multi-tool strategy can cover.


05.13 Tips & Gotchas

💡 Tip 1: Start every session with orientation. Confirm that Claude understands the core rules before you start coding.

bash
1claude
2> Read CLAUDE.md and confirm you understand:
3> 1. Architecture rules
4> 2. Error handling pattern
5> 3. Monetary type rules

This brief orientation loads the rules into the active context and drastically lowers the chance of a mid-session violation.

💡 Tip 2: Use --dangerously-skip-permissions wisely. This flag skips per-action confirmation — efficient but risky. Use it only in a development environment, after reviewing a comprehensive plan, and for tasks whose scope is clear and reversible.

💡 Tip 3: CLAUDE.md must be committed. This context file is a team asset; commit it to the repo and update it every time a new convention or a changed pattern appears so the whole team benefits equally.

💡 Tip 4: Pair with git worktree for parallel experiments. A separate worktree lets you experiment without disturbing the main working directory.

bash
1# Experiment in a separate worktree without affecting the main working directory:
2git worktree add /tmp/experiment-cancel-order -b experiment/cancel-order
3cd /tmp/experiment-cancel-order
4claude > Try approach A for cancel order with the outbox pattern
5# If the approach is good: git cherry-pick or merge
6# If not: discard the worktree

With a worktree, risky experiments become cheap: a good approach is simply merged, a failed one is simply discarded without a trace.

⚠️ Gotcha 1: Claude can be too eager to “improve”. Explicitly limit the scope when you only want a specific fix.

bash
1claude > Fix the bug on line 45 of cancel_order.go
2
3# Claude might not only fix the bug but also "refactor" other areas
4# Mitigation:
5claude > Fix ONLY the bug on line 45.
6        Don't change anything else.
7        If you see other improvements, just report them, don't implement them.

Explicitly limiting the scope prevents unexpected changes that enlarge the diff and complicate review.

⚠️ Gotcha 2: Large files consume a lot of tokens. Files over 1000 lines shouldn’t be included whole; quote only the relevant part.

bash
1# Less efficient:
2claude > [paste a 2000 line file]
3
4# More efficient:
5claude > The file order_repository.go has an issue in the GetByIDAndUserID implementation.
6        Relevant code (lines 145-180):
7        [paste only the relevant part]

Quoting only the relevant snippet saves tokens and focuses Claude’s attention on the area that’s actually problematic.

⚠️ Gotcha 3: Hallucination for internal packages. Claude sometimes “invents” a method that doesn’t exist on your struct.

go
1// Claude might generate:
2order.GetTotalWithTax()  // ← this method doesn't exist on your Order struct!
3
4// Prevention:
5// > Read entity.go first before implementing, so you know the existing methods.

Asking Claude to read the entity definition first is the most effective prevention against internal-method hallucination.

⚠️ Gotcha 4: Per-token cost can spike unexpectedly. A long context plus a complex task and many revisions can inflate the cost beyond expectations — monitor with the --show-cost flag periodically.


05.14 Summary

Claude Code is the best choice for Go developers who prioritize code quality and Go idiom correctness, are comfortable with a terminal/CLI workflow, have already invested in the SDD approach (Topics 1 & 2), and need deep reasoning for complex debugging.

Its main weaknesses — CLI-only, per-token cost, and no visual diff — can be mitigated with a combination of Copilot for inline suggestions, token usage tracking for budget, git diff as a visual diff substitute, and a multi-tool strategy for different tasks.

A comprehensive CLAUDE.md is the key: a 30-minute investment in a good CLAUDE.md can return hundreds of hours saved over the project lifetime. In the next article we move to a very different approach — Cursor, the IDE that makes AI a first-class citizen.

Related Articles

💬 Comments