I think I've seen all of the Redis/Valkey issues the author mentioned in production.

* Outages where Valkey had no memory policy, ate all the memory, and then caused write errors to its append-only file. Bonus points for another one where the disk itself was full, and AOF writes failed.

* 500s where Redis was fully expected to be live, running, and populated with data for every user, and no fallback to a slower path.

* Creative uses of sorted sets and other data structures which depended on the sets never being evicted.

Despite the observations from the field, I think it's still hard to recommend memcache ahead of Redis. It can be difficult to architect an app to have a memcache-friendly cache layout.

I'd almost guarantee a large enough team using memcache will find a way to need Redis. And then we're maintaining 2 cache technologies.

Once someone decides they want to use redis as something other than a cache, you sort of do have 2 cache technologies anyway. You can't use a redis instance that is configured for caching for any other purpose (caching instance must have eviction, non-caching instance must not have eviction). You need a second redis with a different configuration.

Honestly designing your app to have a "memcache-friedly cache layout" is the same thing as designing it to have a redis-friendly cache layout. The pattern for this kind of application cache is identical: "get, and if not there, calculate and set".

Redis can also cache sets with correlated sub-data as part of the model/eviction pattern.. this can give you trends beyond simple k/v

I tend to write an abstraction interface, if there isn't already one, where you request a key and pass an async function/lambda that will return the value from source in case of a cache miss.

    var value = cache.lookup<T>(
      keyname, 
      () => db.query<T>(...), 
      TimeSpan.FromMinutes(5) // or CacheOptions
    );
This way it can fallback/insert on a cache miss directly...

Not maintaining 2 cache technologies is always a winning argument.