DEV Community

Roman Budnikov
Roman Budnikov

Posted on

Teaching AI Modern Go: Solving the "Stuck-in-the-Past" Problem with Antigravity

Last month, I changed my workflow. I switched from vim as my primary code editor to Google's new Antigravity AI IDE. It's an agentic platform that has significantly boosted my performance in writing, testing, and modifying code.

Go is evolving fast. Recent versions introduced helpful features like integer ranges (Go 1.22 for i := range 10) and standard library improvements (Go 1.24 testing.B.Loop). Unfortunately, most LLMs suffer from a "stuck-in-the-past" problem. Because they are trained on older code, they often suggest outdated code that works, but are no longer "idiomatic" or preferred in modern Go.

Here is how I use .agent/rules to teach the LLM new tricks and enforce modern best practices.

The Problem: Outdated Defaults

For example lets take LeetCode problem: 1. Two Sum. Here is an example solution in Go:

func TwoSum(nums []int, target int) []int {
    seen := make(map[int]int)
    for i, num := range nums {
        comp := target - num

        if idx, ok := seen[comp]; ok {
            return []int{idx, i}
        }

        seen[num] = i
    }

    return nil
}
Enter fullscreen mode Exit fullscreen mode

If you simply prompt the Agent with: write benchmark for TwoSum function, it will likely generate a benchmark that looks like this:

func BenchmarkTwoSum(b *testing.B) {
    // Create a larger input for benchmarking
    nums := make([]int, 1000)
    for i := 0; i < 1000; i++ { // Old style loop
        nums[i] = i
    }
    target := 1997 

    b.ResetTimer()
    for i := 0; i < b.N; i++ { // Old style benchmark loop
        TwoSum(nums, target)
    }
}
Enter fullscreen mode Exit fullscreen mode

The issues:

  • It uses for i := 0; i < 1000; i++ instead of the modern Go 1.22 for i := range 1000.
  • It uses b.N manually instead of the modern Go 1.24 b.Loop method.

The Solution: Agent Rules

To handle this, we can use Rules. In Antigravity, a Rule is simply a Markdown file where you define constraints, stack preferences, and style guides. (See the documentation for setup details).

We can create specific rule files to "patch" the LLMs knowledge base.

1. Modernizing Benchmarks (benchmark.md)

Create a file named benchmark.md to instruct the agent on the new testing API:

The `b.Loop` method is now the preferred way to write benchmarks in Go 1.24+.


func BenchmarkExample(b *testing.B) {
    // ... setup ...
    for b.Loop() {
        // optional timer control for in-loop setup/cleanup is handled automatically
        // ... code to measure ...
    }
    // ... cleanup ...
}

Always use b.Loop() instead of b.N in benchmarks.
Enter fullscreen mode Exit fullscreen mode

2. Modernizing Loops (loops.md)

Create a file named loops.md to enforce modern iteration syntax:

Each iteration creates a new instance of the variable. There is no need to declare `v := v` inside the loop for closure safety.

for _, v := range data {
    go func() {
        // safe to use v here directly
    }()
}

`For` loops may now range over integers. We should use `for i := range 10` now.

for i := range 10 {
    fmt.Println(10 - i)
}
Enter fullscreen mode Exit fullscreen mode

The Result

After creating these files, click the Reload Rules button in the editor. Now, retry the prompt: write benchmark for TwoSum function or be specific: apply benchmark and loops rules to generate a benchmark.

You will see the Agent's implementation plan acknowledge the new context:

[NEW] search_test.go
* Create `search_test.go`.
* Implement `BenchmarkTwoSum`.
* Use `b.Loop()` structure.
* Construct a large slice of integers and a target that is found near the end or not found to test worst/average cases.
Enter fullscreen mode Exit fullscreen mode

The resulting code is cleaner, modern, and follows the latest Go specs:

func BenchmarkTwoSum(b *testing.B) {
    nums := make([]int, 1000)
    for i := range 1000 {
        nums[i] = i
    }
    target := 1997 

    for b.Loop() {
        TwoSum(nums, target)
    }
}
Enter fullscreen mode Exit fullscreen mode

Going Further: Enforcing Style Guides

This feature is powerful because it keeps your code consistent. It forces the LLM to follow your team's rules instead of guessing or using random styles.

For example, you can feed the LLM the entire Uber Go Style Guide. Or use specific libraries.

Example: Enforcing Error Wrapping errors.md

If you want to ensure all errors are wrapped using cockroachdb/errors instead of standard returns or fmt.Errorf:

Do not simply `return err`. Always wrap errors using `github.com/cockroachdb/errors` to provide stack traces and context. Use `Wrap(error, string)` or `Wrapf(error, string, ...interface{})`.

func getUser(id int) error {
    if err := someDatabaseCall(id); err != nil {
        // Wrap the original error with added context
        return errors.Wrapf(err, "some database call id %d", id)
    }

    return nil
}
Enter fullscreen mode Exit fullscreen mode

It is annoying when LLMs provide code that is stuck-in-the-past By using .agent/rules in Antigravity, you can stop fighting the LLMs old habits. Instead, you can collaborate with an Agent that understands your specific tools and style. Rules bridge the gap between what the LLM knows and the code you actually want.

Top comments (0)