Skip to content

Conversation

@devin-ai-integration
Copy link

@devin-ai-integration devin-ai-integration bot commented Jan 21, 2026

Closes #121

Summary

The gopls check step in make lint was slow (~3.5 minutes) because it ran go run each time, recompiling gopls. This PR speeds it up by:

  1. Installing gopls once via go install instead of using go run each time
  2. Supporting optional parallelism via GOPLS_PARALLEL env var (defaults to 1 to avoid memory pressure)

Before: ~3.5 minutes
After: ~3 minutes with default settings (sequential), faster with GOPLS_PARALLEL set higher

Changes

  • Modified tools/lint-go-gopls.sh to:
    • Install gopls binary via go install before running checks
    • Verify gopls installation succeeded before proceeding
    • Support configurable parallelism via GOPLS_PARALLEL env var (default: 1)
    • Use xargs -0 -P "$PARALLEL" -n 100 with null-delimited input to handle filenames with spaces

Testing

Verified that the linter still catches errors by intentionally adding a syntax error to services/user/user.go and confirming it was detected.

Updates since last revision

Addressed feedback from @pedrogaudencio: parallel gopls processes can cause memory/swap pressure on local machines. Changed parallelism to be opt-in via GOPLS_PARALLEL env var, defaulting to 1 (sequential). CI can set GOPLS_PARALLEL=4 or higher if desired.

Human Review Checklist

  • Confirm the go install approach works correctly in CI environments
  • Verify GOPLS_PARALLEL env var is respected when set
  • Note: stderr from gopls check is still suppressed (2>/dev/null) - this is intentional to filter noise but could hide some edge case errors

Link to Devin run: https://app.devin.ai/sessions/c29881ff27ee429e9b2592f7f655c998
Requested by: Greg Slepak (@taoeffect)

@devin-ai-integration
Copy link
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional flags.

Open in Devin Review

The gopls check was slow (~3.5 minutes) because it processed all ~2800
Go source files sequentially. This change:

1. Installs gopls once instead of using 'go run' each time
2. Uses xargs with parallel execution (-P nproc) to process files
   in batches of 100 across all available CPU cores

This reduces the lint time from ~3.5 minutes to ~1 minute (about 68%
faster).

Closes #121

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
@devin-ai-integration devin-ai-integration bot force-pushed the devin/1769030545-fix-gopls-lint-performance branch from 622d3fc to 9a94079 Compare January 21, 2026 21:42
@devin-ai-integration devin-ai-integration bot changed the title fix: speed up gopls lint by using ./... instead of individual files fix: speed up gopls lint with parallel processing Jan 21, 2026
Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 3 additional flags in Devin Review.

Open in Devin Review

Add lint-frontend and lint-backend steps to run before their respective
test steps, ensuring linting passes before tests run.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

Linting is already handled by pull-compliance.yml which runs on all PRs.
No need to duplicate lint steps in pull-tests.yml.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

- Add cross-platform CPU detection (nproc for Linux, sysctl for macOS)
- Add verification that gopls was installed successfully
- Use null-delimited format (-0) with xargs to handle filenames with spaces
- Remove stderr suppression from go install to surface installation errors

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Collaborator

@pedrogaudencio pedrogaudencio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for looking into this - switching from go run to installing gopls is a nice win and makes it cleaner.

There is one concern tho: the xargs -P "$NPROC" -n 100 approach ends up launching a bunch of gopls check processes at the same time. On my laptop that caused heavy memory/swap pressure (and lag) and actually made make lint slower overall, even tho it’s “more parallel”.

I’d suggest we keep the “install + reuse gopls” part, but run run gopls check in a single invocation (or sequential batches). If we still want parallelism, maybe make it a configurable and default to something conservative (e.g. GOPLS_PARALLEL=1 locally, and higher in CI).

I'm happy to help adjust the script to “install once + sequential check” vs “configurable parallelism”.

Address feedback from pedrogaudencio: parallel gopls processes can cause
memory/swap pressure on local machines. Changed to:

- Default GOPLS_PARALLEL=1 (sequential) for local development
- CI can set GOPLS_PARALLEL to a higher value if desired
- Removed automatic CPU detection since parallelism is now opt-in

The 'install gopls once' optimization is kept, which still provides
a speedup over the original 'go run' approach.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
@devin-ai-integration devin-ai-integration bot changed the title fix: speed up gopls lint with parallel processing fix: speed up gopls lint with install optimization and optional parallelism Jan 23, 2026
@devin-ai-integration
Copy link
Author

Thanks for the feedback @pedrogaudencio! I've updated the script to address your concern:

  • Parallelism is now configurable via GOPLS_PARALLEL env var
  • Default is 1 (sequential) to avoid memory pressure on local machines
  • CI can set GOPLS_PARALLEL=4 or higher if desired

The "install gopls once" optimization is kept, which still provides a speedup over the original go run approach (~3 minutes vs ~3.5 minutes with sequential execution).

Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

# Install gopls if not already installed, then use the installed binary for
# faster execution. Using 'go run' each time adds overhead.
"$GO" install "$GOPLS_PACKAGE"
GOPLS_BIN=$("$GO" env GOPATH)/bin/gopls
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 GOPLS_BIN path doesn't account for GOBIN environment variable

The script determines the gopls binary path using $("$GO" env GOPATH)/bin/gopls, but when the GOBIN environment variable is set, go install places binaries in $GOBIN instead of $GOPATH/bin.

When a user has GOBIN configured (a common setup for Go developers who want binaries in a custom location), the script will:

  1. Successfully install gopls (to $GOBIN/gopls)
  2. Look for gopls at $GOPATH/bin/gopls (wrong location)
  3. Fail the -x check and exit with "Error: Failed to install gopls"

This causes the lint step to fail with a misleading error message even though gopls was installed correctly. The proper approach would be to use $("$GO" env GOBIN) if set, falling back to $("$GO" env GOPATH)/bin otherwise.

Recommendation: Use the correct binary path that accounts for GOBIN:

GOPLS_BIN=$({ "$GO" env GOBIN; } | grep . || echo "$("$GO" env GOPATH)/bin")/gopls

Or more readably:

GOBIN_DIR=$("$GO" env GOBIN)
if [[ -z "$GOBIN_DIR" ]]; then
  GOBIN_DIR=$("$GO" env GOPATH)/bin
fi
GOPLS_BIN="$GOBIN_DIR/gopls"
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linting takes way too long

2 participants