0% прочитано

One Eligibility Check for All Analytics: Stop Filtering Internal Traffic in Every Tracker

The problem appeared in one analytics path, but adding one more local if would have been the wrong fix. I show why the decision about whether a request is eligible for analytics belongs in one shared boundary.

4 жовтня 2026 р. 8 хв читанняLaravel

One dangerous property of analytics bugs is that incorrect data collection often does not break a page or throw an exception.

Everything continues to work. The metrics simply become less accurate over time.

In my case, the problem became visible in one flow: internal activity could enter public analytics.

The simplest fix looked obvious: add another check directly to that tracker.

But that would only repair the place where the problem became visible, not the underlying cause.

If a rule applies to all public analytics, it should not be the responsibility of every individual tracker.

When the same condition decides whether an analytics event may be recorded at all, I treat it as a shared eligibility policy rather than a local filter for one metric.

The Problem May Appear in Only One Tracker

PHP - A local check looks sufficient
final class SearchAnalyticsTracker{    public function track(        Request $request,        ?User $user,    ): void {        if (            $user !== null            && $user->canAccessAdmin()        ) {            return;        }         $this->storeSearch(            $request,        );

This code may be completely correct locally.

It prevents internal activity from entering one analytics stream, and the focused test passes.

The problem starts when the system already has several collection points:

  • content views;
  • search;
  • sharing;
  • clicks;
  • other public interactions.

If every tracker decides eligibility independently, the same rule begins to exist in several implementations.

What Gets Duplicated Is the Data Rule, Not Just an If

One rule across many collection points
View tracker    |    +--> internal user? -> skip  Search tracker    |    +--> internal user? -> skip  Share tracker    |    +--> internal user? -> skip  Future tracker

The risk is not a few duplicated lines.

The risk is that different metrics begin following different eligibility rules.

For example:

  • views already exclude internal traffic;
  • search does not yet;
  • a new sharing metric does not know the rule exists;
  • months later another tracker repeats the same mistake.

The dashboard may then contain numbers that are technically correct for each query but semantically incompatible with one another.

I Moved the Decision Into One Eligibility Boundary

PHP - Simplified shared policy
final class PublicAnalyticsEligibility{    public function allows(        ?User $user,    ): bool {        if ($user === null) {            return true;        }         return ! $user->hasInternalAccess();    }}

The exact class name is not the important part.

The important part is that the system has one answer to the question: is this interaction eligible for public analytics?

The tracker no longer decides who is internal, which roles exist, or how internal access is determined.

It simply asks the policy.

The Tracker Returns to Its Own Responsibility

PHP - The tracker uses the shared rule
final class SearchAnalyticsTracker{    public function __construct(        private PublicAnalyticsEligibility $eligibility,    ) {}     public function track(        Request $request,        ?User $user,    ): void {        if (            ! $this->eligibility->allows($user)        ) {            return;        } 

The tracker still checks eligibility before writing data.

But it no longer owns the rule itself.

Its responsibility remains local: normalize the specific event data and pass it into analytics storage.

The decision about whether the event belongs in public analytics lives at a higher boundary.

I Do Not Identify Internal Users by Role Name

PHP - Fragile role-name check
if (    $user->role === 'admin') {    return;}
PHP - Check through semantic policy
if (    ! $eligibility->allows($user)) {    return;}

Another important detail is that eligibility should not depend on an arbitrary role name.

Today internal access may belong to an admin role. Tomorrow there may be moderator, support, or editor roles.

If analytics checks one specific role name, a new kind of internal user can easily start entering public metrics.

The rule should therefore rely on the actual capability or another canonical signal that represents internal access.

Excluding Internal Traffic Does Not Mean Excluding All Authenticated Users

Different authenticated states
Guest    -> eligible  Regular authenticated user    -> eligible  Authenticated internal user    -> excluded

I also avoid making the rule too broad.

Authentication alone does not mean the traffic is internal.

A regular site user may be signed in, read content, search, share, and still be part of the real audience.

The policy should represent the actual meaning of "internal traffic" rather than using a broad auth()->check() condition.

A Shared Policy Must Cover Every Analytics Write Boundary

One boundary before different metrics
Public interaction        |        vshared eligibility        |        +--> excluded -> stop        |        +--> eligible                |                +--> view tracker                +--> search tracker                +--> share tracker                +--> future tracker

Creating a policy alone guarantees nothing.

Every real public analytics write path must be identified.

Otherwise part of the system will use the new policy while an older tracker continues following its own rules.

After this kind of refactor, I therefore perform a small audit of the analytics write boundaries instead of stopping at the original bug.

I Test Most Eligibility Rules at the Policy Boundary

PHP - Simplified shared-policy tests
it('allows guests', function () {    expect(        $this->eligibility->allows(null)    )->toBeTrue();}); it('allows regular users', function () {    $user = User::factory()->create();     expect(        $this->eligibility->allows($user)    )->toBeTrue();}); it('excludes internal users', function () {    $user = User::factory()->create();

After centralization, there is little value in copying the full eligibility matrix into every tracker test suite.

I verify the main matrix close to the policy:

  • guests are allowed;
  • regular authenticated users are allowed;
  • internal users are excluded;
  • different ways of receiving the internal capability produce the same result.

Individual trackers then keep representative integration tests proving that they actually use the shared policy.

A New Metric Automatically Inherits the Existing Rule

This is one of the main benefits for me.

When a new metric appears months later, the developer does not need to remember the full history of previous bugs.

If the new tracker participates in the same analytics pipeline and depends on the shared eligibility policy, the rule applies immediately.

The architecture starts protecting the system from a forgotten check.

This Rule Does Not Necessarily Belong in HTTP Middleware

It may be tempting to move the check even higher, for example into HTTP middleware.

But analytics eligibility is not necessarily an HTTP access rule.

An internal user may be perfectly allowed to open a public page. We simply do not want that interaction counted as external audience behavior.

I therefore keep HTTP access and analytics eligibility as separate concepts.

Fixing Collection Does Not Automatically Mean Rewriting History

I also keep future collection and historical data as separate concerns.

Finding that some internal traffic may have entered analytics in the past does not automatically justify rewriting old aggregates.

Historical cleanup requires separate evidence, a reliable way to identify incorrect records, and an understanding of how recalculation affects reports.

I first make new writes correct. Historical correction is a separate decision.

What I Check When I Find a Local Analytics Bug

When a problem appears in one tracker, I check:

  • Is this rule really local to this metric?
  • Should the same condition apply to other public analytics events?
  • Is there one canonical way to determine eligibility?
  • Am I using a role name instead of the real capability?
  • Do all analytics write boundaries use the same policy?
  • Do guests and regular authenticated users remain eligible?
  • Am I keeping analytics eligibility separate from page authorization?
  • Will a future tracker inherit this rule automatically?

A Local Bug Can Reveal a Missing System Boundary

The original problem looked small: one kind of internal activity was entering the wrong numbers.

But a local if would only have hidden the system-level problem.

Public analytics needs one shared definition of which interactions belong to its audience at all.

When that rule has one owner, individual trackers stay simpler, metrics share the same semantics, and new collection points are much harder to implement incorrectly.

For me, the better fix was not "add one more check", but "make sure nobody has to remember this check in every future tracker".

If one condition determines the correctness of every metric, it should not be a private detail of one tracker.