All skills
encoredev avatar

/go-code-review

@cb69bb1 official
by Encoreencoredev/skills28 stars
5

Review existing Encore Go code for best practices and common anti-patterns.

  • 1 file
  • 5.9 KB
  • Updated 5 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/encoredev/skills/go-code-review

This session only. Nothing lands on disk.

SKILL.md

≈23 tokens always: the name and description. ≈1.4k when used: this file.

Encore Go Code Review

Instructions

When reviewing Encore Go code, check for these common issues:

Critical Issues

1. Infrastructure Inside Functions

// WRONG: Infrastructure declared inside function
func setup() {
    db := sqldb.NewDatabase("mydb", sqldb.DatabaseConfig{...})
    topic := pubsub.NewTopic[*Event]("events", pubsub.TopicConfig{...})
}

// CORRECT: Package level declaration
var db = sqldb.NewDatabase("mydb", sqldb.DatabaseConfig{
    Migrations: "./migrations",
})

var topic = pubsub.NewTopic[*Event]("events", pubsub.TopicConfig{
    DeliveryGuarantee: pubsub.AtLeastOnce,
})

2. Missing Context Parameter

// WRONG: Missing context
//encore:api public method=GET path=/users/:id
func GetUser(params *GetUserParams) (*User, error) {
    // ...
}

// CORRECT: Context as first parameter
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
    // ...
}

3. SQL Injection Risk

// WRONG: String interpolation
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
rows, err := db.Query(ctx, query)

// CORRECT: Parameterized query
rows, err := sqldb.Query[User](ctx, db, `
    SELECT * FROM users WHERE email = $1
`, email)

4. Wrong Return Types

// WRONG: Returning non-pointer struct
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (User, error) {
    // ...
}

// CORRECT: Return pointer to struct
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
    // ...
}

5. Ignoring Errors

// WRONG: Ignoring error
user, _ := sqldb.QueryRow[User](ctx, db, query, id)

// CORRECT: Handle error
user, err := sqldb.QueryRow[User](ctx, db, query, id)
if err != nil {
    return nil, err
}

Warning Issues

6. Not Checking for ErrNoRows

// RISKY: Returns nil without proper error
func getUser(ctx context.Context, id string) (*User, error) {
    user, err := sqldb.QueryRow[User](ctx, db, `
        SELECT * FROM users WHERE id = $1
    `, id)
    if err != nil {
        return nil, err  // ErrNoRows returns generic error
    }
    return user, nil
}

// BETTER: Check for not found specifically
import "errors"

func getUser(ctx context.Context, id string) (*User, error) {
    user, err := sqldb.QueryRow[User](ctx, db, `
        SELECT * FROM users WHERE id = $1
    `, id)
    if errors.Is(err, sqldb.ErrNoRows) {
        return nil, &errs.Error{
            Code:    errs.NotFound,
            Message: "user not found",
        }
    }
    if err != nil {
        return nil, err
    }
    return user, nil
}

7. Public Internal Endpoints

// CHECK: Should this cron endpoint be public?
//encore:api public method=POST path=/internal/cleanup
func CleanupJob(ctx context.Context) error {
    // ...
}

// BETTER: Use private for internal endpoints
//encore:api private
func CleanupJob(ctx context.Context) error {
    // ...
}

8. Non-Idempotent Subscription Handlers

// RISKY: Not idempotent (pubsub has at-least-once delivery)
var _ = pubsub.NewSubscription(OrderCreated, "process-order",
    pubsub.SubscriptionConfig[*OrderCreatedEvent]{
        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {
            return chargeCustomer(ctx, event.OrderID)  // Could charge twice!
        },
    },
)

// SAFER: Check before processing
var _ = pubsub.NewSubscription(OrderCreated, "process-order",
    pubsub.SubscriptionConfig[*OrderCreatedEvent]{
        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {
            order, err := getOrder(ctx, event.OrderID)
            if err != nil {
                return err
            }
            if order.Status != "pending" {
                return nil  // Already processed
            }
            return chargeCustomer(ctx, event.OrderID)
        },
    },
)

9. Not Closing Query Rows

// WRONG: Rows not closed
func listUsers(ctx context.Context) ([]*User, error) {
    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)
    if err != nil {
        return nil, err
    }
    // Missing: defer rows.Close()
    
    var users []*User
    for rows.Next() {
        users = append(users, rows.Value())
    }
    return users, nil
}

// CORRECT: Always close rows
func listUsers(ctx context.Context) ([]*User, error) {
    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        users = append(users, rows.Value())
    }
    return users, rows.Err()
}

Review Checklist

  • All infrastructure at package level
  • All API endpoints have context.Context as first parameter
  • SQL uses parameterized queries ($1, $2, etc.)
  • Response types are pointers
  • Errors are handled, not ignored
  • sqldb.ErrNoRows checked where appropriate
  • Internal endpoints use private not public
  • Subscription handlers are idempotent
  • Query rows are closed with defer rows.Close()
  • Migrations follow naming convention (1_name.up.sql)

Output Format

When reviewing, report issues as:

[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern  
[GOOD] Notable good practice observed

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at cb69bb1. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 5 months ago
Other metadata
when_to_use
User is reviewing a pull request, auditing existing code, or checking for Encore-specific anti-patterns before merging — infrastructure declared inside functions, missing service files, wrong import paths, raw `errors.New(...)` thrown instead of `errs.B`, untyped APIs, panicking in handlers. SKIP for greenfield code being actively written. Trigger phrases: "audit", "review", "before merge", "PR review", "anti-patterns", "code smell", "lint this".
  • Go
  • API
  • encore
  • code-review
  • best-practices
  • anti-patterns
  • sql
  • pubsub
  • linting

README badge

README badge for encoredev/skills/go-code-review

Audits Encore Go code for critical anti-patterns like infrastructure declared inside functions, missing context parameters, SQL injection risks, and non-idempotent pub/sub handlers. Checks against Encore-specific best practices including package-level declarations, parameterized queries, pointer return types, and proper error handling patterns.

Generated from the current SKILL.md.

What Encore-specific anti-patterns does this skill check for?
The skill checks for infrastructure declared inside functions instead of at package level, missing context parameters in API handlers, SQL injection via string interpolation, wrong return types (non-pointer structs), and ignoring errors. It also flags public endpoints that should be private, non-idempotent pubsub handlers, and unclosed query rows.
Does this work with existing Go codebases or only new projects?
This is for reviewing existing code in pull requests or audits. Skip it for greenfield code being actively written.
What should I do if the skill finds critical issues?
The skill reports issues with severity level (CRITICAL, WARNING, GOOD) and file location. Critical issues like infrastructure inside functions or SQL injection risks should be fixed before merging.
Does this check for general Go best practices or only Encore-specific ones?
It focuses on Encore-specific patterns and anti-patterns, like service-level declarations, the errs package, and pubsub idempotency requirements. General Go practices like error handling are included only where they intersect with Encore's framework constraints.

Generated from the current SKILL.md. These answers refresh after source changes.