Skip to content

PATTERN Cited by 1 source

Base-class automatic instrumentation

Problem

Instrumenting N similar components (screens, handlers, services) with the same measurement logic scales as O(N) engineering work when each component adds the instrumentation independently. Pinterest disclosed a concrete datum: two engineer-weeks per Android surface to add a User Perceived Latency metric and wire it to all production toolsets (Source: sources/2026-04-08-pinterest-performance-for-everyone). At 60+ surfaces that's 120+ engineer-weeks — and product engineers skip instrumentation on short-lived surfaces (holiday landing pages) because the cost doesn't justify the benefit. The per-unit instrumentation cost gates coverage.

Solution

Lift the instrumentation logic into the ancestor class all components inherit from. Every subclass gets the behaviour automatically, without touching its own code:

  1. Identify the base class that every relevant component already inherits from (UI framework base screen, HTTP handler base, service base class).
  2. Move the measurement logic into that base class — lifecycle-aware emission, sampling, dispatch.
  3. Expose a minimal opt-in contract (see patterns/opt-in-performance-interface) for per-subclass customisation where the base can't infer intent.
  4. The base class handles: initiating measurement, computing the predicate, emitting the timestamp, shipping to toolsets.
  5. The subclass handles: only the parts the base genuinely can't know (e.g. which views are content-critical).

Pinterest's instance

Pinterest's BaseSurface + PerfImageView / PerfTextView / PerfVideoView Android client implementation (Source: sources/2026-04-08-pinterest-performance-for-everyone):

  • BaseSurface is the UI base class every Pinterest feature screen inherits from.
  • BaseSurface runs a view-tree walk to detect Visually Complete.
  • Product engineers tag content-critical views by implementing one of three Perf* marker interfaces.
  • Result: 60+ Android surfaces continuously measured with no per-surface instrumentation work.

Pinterest's framing: "we built the Visually Complete logic into the base UI class (e.g. BaseSurface). Therefore, the Perceived Latency of any UI surface (existing or new) will be automatically measured as long as the feature is built on top of this base UI class."

When to use

  • There is a shared ancestor class all relevant components inherit from, or a framework entry point they all pass through.
  • The instrumentation is cross-cutting — identical logic wanted on every instance, with at most a small amount of per-instance configuration.
  • Per-subclass instrumentation is costly, errror-prone, or gets skipped (coverage problem).
  • The platform team owns the base class and can iterate on the instrumentation.

When not to use

  • No shared ancestor — if components don't share a base class (SwiftUI views, React components without a shared HOC), use environment-based mechanisms instead (see related mechanisms below).
  • Per-subclass instrumentation needs genuinely different logic — if each component needs a hand-tuned detector, base-class automation becomes a base class full of switches on the subclass type, which is worse than per-subclass code.
  • Subclasses frequently bypass the base class — if the inheritance convention is loosely enforced, coverage is incomplete and users won't know which screens are actually measured.

Generalisation beyond UI frameworks

The same shape works across many domains:

  • HTTP handler middleware — every request-handler goes through a framework filter / middleware / interceptor chain.
  • RPC server interceptors — gRPC / Thrift service stubs inherit or compose with a base class that logs + times every call.
  • Actor / service base classes — an Akka / Erlang actor base that instruments message handling.
  • UI framework wrappers — React HOCs / Vue mixins that wrap every component with instrumentation.
  • Workflow step base classes — workflow engines where every step inherits from a base that logs inputs / outputs / duration.

Common shape: one substrate + one opt-in point = coverage of every consumer for free.

  • Bytecode instrumentation / AOP — attach behaviour to matched call sites via Java agents, AspectJ; instrumentation-language parallel to base-class instrumentation.
  • Framework middleware — functional / compositional equivalent; less type-system-dependent.
  • Decorators (Python, TypeScript, higher-order functions in JS) — per-function wrappers achieving similar opt-in behaviour.
  • Environment injection (SwiftUI EnvironmentValues, React Context) — useful when there's no base class but a framework-provided lifecycle entry point.

Pinterest's instance is specifically a base-class + opt-in-marker-interface hybrid that combines automatic per-subclass measurement (from the base class) with product-engineer-controlled per-view tagging (from the marker interfaces).

Caveats

  • Doesn't solve the intent problem — the base class can measure when things happen but typically can't know what counts as the content (Pinterest pushes this to the product engineer via PerfView tagging).
  • Inheritance coupling — every subclass is coupled to the base class's behaviour; changes risk breaking every consumer simultaneously.
  • Observability opacity — subclasses get behaviour they didn't write; debugging unexpected emissions requires reading the base class.
  • Single-inheritance languages cap stacking — Java / Kotlin / C# etc. have single inheritance; layering multiple base-class-instrumentation systems requires deeper hierarchies or mixing in composition.

Seen in

  • 2026-04-08 Pinterest — Performance for Everyone (sources/2026-04-08-pinterest-performance-for-everyone) — canonical wiki instance. Pinterest Android BaseSurface + PerfView interfaces; 60+ surfaces instrumented for free; two-engineer-weeks-per-surface → zero-per-surface cost collapse.
Last updated · 319 distilled / 1,201 read