Generating code is easier. Verifying intent still takes work.

AI coding tools make it easier to produce a plausible implementation. That changes how quickly we can propose a solution, but it does not decide whether the solution follows our business rules. A function can compile, look clean, and still count a retry incorrectly or accept an invalid configuration.

GitHub’s guidance for Copilot explicitly calls for reviewing and testing generated suggestions. I see unit tests as a practical part of that review: they turn a few important expectations into executable checks that survive the original conversation with the coding assistant.

The argument is not that every AI-assisted edit needs a large test suite. It is that important behavior deserves evidence, regardless of who or what wrote the implementation. The faster we generate changes, the more useful a small, trusted feedback loop becomes.

Write expectations independently of the implementation

A weak workflow asks an assistant to implement a function and then generate tests that match it. If both steps share the same mistaken assumption, the result can be a green test suite around the wrong behavior. Passing tests demonstrate agreement with the assertions, not automatic agreement with the requirements.

Start with examples a person can explain without reading the function. What inputs are valid? What happens at the boundary? What should fail? Give those expectations to the assistant, review the resulting assertions, and keep the requirements as the reference when code and tests disagree.

AI is useful for suggesting additional cases and writing repetitive test scaffolding. Reviewing the expected outputs remains engineering work. Asking another model to review the code can add a perspective, but it does not replace an independently defined rule.

A small Go test can expose a big ambiguity

Consider a retry policy. Does a maximum of three attempts mean three calls in total, or three retries after the first call? Both interpretations can produce tidy code. Before implementation, choose a contract: maxAttempts includes the initial call, and attempt is the zero-based index of the call that just finished.

Under that contract, attempts zero and one allow another call, while attempt two does not. This example also chooses to return false for negative attempts or non-positive limits. That is an explicit policy for this helper, not a universal rule for retry systems. A public configuration API might reject invalid values with an error instead.

Verify performance changes with a reproducible Go benchmark

package retries

import "testing"

// maxAttempts includes the initial call; attempt is zero-based.
func CanRetry(attempt, maxAttempts int) bool {
    return attempt >= 0 && maxAttempts > 0 && attempt < maxAttempts-1
}

func TestCanRetry(t *testing.T) {
    cases := []struct {
        name        string
        attempt     int
        maxAttempts int
        want        bool
    }{
        {"retry after first call", 0, 3, true},
        {"retry after second call", 1, 3, true},
        {"stop after third call", 2, 3, false},
        {"already past the limit", 3, 3, false},
        {"one call means no retries", 0, 1, false},
        {"negative attempt", -1, 3, false},
        {"zero allowed calls", 0, 0, false},
        {"negative limit", 0, -1, false},
    }
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            if got := CanRetry(tc.attempt, tc.maxAttempts); got != tc.want {
                t.Errorf("CanRetry(%d, %d) = %v; want %v",
                    tc.attempt, tc.maxAttempts, got, tc.want)
            }
        })
    }
}
  • Save the complete example as retry_test.go in a Go module. Run: go test ./...
  • Temporarily change the final comparison to attempt < maxAttempts. The last-call test should fail.
  • Restore the implementation before committing. The deliberate failure checks that the test can detect the boundary mistake.

Coverage helps find gaps; assertions define the checks

Coverage can show which statements ran during a test. It cannot tell you whether the expected outcome is correct or whether you covered the important business cases. A test that executes a function without checking its result may increase coverage while providing little protection.

Use coverage to guide investigation, then inspect the assertions. For this retry helper, the boundary between the last allowed retry and the first forbidden retry matters more than adding several extra cases that all follow the same successful path. A deliberate local mutation is one way to check that an assertion notices the behavior you care about.

Keep unit tests within their boundary

For a Go service or Kubernetes controller, unit tests are a good place to exercise configuration validation, policy decisions, and transformations of input data. Prefer deterministic tests that avoid unnecessary network calls or dependence on wall-clock timing.

They do not prove that RBAC allows a real operation, an API server enforces a schema, or a complete provisioning workflow succeeds. Those questions need integration or end-to-end checks at the relevant boundary. Mocking every dependency can make a test pass while hiding a broken assumption about the real system.

Testing Kubernetes reconciliation

Use AI to shorten the feedback loop

My recommended workflow is to define the behavior, ask AI for a small implementation, and review both the diff and the tests. Run the targeted tests while iterating, then run the relevant broader checks before merging. Treat a failing test as information to investigate, rather than a reason to change the expectation until the build is green.

Keep the valuable tests close to the code and run them in CI. Future refactors, whether manual or AI-assisted, then face the same documented expectations. The goal is not more test files. It is a codebase where we can explain what matters and demonstrate that a change still respects it.

References & further reading

Need help with your Go backend?

I work with engineering teams on focused Go and Kubernetes platform engagements, with limited consulting availability.

Explore Go consulting services