{"slug": "don-t-review-ai-code-review-its-clock", "title": "Don't Review AI Code. Review Its Clock.", "summary": "A Dev.to post by Taylor Wang argues that reviewing AI-written rate limiters should focus on whether the code allows injecting a fake clock, not on the bucket math, because injectable time turns generated code into a verifiable table. The post notes that Go 1.25 stabilized testing/synctest and .NET 8 shipped time abstraction, showing platform teams already prioritize this seam. Rachel Goldstein's article frames this as the difference between code you can verify and code you have to trust, recommending prompts that require a clock/now parameter and a mechanical review rule to reject direct system time calls.", "body_md": "[AI](https://sourcefeed.dev/c/ai)Article\n\n# Don't Review AI Code. Review Its Clock.\n\nInjectable time turns an LLM-written rate limiter from trust-me code into a table you can verify in microseconds.\n\n[Rachel Goldstein](https://sourcefeed.dev/u/rachel_goldstein)\n\nA rate limiter is a nasty thing to review, because reading it tells you almost nothing. The code compiles, the variable names are plausible, the refill math looks right at a glance — and every real defect lives at a boundary you can't see until a client crosses it at 2 a.m. and your support queue fills with 429 complaints. Now multiply that by the way this code increasingly gets written: you ask an LLM for a token bucket, it hands you forty confident lines, and you're the only human who will ever look at them.\n\nA recent Dev.to post by Taylor Wang made a small argument I think deserves a bigger frame: when you review an AI-written rate limiter, the part worth scrutinizing isn't the bucket math. It's whether the code lets you inject a fake clock. That's not a testing nicety. It's the difference between code you can verify and code you have to trust — and with generated code, trust is exactly the thing you don't have.\n\n## The seam is the review\n\nMost generated rate limiters read the wall clock directly — `time.time()`\n\n, `Date.now()`\n\n, `DateTime.UtcNow`\n\n— because that's what the bulk of tutorial code on the internet does, and models reproduce the majority pattern. The moment the limiter reaches for the system clock itself, its behavior becomes a function of when you run it. Your test either sleeps through real seconds (slow, flaky, and still imprecise) or it doesn't test the boundaries at all.\n\nFlip the dependency and everything changes. Pass time in — a clock object, or just an explicit `now`\n\nargument — and the limiter collapses into a pure function: given this configuration, this key, and this sequence of timestamps, which requests pass? That's a table you can write down before you read a single line of the generated implementation. Which is the right order of operations: start from the failures you want to catch, not from the model's confident prose.\n\n``` python\ndef test_burst_then_refill(limiter):\n    clock = FakeClock(start=0.0)\n    rl = limiter(rate=2, burst=3, clock=clock)  # 2 req/s, bucket of 3\n\n    timeline = [(0.000, True), (0.050, True), (0.100, True),  # burst drains\n                (0.450, False), (0.950, False),               # too soon\n                (1.100, True)]                                # ~1s: 2 tokens back\n    for t, expected in timeline:\n        clock.now = t\n        assert rl.allow(\"key-a\") is expected, f\"at {t}s\"\n```\n\nTen lines, runs in microseconds, and it catches the classic generated-code bugs: refill arithmetic that rounds in the client's favor, integer division silently truncating fractional tokens, and state that's accidentally global — where exhausting `key-a`\n\n's bucket starts denying `key-b`\n\n. That last one is worth an explicit test with two keys, because it's invisible in every single-key demo the model was trained on.\n\nIf you're prompting for this code, put the seam in the prompt: \"accept a clock/now parameter; no direct system time calls.\" You'll get more testable output, and you've also created a mechanical review rule — grep the diff for direct clock reads and reject on sight. That's a check a reviewer (or CI) can apply without understanding the algorithm, which is precisely what scales when the volume of generated code outstrips human attention.\n\n## The ecosystem already voted\n\nIf injectable time sounds like ceremony, notice that platform teams have spent the last few years building it into their foundations. Go 1.25 stabilized [ testing/synctest](https://go.dev/blog/testing-time), which runs a test in a \"bubble\" where the\n\n`time`\n\npackage itself is fake and only advances when every goroutine is durably blocked — five-second sleeps complete in microseconds, deterministically. .NET 8 shipped the [abstraction with a first-party](https://learn.microsoft.com/en-us/dotnet/core/extensions/timeprovider-testing)\n\n`TimeProvider`\n\n`FakeTimeProvider`\n\nyou `Advance()`\n\nby hand. Tokio has let Rust tests [pause and auto-advance the runtime clock](https://docs.rs/tokio/latest/tokio/time/fn.pause.html)for years, and\n\n[Jest's fake timers](https://jestjs.io/docs/timer-mocks)are table stakes in JavaScript.\n\nAnd the canonical Go limiter, [ golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate), bakes the seam into its public API:\n\n`AllowN(now, n)`\n\ntakes the timestamp as an argument. The maintainers of the reference implementation decided time is an input, not ambient state.That convergence matters for the AI-codegen era more than any of those teams intended. Everyone's review advice for generated code says the same vague thing — \"verify behavior, don't trust vibes\" — but verification has a cost, and the fake clock is what makes the cost low enough that you'll actually pay it. Deterministic seams turn \"review this algorithm\" into \"check this table,\" and tables are cheap.\n\n## What the fake clock can't tell you\n\nBe honest about the limits, because a green deterministic suite is its own trap. A single-threaded timeline proves the logic, not the locking — a limiter can pass every boundary test and still corrupt its bucket state under concurrent access, so you still want a race-detector pass or a hammering test with real threads. It also says nothing about capacity or memory growth across a few hundred thousand keys.\n\nAnd the technique mostly evaporates for distributed limiters. If your bucket lives in Redis and the arithmetic runs in a Lua script against Redis's own `TIME`\n\n, there's no seam to inject — you're back to integration tests and careful reading. That's a real argument for keeping generated limiters at the edge of what they're good for: single-process, best-effort throttling. For the endpoints where a limiter failure costs money — payments, auth, anything compliance-shaped — use the gateway's limiter or a maintained library, not forty lines from a chat window. On that point the original post and I fully agree.\n\nThe transferable lesson is bigger than rate limiting, though. Retry backoff, cache TTLs, token expiry, schedulers — anything an LLM writes that touches time, randomness, or I/O should be reviewed at the seam first. Does the nondeterminism come in as a dependency you control? If yes, twenty minutes buys you a verdict instead of a vibe. If no, send it back before you bother reading the math. The model will happily regenerate; your incident channel is less forgiving.\n\n## Sources & further reading\n\n-\n[The Fake Clock Is the Part of an AI-Written Rate Limiter Worth Reviewing](https://dev.to/codepy_1473/the-fake-clock-is-the-part-of-an-ai-written-rate-limiter-worth-reviewing-186d)— dev.to -\n[Testing Time (and other asynchronicities)](https://go.dev/blog/testing-time)— go.dev -\n[Testing with FakeTimeProvider](https://learn.microsoft.com/en-us/dotnet/core/extensions/timeprovider-testing)— learn.microsoft.com -\n[tokio::time::pause](https://docs.rs/tokio/latest/tokio/time/fn.pause.html)— docs.rs -\n[golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate)— pkg.go.dev\n\n[Rachel Goldstein](https://sourcefeed.dev/u/rachel_goldstein)· Dev Tools Editor\n\nRachel has been embedded in the developer tooling ecosystem for nearly eight years, covering everything from IDE wars and package-manager drama to the quiet rise of AI-assisted coding. She has a soft spot for open-source maintainers and an unhealthy number of terminal emulators installed on a single laptop.\n\n## Discussion 0\n\nNo comments yet\n\nBe the first to weigh in.", "url": "https://wpnews.pro/news/don-t-review-ai-code-review-its-clock", "canonical_source": "https://sourcefeed.dev/a/dont-review-ai-code-review-its-clock", "published_at": "2026-08-19 05:08:05+00:00", "updated_at": "2026-08-19 05:10:49.120364+00:00", "lang": "en", "topics": ["artificial-intelligence", "developer-tools", "ai-tools"], "entities": ["Taylor Wang", "Rachel Goldstein", "Dev.to", "Go 1.25", ".NET 8"], "alternates": {"html": "https://wpnews.pro/news/don-t-review-ai-code-review-its-clock", "markdown": "https://wpnews.pro/news/don-t-review-ai-code-review-its-clock.md", "text": "https://wpnews.pro/news/don-t-review-ai-code-review-its-clock.txt", "jsonld": "https://wpnews.pro/news/don-t-review-ai-code-review-its-clock.jsonld"}}