Memory Cache
Level: Intermediate 30–60 minConcepts: StatePerformance
Solutions: C# | TypeScript | Python
A memory cache is a straightforward storage paradigm. One suitable example of a memory cache is a dictionary, where values are stored and retrieved using a key.
Distinct from a dictionary, the data in a memory cache can go stale based on a specified time to live (TTL) duration.
Additionally, it has a maximum capacity. Once the cache’s capacity reaches its limit, the least-recently-used key-value pair is removed and replaced with the incoming value.
This particular cache should be able to hold a maximum of 100 entries, with a TTL of 60 seconds.
It is important to note that both the key and the value are always strings.
Bonus
- Enable the TTL and maximum size to be read from a configuration file. Make sure to handle scenarios where the configuration file is not present or contains negative TTL or size limits.
- Additionally, allow the value type to be specified through the configuration file. This includes options such as int, double, or MyAwesomeType. The cache should accommodate both primitive and custom object types.
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/memory-cache. Twenty scenarios across construction, put/get, capacity + LRU eviction, and TTL expiry (lazy and explicit sweep) — shared across all three languages — with a generic Cache<V> aggregate, Clock collaborator, CacheBuilder + FixedClock test doubles, and named domain exceptions for invalid capacity/TTL.
- C# (.NET 8, xUnit, FluentAssertions) — walkthrough
- TypeScript (Node 20, Vitest, strict types) — walkthrough
- Python (3.11, pytest, OrderedDict, Protocol clock) — walkthrough
This kata ships in Agent Full-Bake (F3) mode: one commit per language with the full domain design landing together. The walkthroughs read as design rationale — why Cache<V> is generic, why Clock is injected, why evictExpired() is an explicit sweep like library-management’s ExpireReservations and video-club-rental’s MarkOverdueRentals. The reference scopes to the twenty core scenarios; the bonus items (configuration-file loading, runtime-configurable value types) are deliberately out of scope — the generic Cache<V> already shows the value-type seam, and config loading is an application-layer concern rather than a domain one. See the repo’s Gears section for when middle gear is the right call.
Related reading
- Test Deletion Is a Privileged Operation
The cheapest way for an agent to make a failing test pass is to delete it. That is logical for the agent and catastrophic for the codebase. Tests are append-only by default. Deletion needs a human author, a separate commit, and a separate review. - The Instruction File Is Not the Discipline
Teams that want their agent to do TDD reach for the instruction file. The TDAD paper measured this directly: adding a 'do TDD' instruction raised regressions by nearly two-thirds. Contextual test discovery cut them by seventy percent. The instruction is theatre. The codebase's test surface is the discipline. - Tamper-Resistant Test Design Is What the Suite Now Owes the Codebase
Tests were a contract the team made with itself. Agents made the suite an attack surface. Tamper-resistant test design is what the suite now owes the codebase: expensive to spoof, redundantly verified, partially hidden, and impossible to delete without a separate review.