
Go Code Review
- 1.2k installs
- 137 repo stars
- Updated June 20, 2026
- cxuu/golang-skills
go-code-review provides documented workflows for Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code chan
About
The go-code-review skill use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code changes, even if the user doesn't explicitly request a style review. Does not cover language-specific syntax - delegates to specialized skills. # Go Code Review Checklist ## Review Procedure > Use `assets/review-template.md` when formatting the output of a code review to ensure consistent structure with Must Fix / Should Fix / Nits severity grouping. Run `gofmt -d .` and `go vet ./...` to catch mechanical issues first 2. Read the diff file-by-file; for each file, check the categories below in order 3. Flag issues with specific line references and the rule name 4. After reviewing all files, re-read flagged items to verify they're genuine issues 5. Summarize findings grouped by severity (must-fix, should-fix, nit) > **Validation**: After completing the review, re-read the diff once more to verify every flagged issue is real.
- Run `gofmt -d .` and `go vet ./...` to catch mechanical issues first
- Read the diff file-by-file; for each file, check the categories below in order
- Flag issues with specific line references and the rule name
- After reviewing all files, re-read flagged items to verify they're genuine issues
- Summarize findings grouped by severity (must-fix, should-fix, nit)
Go Code Review by the numbers
- 1,235 all-time installs (skills.sh)
- +44 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #199 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
go-code-review capabilities & compatibility
- Capabilities
- run `gofmt d .` and `go vet ./...` to catch mec · read the diff file by file; for each file, check · flag issues with specific line references and th · after reviewing all files, re read flagged items · summarize findings grouped by severity (must fix
- Use cases
- documentation
What go-code-review says it does
# Go Code Review Checklist ## Review Procedure > Use `assets/review-template.md` when formatting the output of a code review to ensure consistent structure with Must Fix / Should Fix / Nits severity g
Run `gofmt -d .` and `go vet ./...` to catch mechanical issues first 2.
npx skills add https://github.com/cxuu/golang-skills --skill go-code-reviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.2k |
|---|---|
| repo stars | ★ 137 |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 20, 2026 |
| Repository | cxuu/golang-skills ↗ |
How do I use go-code-review for the task described in its SKILL.md triggers?
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code changes, even if the user doesn't explicitl.
Who is it for?
Teams invoking go-code-review when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code changes, even if the user doesn't explicitly request a style rev
What you get
Step-by-step guidance grounded in go-code-review documentation and reference files.
- structured code review
- Must Fix issue list
Files
Go Code Review Checklist
Review Procedure
Use assets/review-template.md when formatting the output of a code review to ensure consistent structure with Must Fix / Should Fix / Nits severity grouping.1. Run gofmt -d . and go vet ./... to catch mechanical issues first 2. Read the diff file-by-file; for each file, check the categories below in order 3. Flag issues with specific line references and the rule name 4. After reviewing all files, re-read flagged items to verify they're genuine issues 5. Summarize findings grouped by severity (must-fix, should-fix, nit)
Validation: After completing the review, re-read the diff once more to verify every flagged issue is real. Remove any finding you cannot justify with a specific line reference.
---
Formatting
- [ ] gofmt: Code is formatted with
gofmtorgoimports→ go-linting
---
Documentation
- [ ] Comment sentences: Comments are full sentences starting with the name being described, ending with a period → go-documentation
- [ ] Doc comments: All exported names have doc comments; non-trivial unexported declarations too → go-documentation
- [ ] Package comments: Package comment appears adjacent to package clause with no blank line → go-documentation
- [ ] Named result parameters: Only used when they clarify meaning (e.g., multiple same-type returns), not just to enable naked returns → go-documentation
---
Error Handling
- [ ] Handle errors: No discarded errors with
_; handle, return, or (exceptionally) panic → go-error-handling - [ ] Error strings: Lowercase, no punctuation (unless starting with proper noun/acronym) → go-error-handling
- [ ] In-band errors: No magic values (-1, "", nil); use multiple returns with error or ok bool → go-error-handling
- [ ] Indent error flow: Handle errors first and return; keep normal path at minimal indentation → go-error-handling
---
Naming
- [ ] MixedCaps: Use
MixedCapsormixedCaps, never underscores; unexported ismaxLengthnotMAX_LENGTH→ go-naming - [ ] Initialisms: Keep consistent case:
URL/url,ID/id,HTTP/http(e.g.,ServeHTTP,xmlHTTPRequest) → go-naming - [ ] Variable names: Short names for limited scope (
i,r,c); longer names for wider scope → go-naming - [ ] Receiver names: One or two letter abbreviation of type (
cforClient); nothis,self,me; consistent across methods → go-naming - [ ] Package names: No stuttering (use
chubby.Filenotchubby.ChubbyFile); avoidutil,common,misc→ go-packages - [ ] Avoid built-in names: Don't shadow
error,string,len,cap,append,copy,new,make→ go-declarations
---
Concurrency
- [ ] Goroutine lifetimes: Clear when/whether goroutines exit; document if not obvious → go-concurrency
- [ ] Synchronous functions: Prefer sync over async; let callers add concurrency if needed → go-concurrency
- [ ] Contexts: First parameter; not in structs; no custom Context types; pass even if you think you don't need to → go-context
---
Interfaces
- [ ] Interface location: Define in consumer package, not implementor; return concrete types from producers → go-interfaces
- [ ] No premature interfaces: Don't define before used; don't define "for mocking" on implementor side → go-interfaces
- [ ] Receiver type: Use pointer if mutating, has sync fields, or is large; value for small immutable types; don't mix → go-interfaces
---
Data Structures
- [ ] Empty slices: Prefer
var t []string(nil) overt := []string{}(non-nil zero-length) → go-data-structures - [ ] Copying: Be careful copying structs with pointer/slice fields; don't copy
*Tmethods' receivers by value → go-data-structures
---
Security
- [ ] Crypto rand: Use
crypto/randfor keys, notmath/rand→ go-defensive - [ ] Don't panic: Use error returns for normal error handling; panic only for truly exceptional cases → go-defensive
---
Declarations and Initialization
- [ ] Group similar: Related
var/const/typein parenthesized blocks; separate unrelated → go-declarations - [ ] var vs :=: Use
varfor intentional zero values;:=for explicit assignments → go-declarations - [ ] Reduce scope: Move declarations close to usage; use if-init to limit variable scope → go-declarations
- [ ] Struct init: Always use field names; omit zero fields;
varfor zero structs → go-declarations - [ ] Use `any`: Prefer
anyoverinterface{}in new code → go-declarations
---
Functions
- [ ] File ordering: Types → constructors → exported methods → unexported → utilities → go-functions
- [ ] Signature formatting: All args on own lines with trailing comma when wrapping → go-functions
- [ ] Naked parameters: Add
/* name */comments for ambiguous bool/int args, or use custom types → go-functions - [ ] Printf naming: Functions accepting format strings end in
fforgo vet→ go-functions
---
Style
- [ ] Line length: No rigid limit, but avoid uncomfortably long lines; break by semantics, not arbitrary length → go-style-core
- [ ] Naked returns: Only in short functions; explicit returns in medium/large functions → go-style-core
- [ ] Pass values: Don't use pointers just to save bytes; pass
stringnot*stringfor small fixed-size types → go-performance - [ ] String concatenation:
+for simple;fmt.Sprintffor formatting;strings.Builderfor loops → go-performance
---
Logging
- [ ] Use slog: New code uses
log/slog, notlogorfmt.Printlnfor operational logging → go-logging - [ ] Structured fields: Log messages use static strings with key-value attributes, not fmt.Sprintf → go-logging
- [ ] Appropriate levels: Debug for developer tracing, Info for notable events, Warn for recoverable issues, Error for failures → go-logging
- [ ] No secrets in logs: PII, credentials, and tokens are never logged → go-logging
---
Imports
- [ ] Import groups: Standard library first, then blank line, then external packages → go-packages
- [ ] Import renaming: Avoid unless collision; rename local/project-specific import on collision → go-packages
- [ ] Import blank:
import _ "pkg"only in main package or tests → go-packages - [ ] Import dot: Only for circular dependency workarounds in tests → go-packages
---
Generics
- [ ] When to use: Only when multiple types share identical logic and interfaces don't suffice → go-generics
- [ ] Type aliases: Use definitions for new types; aliases only for package migration → go-generics
---
Testing
- [ ] Examples: Include runnable
Examplefunctions or tests demonstrating usage → go-documentation - [ ] Useful test failures: Messages include what was wrong, inputs, got, and want; order is
got != want→ go-testing - [ ] TestMain: Use only when all tests need common setup with teardown; prefer scoped helpers first → go-testing
- [ ] Real transports: Prefer
httptest.NewServer+ real client over mocking HTTP → go-testing
---
Automated Checks
Run automated pre-review checks:
bash scripts/pre-review.sh ./... # text output
bash scripts/pre-review.sh --json ./... # structured JSON outputOr manually: gofmt -l <path> && go vet ./... && golangci-lint run ./...
Fix any issues before proceeding to the checklist above. For linter setup and configuration, see go-linting.
---
Integrative Example
Read references/WEB-SERVER.md when building a production HTTP server and want to verify your code applies concurrency, error handling, context, documentation, and naming conventions together.
---
Related Skills
- Style foundations: See go-style-core when resolving formatting debates or applying the clarity > simplicity > concision priority
- Linting setup: See go-linting when configuring golangci-lint or adding automated checks to CI
- Error strategy: See go-error-handling when reviewing error wrapping, sentinel errors, or the handle-once pattern
- Naming conventions: See go-naming when evaluating identifier names, receiver names, or package-symbol stuttering
- Testing patterns: See go-testing when reviewing test code for table-driven structure, failure messages, or helper usage
- Concurrency safety: See go-concurrency when reviewing goroutine lifetimes, channel usage, or mutex placement
- Logging practices: See go-logging when reviewing log usage, structured logging, or slog configuration
Code Review: [PR Title]
Summary
[Brief description of the changes]
Findings
Must Fix
- [ ] [file:line] Description of critical issue
Should Fix
- [ ] [file:line] Description of recommended improvement
Nits
- [ ] [file:line] Description of minor suggestion
Automated Checks
- [ ]
gofmt -d .— clean - [ ]
go vet ./...— clean - [ ]
golangci-lint run— clean
Skills Applied
[List of go-* skills referenced during review]
Web Server: Skills Applied Together
This example shows how Go skills integrate in a real HTTP server. Each section references the relevant skill for detailed guidance.
Structure
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"time"
)
// --- Interfaces (go-interfaces) ---
// Store defines the data access boundary. Defined in the consumer
// package, not the implementation package.
type Store interface {
GetUser(ctx context.Context, id string) (*User, error)
}
// --- Types and constructors (go-naming, go-declarations) ---
// Server handles HTTP requests for the user API.
type Server struct {
store Store
router *http.ServeMux
}
// NewServer creates a Server with the given dependencies.
// The caller must call Shutdown to release resources.
func NewServer(store Store) *Server {
s := &Server{store: store}
s.router = http.NewServeMux()
s.router.HandleFunc("GET /users/{id}", s.handleGetUser)
return s
}
// --- Error handling (go-error-handling) ---
// Domain errors as sentinels — checked with errors.Is.
var ErrNotFound = errors.New("not found")
// --- HTTP handler (go-control-flow, go-context, go-error-handling) ---
func (s *Server) handleGetUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // go-context: derive from request
id := r.PathValue("id")
user, err := s.store.GetUser(ctx, id)
if err != nil {
if errors.Is(err, ErrNotFound) { // go-error-handling: errors.Is
http.Error(w, "user not found", http.StatusNotFound)
return // go-control-flow: early return
}
// HTTP handlers are an exception to "log OR return": log detail server-side, return sanitized error to client.
slog.Error("GetUser failed", "id", id, "err", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
// --- Graceful shutdown (go-concurrency, go-defensive) ---
func main() {
store := NewDBStore(os.Getenv("DATABASE_URL"))
srv := NewServer(store)
httpSrv := &http.Server{
Addr: ":8080",
Handler: srv.router,
ReadTimeout: 5 * time.Second, // go-defensive: use time.Duration
WriteTimeout: 10 * time.Second,
}
// go-concurrency: goroutine lifetime is clear
go func() {
sigCh := make(chan os.Signal, 1) // go-concurrency: channel size 1
signal.Notify(sigCh, os.Interrupt)
<-sigCh
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() // go-defensive: defer cleanup
httpSrv.Shutdown(ctx)
}()
slog.Info("starting server", "addr", httpSrv.Addr)
if err := httpSrv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
slog.Error("server error", "err", err)
os.Exit(1) // go-packages: exit only from main
}
}Skills Applied
| Area | Skill | What's demonstrated |
|---|---|---|
| Interface at consumer | go-interfaces | Store defined where it's used |
| Naming | go-naming | MixedCaps, receiver abbreviation, clear func names |
| Error handling | go-error-handling | Sentinels, errors.Is, log-or-return |
| Context | go-context | Derived from request, passed through |
| Control flow | go-control-flow | Early returns for error cases |
| Concurrency | go-concurrency | Clear goroutine lifetime, channel sizing |
| Defensive | go-defensive | defer cancel(), time.Duration, graceful shutdown |
| Packages | go-packages | Exit only in main() |
| Logging | go-error-handling | Structured slog, handle error once |
#!/usr/bin/env bash
set -euo pipefail
VERSION="1.0.0"
SCRIPT_NAME="$(basename "$0")"
usage() {
cat <<EOF
$SCRIPT_NAME v$VERSION — Run automated pre-review checks on Go code
USAGE
bash $SCRIPT_NAME [options] [path]
DESCRIPTION
Runs gofmt, go vet, and golangci-lint against the target path and
reports any findings. Use before manual code review to catch
mechanical issues early.
Exits 0 if all checks pass, 1 if issues found, 2 on error.
OPTIONS
-h, --help Show this help message
-v, --version Show version
--json Output results as JSON
--force Run even if golangci-lint is not installed (skip it)
--limit N Max items reported per section (0 = unlimited, default: 0)
ARGUMENTS
path Package pattern to check (default: ./...)
EXAMPLES
bash $SCRIPT_NAME
bash $SCRIPT_NAME ./pkg/...
bash $SCRIPT_NAME --json ./cmd/server/...
bash $SCRIPT_NAME --force ./...
bash $SCRIPT_NAME --json --limit 10 ./...
EOF
}
json_escape() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//$'\t'/\\t}"
s="${s//$'\r'/}"
s="${s//$'\n'/\\n}"
printf '%s' "$s"
}
JSON_OUTPUT=false
FORCE=false
LIMIT=0
TARGET=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help) usage; exit 0 ;;
-v|--version) echo "$SCRIPT_NAME v$VERSION"; exit 0 ;;
--json) JSON_OUTPUT=true; shift ;;
--force) FORCE=true; shift ;;
--limit) LIMIT="${2:?error: --limit requires a number}"; shift 2 ;;
-*) echo "error: unknown option: $1" >&2; usage >&2; exit 2 ;;
*) TARGET="$1"; shift ;;
esac
done
TARGET="${TARGET:-./...}"
if ! command -v go &>/dev/null; then
echo "error: go is not installed or not in PATH" >&2
exit 2
fi
if ! command -v gofmt &>/dev/null; then
echo "error: gofmt is not installed or not in PATH" >&2
exit 2
fi
GOFMT_STATUS="pass"
GOFMT_FINDINGS=()
GOFMT_DIR="${TARGET%%/...}"
GOFMT_DIR="${GOFMT_DIR:-.}"
UNFORMATTED=$(gofmt -l "$GOFMT_DIR" 2>&1) || true
if [[ -n "$UNFORMATTED" ]]; then
GOFMT_STATUS="fail"
while IFS= read -r f; do
[[ -n "$f" ]] && GOFMT_FINDINGS+=("$f")
done <<< "$UNFORMATTED"
fi
GOVET_STATUS="pass"
GOVET_OUTPUT=""
if ! GOVET_OUTPUT=$(go vet "$TARGET" 2>&1); then
GOVET_STATUS="fail"
fi
LINT_STATUS="skip"
LINT_OUTPUT=""
if command -v golangci-lint &>/dev/null; then
LINT_STATUS="pass"
if ! LINT_OUTPUT=$(golangci-lint run "$TARGET" 2>&1); then
LINT_STATUS="fail"
fi
elif ! $FORCE; then
echo "error: golangci-lint not installed (use --force to skip)" >&2
exit 2
fi
FAILED=0
[[ "$GOFMT_STATUS" == "fail" ]] && FAILED=1
[[ "$GOVET_STATUS" == "fail" ]] && FAILED=1
[[ "$LINT_STATUS" == "fail" ]] && FAILED=1
if $JSON_OUTPUT; then
GOFMT_TRUNCATED=false
GOFMT_DISPLAY=("${GOFMT_FINDINGS[@]+"${GOFMT_FINDINGS[@]}"}")
if [[ $LIMIT -gt 0 && ${#GOFMT_DISPLAY[@]} -gt $LIMIT ]]; then
GOFMT_DISPLAY=("${GOFMT_FINDINGS[@]:0:$LIMIT}")
GOFMT_TRUNCATED=true
fi
GOFMT_JSON="["
first=true
for f in "${GOFMT_DISPLAY[@]+"${GOFMT_DISPLAY[@]}"}"; do
$first || GOFMT_JSON+=","
first=false
GOFMT_JSON+="\"$(json_escape "$f")\""
done
GOFMT_JSON+="]"
GOVET_TRUNCATED=false
GOVET_DISPLAY="$GOVET_OUTPUT"
if [[ $LIMIT -gt 0 && -n "$GOVET_OUTPUT" ]]; then
GOVET_ARR=()
while IFS= read -r line; do
GOVET_ARR+=("$line")
done <<< "$GOVET_OUTPUT"
if [[ ${#GOVET_ARR[@]} -gt $LIMIT ]]; then
GOVET_DISPLAY=""
for (( i=0; i<LIMIT; i++ )); do
[[ -n "$GOVET_DISPLAY" ]] && GOVET_DISPLAY+=$'\n'
GOVET_DISPLAY+="${GOVET_ARR[$i]}"
done
GOVET_TRUNCATED=true
fi
fi
GOVET_ESC="$(json_escape "$GOVET_DISPLAY")"
LINT_TRUNCATED=false
LINT_DISPLAY="$LINT_OUTPUT"
if [[ $LIMIT -gt 0 && -n "$LINT_OUTPUT" ]]; then
LINT_ARR=()
while IFS= read -r line; do
LINT_ARR+=("$line")
done <<< "$LINT_OUTPUT"
if [[ ${#LINT_ARR[@]} -gt $LIMIT ]]; then
LINT_DISPLAY=""
for (( i=0; i<LIMIT; i++ )); do
[[ -n "$LINT_DISPLAY" ]] && LINT_DISPLAY+=$'\n'
LINT_DISPLAY+="${LINT_ARR[$i]}"
done
LINT_TRUNCATED=true
fi
fi
LINT_ESC="$(json_escape "$LINT_DISPLAY")"
GOFMT_TRUNC=""
$GOFMT_TRUNCATED && GOFMT_TRUNC=',"truncated":true'
GOVET_TRUNC=""
$GOVET_TRUNCATED && GOVET_TRUNC=',"truncated":true'
LINT_TRUNC=""
$LINT_TRUNCATED && LINT_TRUNC=',"truncated":true'
cat <<EOF
{"gofmt":{"status":"$GOFMT_STATUS","files":$GOFMT_JSON$GOFMT_TRUNC},"govet":{"status":"$GOVET_STATUS","output":"$GOVET_ESC"$GOVET_TRUNC},"golangci_lint":{"status":"$LINT_STATUS","output":"$LINT_ESC"$LINT_TRUNC},"passed":$( [[ $FAILED -eq 0 ]] && echo true || echo false )}
EOF
else
echo "=== gofmt ==="
if [[ "$GOFMT_STATUS" == "fail" ]]; then
echo "Unformatted files:"
GOFMT_COUNT=0
for f in "${GOFMT_FINDINGS[@]}"; do
GOFMT_COUNT=$((GOFMT_COUNT + 1))
if [[ $LIMIT -gt 0 && $GOFMT_COUNT -gt $LIMIT ]]; then
echo " ... ($(( ${#GOFMT_FINDINGS[@]} - LIMIT )) more items truncated)"
break
fi
echo " $f"
done
else
echo "OK"
fi
echo ""
echo "=== go vet ==="
if [[ "$GOVET_STATUS" == "fail" ]]; then
if [[ $LIMIT -gt 0 ]]; then
GOVET_ARR=()
while IFS= read -r line; do
GOVET_ARR+=("$line")
done <<< "$GOVET_OUTPUT"
for (( i=0; i<${#GOVET_ARR[@]} && i<LIMIT; i++ )); do
echo "${GOVET_ARR[$i]}"
done
if [[ ${#GOVET_ARR[@]} -gt $LIMIT ]]; then
echo "... ($(( ${#GOVET_ARR[@]} - LIMIT )) more items truncated)"
fi
else
echo "$GOVET_OUTPUT"
fi
else
echo "OK"
fi
echo ""
echo "=== golangci-lint ==="
if [[ "$LINT_STATUS" == "skip" ]]; then
echo "Skipped (not installed)"
elif [[ "$LINT_STATUS" == "fail" ]]; then
if [[ $LIMIT -gt 0 ]]; then
LINT_ARR=()
while IFS= read -r line; do
LINT_ARR+=("$line")
done <<< "$LINT_OUTPUT"
for (( i=0; i<${#LINT_ARR[@]} && i<LIMIT; i++ )); do
echo "${LINT_ARR[$i]}"
done
if [[ ${#LINT_ARR[@]} -gt $LIMIT ]]; then
echo "... ($(( ${#LINT_ARR[@]} - LIMIT )) more items truncated)"
fi
else
echo "$LINT_OUTPUT"
fi
else
echo "OK"
fi
echo ""
if [[ $FAILED -eq 1 ]]; then
echo "Pre-review checks FAILED — fix issues before manual review."
else
echo "All pre-review checks passed."
fi
fi
exit $FAILED
Related skills
FAQ
What does go-code-review do?
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code changes, even if the user doesn't explicitly request a style rev
When should I use go-code-review?
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or when reviewing any Go code changes, even if the user doesn't explicitly request a style rev
What are common prerequisites?
--- name: go-code-review description: Use when reviewing Go code or checking code against community style standards.
Is Go Code Review safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.