An expiry boundary in a coding agent session
The task looked small: update an in-memory cache so that expired entries could never be returned. The repository already had a test runner and a basic cache implementation. A coding agent could inspect both. The important choice was not which agent to use, but whether to ask for the behavior change or establish an executable contract first.
Here is the same task approached two ways: implementation first, followed by tests; and tests first, followed by implementation. Both can produce a correct patch. They expose different mistakes at different points.
§ The same request, with a different starting point
Implementation first: “Fix cache expiry and add tests.” The agent has room to infer what expiry means. It might check timestamps during reads, introduce periodic cleanup, or treat an entry as valid at its exact expiry time. Tests written afterward may simply confirm those choices.
Tests first: Specify the observable rule before requesting code: an entry is readable before its expiry timestamp and missing at or after that timestamp. Expired entries must not affect unrelated keys. Ask the agent to inspect existing conventions, then add tests without changing production code.
The second request is longer because it makes a product decision explicit. Tests cannot resolve an ambiguous requirement by themselves.
§ Defining the evidence before the patch
Implementation first: Review begins with a diff containing both behavior and assertions. This is convenient, but it creates a circularity risk: the implementation and its tests may share the same mistaken interpretation.
Tests first: Review begins with a smaller test-only diff. Use an injected clock or the repository’s existing fake timers rather than real sleeps. Cover a read just before expiry, exactly at expiry, and after expiry. Include a separate live key to catch accidental broad deletion.
Prefer public behavior over internal details. Unless cleanup itself is part of the contract, assert that an expired lookup returns a miss, not that a particular private map entry was deleted. This leaves the implementation flexible.
§ Checking that red means something
Implementation first: A passing suite shows that the final code satisfies the assertions. It does not show whether those assertions would detect the original defect.
Tests first: Run the new tests against unchanged production code. Ask the agent to report the command, exit status, and relevant failure. The boundary test should fail because an expired value is returned, not because of a broken import or an unavailable dependency.
Not every new test must fail. A pre-expiry case may already pass. The useful evidence is that at least one targeted assertion detects the missing behavior. If execution is unavailable, record that limitation rather than treating a proposed test as a verified failure.
§ Giving the agent a narrower implementation step
Implementation first: The agent may combine the fix with cleanup, dependency changes, or a new cache abstraction. Those changes can be reasonable, but they expand the review surface.
Tests first: After reviewing the failing tests, request the smallest production change that satisfies them. Tell the agent not to weaken assertions or change expiry semantics without approval. Run the focused tests, then the broader relevant suite, and inspect the final diff for unrelated edits.
A green result remains evidence, not proof. Missing cases, integration behavior, and concurrency assumptions still need review. Refactoring can follow once the behavior is stable.
§ Keeping the comparison useful across tools
Vana API works with Claude Code, Codex, and Gemini CLI, so this workflow does not require committing to one coding client. Its OpenAI-compatible endpoint also supports Anthropic Messages and Gemini native protocols. Create an account when you want to try the workflow in your existing tool; usage is billed per real token with no monthly fee.
When comparing sessions, keep the repository revision, task wording, permissions, and test commands constant. Record retries and human corrections as well as token usage. A shorter session that silently changes the contract is not necessarily the cheaper result.
§ Where each approach fits
Implementation-first work can be useful for exploration, especially when the interface is still unsettled. Tests-first work is particularly useful when a small boundary condition matters and the expected behavior can be stated clearly. In this cache change, its main benefit is modest but concrete: the reviewer agrees on the expiry rule before the agent makes that rule harder to see inside a patch.