Teaching AI Modern Go: Solving the 'Stuck-in-the-Past' Problem with Antigravity
Source: Dev.to
The Problem: Outdated Defaults
LeetCode – 1. Two Sum
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
}
Prompting the agent with write benchmark for TwoSum function typically yields a benchmark 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)
}
}
Issues
- Uses the classic
for i := 0; i < 1000; i++loop instead of the modernfor i := range 1000. - Relies on
b.Nmanually rather than the newerb.Loop()method introduced in Go 1.24.
The Solution: Agent Rules
In Antigravity, a Rule is a Markdown file that defines constraints, preferences, and style guides for the agent.
(See the documentation for setup details.)
1. Modernizing Benchmarks (benchmark.md)
// benchmark.md
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() {
// ... code to measure ...
}
// ... cleanup ...
}
Always use b.Loop() instead of b.N in benchmarks.
2. Modernizing Loops (loops.md)
// loops.md
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
}()
}
// Integer ranges are now supported.
for i := range 10 {
fmt.Println(10 - i)
}
The Result
After adding the rule files and clicking Reload Rules, the agent respects the new conventions. Prompting again:
write benchmark for TwoSum function
produces a plan like:
[NEW] search_test.go
* Create `search_test.go`.
* Implement `BenchmarkTwoSum`.
* Use `b.Loop()` structure.
* Construct a large slice of integers and a target that tests worst/average cases.
Updated Benchmark
func BenchmarkTwoSum(b *testing.B) {
nums := make([]int, 1000)
for i := range 1000 {
nums[i] = i
}
target := 1997
for b.Loop() {
TwoSum(nums, target)
}
}
The code now follows the latest Go specifications and is cleaner overall.
Going Further: Enforcing Style Guides
Rules can also enforce broader style guides. For example, you could import the entire Uber Go Style Guide or any custom conventions.
Example: Enforcing Error Wrapping (errors.md)
// errors.md
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
}
By defining such rules, you prevent the LLM from falling back to outdated patterns and ensure the generated code aligns with your team’s tools and style. Rules bridge the gap between the LLM’s knowledge and the code you actually want.