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
- The Test Pyramid Was an Economic Argument
The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake. - Agents Amplify Whatever Vocabulary They Find
Agents do not bring vocabulary to your codebase. They mirror what's already there. Strong vocabulary gets agent contributions that read like the team. Weak vocabulary gets faster mediocrity. The work you did for craft reasons just became leverage. - The Hidden Output of TDD Was Never Code
The visible artifact of TDD is a passing test suite. The actual artifact, the one that compounded value over the life of the codebase, was the vocabulary the team built while writing it. The dictionary, not the regression net.