2026-04-10

Writing Go in GoLand is already a solid experience. Adding GitHub Copilot on top makes the combo almost unfairly good. But there’s a catch: generic Copilot tends to suggest code that isn’t idiomatic Go (interface abuse, swallowed errors, generics where they don’t belong). In this guide I’ll show you the configuration I use to make Copilot look like a gopher, and the shortcuts that save the most time day to day.
If you also work on front-end, check out my other guide: GitHub Copilot in WebStorm: the best configuration to supercharge your day.
To follow this tutorial, you’ll need:
If you’re still on an old version of GoLand, update first. Older versions have a conflict between Copilot and Full Line Code Completion fighting over the Tab key.
Cmd + , on Mac / Ctrl + Alt + S on Windows/Linux)GitHub Copilot in the searchOn GoLand 2025+, the GitHub Copilot plugin already includes the chat. If you only see a single plugin called GitHub Copilot, that’s the one with everything bundled.
After restart, the Copilot icon appears in the bottom right bar.
When the icon turns green, Copilot is ready to use.
This is the most important step for anyone who wants Tab working without surprises. GoLand has a native Full Line Code Completion that also takes Tab and competes with Copilot. If you leave both on, you never know which one will be accepted.
Insert Inline CompletionCmd + Shift + ] (Mac) / Ctrl + Shift + ] (Windows/Linux)Apply completions to Editor (Copilot’s) on TabIn practice, for Go I prefer option A: Copilot knows more about the project context as a whole, and Full Line ends up adding very little.
Go to Settings > Tools > GitHub Copilot > Completions and adjust:
| Option | Recommended value | Why |
|---|---|---|
| Show completions automatically | Enabled | Suggests as you type |
| Automatically check for updates | Enabled | Keeps the plugin up to date |
| Enable Copilot | Enabled | Per-language toggles are below |
In the Languages section, my recommendation for day-to-day Go work:
*_gen.go, *.pb.go, mock_*.go): Copilot only gets in the way hereIf you use Bazel or Buf a lot, also enable .bazel, .bzl, and buf.yaml files.
Open chat with Ctrl + Shift + C or via the right sidebar (Copilot icon).
The three available modes:
To enable Agent Mode:
This is the secret to stop Copilot from suggesting JavaScript disguised as Go. Custom instructions are rules Copilot reads before every response.
.github/ folder (if it doesn’t exist)copilot-instructions.md file# Copilot Instructions
This is a Go 1.22+ service exposing an HTTP API.
## Stack
- Go 1.22 (standard library preferred whenever possible)
- Router: net/http with the new ServeMux (no external frameworks)
- Database: PostgreSQL via pgx/v5 (no ORM)
- Logging: log/slog structured in JSON
- Tests: standard testing + testify for assertions
## Required idiomatic patterns
- Never swallow errors: always return or log them with context
- Use errors.Is / errors.As, never compare errors with ==
- Never use panic in production code, only in init or main
- Pointer receivers only when you need to mutate or the struct is large
- Functions that return an error must return (T, error), never the other way around
- Never name variables with generic err when there's more than one error in scope
- Use context.Context as the first parameter in any function that touches I/O
## Tests
- Always use table-driven tests
- Subtests via t.Run with descriptive names
- Use t.Helper() in test helper functions
- Use t.Cleanup instead of defer when appropriate
- Mocks only for external I/O interfaces, never for internal logic
## Concurrency
- Never create goroutines without a clear termination plan (context or WaitGroup)
- Buffered channels only with a clear justification
- Protect shared state with sync.Mutex, not channels if it's a simple lock
- Prefer errgroup.Group for fan-out with errors
## Never do
- Never use init() for business logic
- Never use mutable global variables
- Never return nil for a slice or map when the caller expects to iterate (return empty)
- Never ignore the return of Close() on files and connections
Save it, restart the chat, and you’ll feel the difference on the first suggestion.
For rules that apply to all Go projects:
Starting in 2026.2, Copilot also reads nested AGENTS.md and CLAUDE.md files. If you’re already using Claude Code, reuse the same file: Settings > Tools > GitHub Copilot > Customizations > Enable nested AGENTS.md and CLAUDE.md.
| Action | Mac | Windows/Linux |
|---|---|---|
| Accept full suggestion | Tab | Tab |
| Dismiss suggestion | Esc | Esc |
| Accept line by line | Shift + Option + → | Shift + Alt + → |
| Next suggestion | Shift + Option + ] | Shift + Alt + ] |
| Previous suggestion | Shift + Option + [ | Shift + Alt + [ |
| Trigger suggestion manually | Shift + Option + \ | Shift + Alt + \ |
| Open suggestions panel (10 options) | Cmd + Shift + A > Open Copilot | Ctrl + Shift + A > Open Copilot |
Important for ABNT2 keyboard users: the official Copilot defaults are
Option + ],Option + [, andOption + \, but on Brazilian keyboards those combinations produce typographic characters («,», and others) and never reach Copilot. The fix is to addShiftto the combo (making itShift + Option + ...), which is what I used in the table above. On a US keyboard, you can use the defaults without the extraShift.
| Action | Mac | Windows/Linux |
|---|---|---|
| Open inline chat at cursor | Ctrl + Shift + I | Ctrl + Shift + G |
| Run prompt | Enter | Enter |
| Cancel | Esc | Esc |
With inline chat you select a function, hit Ctrl + Shift + I, type “write table-driven tests for this function” and Copilot generates straight into the test file.
| Action | Mac | Windows/Linux |
|---|---|---|
| Open chat window | Ctrl + Shift + C | Ctrl + Shift + C |
| New conversation | Cmd + N inside chat | Ctrl + N inside chat |
| Focus input field | Cmd + L | Ctrl + L |
| Attach file to context | #filename.go | Same |
In Settings > Keymap, search for copilot and you’ll see the plugin only exposes a handful of bindable actions (copilot.chat.show, copilot.chat.inline, copilot.openCopilot, copilot.applyInlays, copilot.cycleNextInlays, copilot.cyclePrevInlays, and copilot.requestCompletions). Features like “explain”, “fix”, “generate tests” aren’t plugin actions: they’re slash commands you type inside the chat (see the next section).
What I keep in my keymap:
| Plugin action | Custom shortcut |
|---|---|
copilot.chat.show (open chat window) | Cmd + Shift + K |
copilot.chat.inline (open inline chat at cursor) | Cmd + Shift + I |
copilot.openCopilot (Copilot tool window) | Ctrl + Option + \ |
If any shortcut conflicts, Keymap will warn you. Cmd + Shift + K normally opens Push… in Git in GoLand: I prefer triggering Push via the menu, so I free that key for Copilot. If you use Push via shortcut, pick another combo (like Cmd + Alt + K).
Once the chat is open, slash commands handle the rest:
/explain explains the selected function/fix fixes errors reported by gopls/tests generates table-driven tests/doc generates a godoc comment/simplify proposes a more idiomatic versionWrite a descriptive godoc above the function and Copilot implements based on it:
// FetchActivePosts returns posts published in the last 30 days,
// sorted by publication date descending, limited to 10.
// Returns an error if the database is unavailable or if the query fails.
// The context is used for cancellation.
func (r *PostsRepository) FetchActivePosts(ctx context.Context) ([]Post, error) {
// Copilot fills in here following the rest of the repository's pattern
}
The more specific the godoc, the better the suggestion. Adding the names of relevant files to the comment helps even more.
Select the function, hit Ctrl + Shift + I, and type:
write table-driven tests for this function, covering the happy path, database error, cancelled context, and empty slice. Use testify/require in the assertions.
Copilot generates the full _test.go file, with t.Run and named subtests already in place.
If the project still uses fmt.Errorf("%v") and you want to migrate to %w + errors.Is, open chat in Agent Mode and ask:
Refactor all error wrappers in the
internal/servicepackage to use%wand replace == comparisons with errors.Is. Keep the tests passing.
You review the changes file by file before accepting.
# to contextualize the chatIn chat, type #internal/repository/posts.go and #internal/service/posts.go to load the files as context. Saves you from pasting code.
/tests: generates tests for the selected code/doc: generates a godoc comment/fix: fixes errors reported by gopls/explain: explains the selected code/simplify: proposes a more idiomatic versionFor Go, /simplify is gold: it often proposes versions that remove unnecessary loops, take advantage of slices from the stdlib, and trim code without losing readability.
If you use interfaces for I/O, select the interface, open inline chat, and ask:
generate a mock for this interface using testify/mock, in the file mocks/mock_repository.go
It respects the custom instructions and generates something consistent with the rest of the project.
If you want Copilot to use your project’s gopls as the source of truth (go to definition, coverage, go test, go mod tidy), you can set up an MCP server like mcp-gopls. Copilot will consult Go’s LSP before suggesting, which drastically reduces hallucination on large projects. It’s advanced, but worth the learning curve if you’re working on a Go monorepo.
func(http.Handler) http.Handler).pb.go files (they’re generated, any suggestion breaks the generator).proto if you write contractsFull Line Code Completion fighting. Go back to Step 3 and apply one of the two solutions.
Custom instructions are probably empty or too generic. Review Step 6 and detail the idiomatic patterns you use.
Shift + Option + \ (Mac) / Shift + Alt + \ (Windows/Linux)Install the GitHub Copilot Chat plugin separately (some versions still come separated).
interface{} or any too muchAdd to your custom instructions: “Avoid any and interface{} except when there’s really no known type. Prefer concrete types or typed generics.”
.github/copilot-instructions.md file created with idiomatic rulesWith this setup locked in, Copilot stops being a generic autocompleter and becomes a pair programmer that understands Go. Within a week of using it, you’ll be writing godocs that generate entire functions and using inline chat to generate table-driven tests without taking your hands off the keyboard.