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
}
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)
}
}
The issues:
- It uses
for i := 0; i < 1000; i++instead of the modern Go 1.22for i := range 1000. - It uses
b.Nmanually instead of the modern Go 1.24b.Loopmethod.
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.
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)
}
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.
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)
}
}
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
}
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)