Demo of using Claude Code to analyze, refactor, and fix concurrency issues in a JavaScript Three.js frontend and Go WebSocket backend for a multiplayer RetroRacer game.
Now there’s another powerful thing you can do with Claude Code: analyze and modify large codebases to find architectural issues, implement fixes, and validate the result with builds.
This demo uses a moderately large repository from my collection called RetroRacer. I suspected it was a JavaScript/Three.js frontend with a Go backend (initially guessed a SQLite-backed API), so I asked Claude Code to analyze the repo and answer targeted questions.
Before allowing tools to read or execute files, review permissions carefully. Executing untrusted code is unsafe; reading files may still surface sensitive data.
Claude Code asks for permission to read (and optionally execute) files in the workspace. The prompt for this repo looked like:
Copy
Do you trust the files in this folder?/Users/jeremy/Repos/RetroRacerClaude Code may read files in this folder. Reading untrusted files may lead Claude Code to behave in unexpected ways.With your permission Claude Code may execute files in this folder. Executing untrusted code is unsafe.https://docs.anthropic.com/s/claude-code-security› 1. Yes, proceed 2. No, exitEnter to confirm · Esc to exit
After granting access, Claude Code shows onboarding tips and lets you initialize a workspace:
Copy
* Welcome to Claude Code!/help for help, /status for your current setupcwd: /Users/jeremy/Repos/RetroRacerTips for getting started:1. Run /init to create a CLAUDE.md file with instructions for Claude2. Use Claude to help with file analysis, editing, bash commands and git3. Be as specific as you would with another engineer for the best results4. ✓ Run /terminal-setup to set up terminal integration✱ Tip: Cmd+Escape to launch Claude in your IDE> Try "refactor <filepath>"? for shortcuts
I ran the terminal setup so Claude would know my VS Code key bindings:
Copy
> /terminal-setup└ Installed VSCode terminal Shift+Enter key binding See /Users/jeremy/Library/Application Support/Code/User/keybindings.json>? for shortcuts
Tip: Being specific when asking about code (file paths, function names, or error messages) yields more precise analysis and actionable fixes.
> give me a high level overview of this codebase• I'll analyze the codebase structure to give you a high-level overview.● List(.)└ Listed 47 paths (ctrl+r to expand)● Read(README.md)└ Read 3 lines (ctrl+r to expand)● Read(package.json)└ Read 32 lines (ctrl+r to expand)● Read(backend/go.mod)└ Read 14 lines (ctrl+r to expand)* Crunching (12s · ↓ 93 tokens · esc to interrupt)
Claude produced an accurate summary:
RetroRacer is a multiplayer racing game with a retro aesthetic built with modern web technologies and a Go backend.
Architecture:
Frontend: JavaScript client using Three.js for 3D graphics
Backend: Go server with WebSocket support for real-time multiplayer
Build System: Vite for development and bundling
Key frontend and backend files were listed and categorized.
I asked Claude to scan recent server logs for errors:
Copy
> what are the most recent errors found in retroracer_server.log+ Computing... (0s · ↑ 0 tokens · esc to interrupt)
The log scan did not show many explicit ERROR-level messages, but it surfaced serious synchronization and lifecycle issues. Key problems found:
Player/client map synchronization problem
Example log: “[DEBUG] Removing player 1748150421520079300-2524 from players map - no client found” — players are removed immediately after joining, indicating a race between registration and tracking.
Client count discrepancy
Broadcasts were dispatched to 0 clients even as players were joining — client tracking is inconsistent.
Phantom heartbeat messages
Heartbeats arrive from clients that appear removed from internal maps — the WebSocket remains open while server state shows zero clients.
WebSocket connection errors
Lines like “websocket: close 1001 (going away)” and “Error sending ping to client: websocket: close sent” indicate disconnects, but timing makes them look like symptoms of premature cleanup.
Dual maps (clients + players) combined with concurrent operations produced race conditions where cleanup and synchronization tasks remove players before registration completes. The recommended approach is:
Use a single source of truth (clients map).
Add a registration state flag to the Client struct to avoid treating partially-registered connections as active players.
Perform registration atomically inside the hub event loop using a registration message channel.
Claude suggested introducing an atomic registration message type and channel so registrations are handled inside the hub’s main event loop.Example server types:
I asked Claude to implement the changes and run a build. During the edit-and-build cycle the initial compile failed (expected while refactoring):
Copy
cd /Users/jeremy/Repos/RetroRacer/backend && go build .# Build output (errors)server/hub.go:377:23: h.players undefined (type *Hub has no field or method players)server/multiplayer.go:97:16: s.defaultHub.players undefined (type *Hub has no field or method players)server/multiplayer.go:107:33: s.defaultHub.players undefined (type *Hub has no field or method players)# ... additional lines omitted for brevity
Claude iterated, updating references and helper methods to rely on the clients map and the registrationComplete flag. After the edits, Claude rebuilt the backend:
Copy
cd /Users/jeremy/Repos/RetroRacer/backend && go build . && echo "Build successful!"Build successful!