Product Decisions This Supports
- Performance budget and latency roadmap. Memoization is a first-class lever for p95/p99 latency and per-request CPU when profiling shows the same pure computation repeated — config/permission resolution, reference-data lookups, repeated serialization of the same DTO, duplicate resolver calls. This package lets you express that as a method attribute instead of hand-rolled
$this->cache[$key] ??= ... blocks.
- Build vs. buy for a ~200-line capability. The honest framing is not "adopt a library" but "adopt a library or write the same thing in an afternoon." v1.1.0 moves this trade slightly toward buying: configurable cache-key specification is exactly the part teams get wrong when rolling their own (hashing complex argument objects, colliding keys), so the package now covers both the ergonomics and the subtle correctness edge.
- Framework standardization. This is a Symfony bundle, not a Laravel package — that mismatch remains the most consequential product finding. v1.1.0's Symfony 7 support widens the eligible surface to the current LTS line, so the decision is now "Symfony 6 or 7" rather than "Symfony 6 only." If the roadmap is Laravel, the decision stays "standardize on the native
once() helper (Laravel 11+) and Cache::remember" rather than introducing a Symfony bundle plus a bridge.
- Caching-layer scoping. It clarifies where caching belongs: in-process memoization (this package), shared cache (Redis/APCu with invalidation), or HTTP cache. This only covers the first — safe precisely because per-request memoization has no invalidation problem, but it also doesn't survive the request boundary.
- Key-space governance. The new cache-key specifier turns "the package decides my memoization key" into a deliberate design choice. That's a product-level input for long-running-worker deployments: a constrained, intentional key space is the difference between a bounded working set and an unbounded map.
- Use cases it validates: read-heavy endpoints, report/summary generation, GraphQL resolvers hitting the same service repeatedly, expensive deterministic domain functions.
- Explicit non-goals: anything with side effects, mutable-state-dependent results, or values whose object identity matters. Memoizing those turns a performance ticket into a correctness incident.
When to Consider This Package
Consider adopting when:
- The stack is Symfony 6 or 7 with attribute-driven DI already in use, so a method attribute is idiomatic for the team. v1.1.0 removes the "pinned to an older Symfony major" objection.
- Hot paths repeat pure, deterministic, side-effect-free method calls within a single request or process.
- You want zero invalidation logic — request-scoped memoization sidesteps staleness entirely, which is the cheapest correctness story available.
- You have non-trivial argument shapes (DTOs, value objects, composite keys) where the custom cache-key specifier earns its keep over naive serialization-based hashing.
- You accept the maintenance profile: still a low-star, zero-dependent, single-maintainer package, but v1.1.0 is a positive activity signal — Symfony 7 support plus a real feature, not a dependency-bump-only release. Adopt with a pin, a fork plan, or by vendoring the source into your own namespace.
Look elsewhere when:
- You're on Laravel. Use
once() or Cache::remember. Adding a Symfony bundle for this is a bridge with no payoff.
- Results must persist across requests or be shared across workers — use a real cache backend with a deliberate invalidation strategy.
- Memoized methods have side effects, depend on mutable state, or return objects whose identity is meaningful.
- Memory ceilings matter. Unbounded memoization in long-running workers (RoadRunner, Swoole, queue consumers) is a leak. The per-request assumption doesn't really protect daemons — the new key specifier helps you bound the key space, but it doesn't give you eviction. Require scoped/clearable memoization or don't adopt.
- Supply-chain or compliance gates require active maintenance, a security policy, or more than one maintainer. v1.1.0 improves the story but doesn't clear that bar.
How to Pitch It (Stakeholders)
Executives: A cheap, low-risk experiment to shave latency and CPU on hot paths. Because memoization here is request-scoped, there is no cache-invalidation liability — the classic source of "stale data" incidents simply isn't in scope. The latest release adds Symfony 7 support and lets you control the cache key, which addresses the two most common reasons teams would have skipped it. The offsetting risk is unchanged: tiny project, single maintainer, no meaningful dependent base. Treat this as roughly 200 lines we could own outright, not a strategic dependency.
Engineering: It gives you an attribute that memoizes a method keyed by its arguments, and v1.1.0 adds a cache-key specifier so you can define the key yourself instead of relying on default argument hashing — use it for DTO/value-object arguments and to keep the key space small and predictable. Symfony 7 is now supported, so the version-fit objection is gone. Two caveats remain. First, framework fit: this is Symfony, not Laravel — on Laravel, once() already covers this ground and should be the default. Second, adopt behind a profiling signal, restrict it to pure functions, and be explicit about worker-mode memory growth; the key specifier mitigates but does not solve unbounded growth in daemons. Pin the version and keep a fork option open.
Recommendation: Conditional, upgraded slightly. Worth a short spike in a Symfony 6/7 service where profiling shows duplicate computation; the v1.1.0 key-specifier feature makes that spike more likely to land cleanly in code with real argument types. Still not worth adopting on Laravel, and still not load-bearing infrastructure without a vendor/fork plan and a named owner.