orchid/experiment
A Laravel package for running experiments and A/B tests. Define variants, assign visitors, track conversions, and review results through a straightforward API, making it easy to add controlled feature and UI tests to your application.
Architecture fit. The package occupies the assignment layer of an experimentation stack: declarative experiment definitions, deterministic hash-based bucketing, a facade for resolution, Blade helpers, and goal/conversion counters. For a Laravel-native product this is a clean fit — assignment happens in-process, requires no external service, and keeps experiment configuration in code (reviewable, testable, cheap to retire). The critical architectural gap is that it stops at assignment. The measurement layer — exposure logging, goal deduplication, bot filtering, attribution windows, reporting, and statistical inference — remains ours. That split is often correct for a small team that wants the mechanism in-house, and clearly wrong for a team that expects experimentation rigor out of the box. This package forces that conversation early, which is its most valuable property as a candidate.
Integration feasibility. Mechanically straightforward: composer require orchid/experiment, publish the config, declare weighted variants, resolve the variant once at the edge (middleware/controller), branch in the view, record conversions through one seam. Footprint is small; the facade is the contract. The blocker is not code, it's knowledge: documentation is thin, the only public release signal is 2.4 ("Laravel 9 support"), and the supported Laravel/PHP matrix is effectively unknown until we read composer.json and diff the PR. Budget a discovery spike before committing to a roadmap that depends on this.
Technical risk. The dominant risk is maintenance, not functionality. Zero dependents, an overall score of 0.3, an opportunity score of ~20, sparse release notes, and a latest release that only widened support to Laravel 9 (itself past security support) together describe a package with a low probability of tracking future Laravel majors. Secondary risks are silent readout invalidation: session/cache-based assignment flipping on cache clear, hash reshuffling when weights change mid-flight, bot-inflated exposures, and ambiguous goal deduplication. Each is fixable, but only if we know which mode the package runs in before experiment #6.
Key questions.
(experiment, goal, identity), or must reporting SQL dedupe itself?Stack fit. Laravel-native by intent, and compatible in spirit with Blade, Inertia/Livewire, and JSON APIs. The one rule that matters: the variant is resolved server-side and passed down as a value; the client never picks a variant. That preserves stickiness, avoids render flicker, and keeps the readout clean. Wrap the package behind a thin internal interface — resolve(string $experiment, $identity): string and converge(string $experiment, string $goal, $identity): void — so the package is an implementation detail, not an API every controller learns.
Migration path. Greenfield adoption is a two-hour integration for one experiment; the real migration is organizational. If manual flag config or ad-hoc database flags exist today, migrate them behind the same resolver interface so callsites don't change twice. If we're on Laravel 10+, plan the fork as part of adoption, not after — fork the repo, pin to a commit, and treat the fork as the artifact we support. Upgrading 2.3 → 2.4 should be verified by diffing migrations and the service provider, not by trusting the "Laravel 9 support" changelog line.
Compatibility. Validate in staging against php artisan config:cache, route:cache, and package discovery before production. Config-driven experiment packages commonly read config at boot; a cached config with stale definitions will not pick up new experiments until recached — a footgun that looks like a bug in the experiment. Inspect any shipped migration and treat the resulting tables as our reporting schema, because they will be.
Sequencing.
composer.json, the service provider, and the migrations directory. Decide adopt / fork / reject. Cap it at a day.user_id for authenticated, cookie/session ID for guests, with the resolved variant stored alongside for audit and a test asserting variant stability across logout/login.force()/fake() implementation so every variant branch is unit-testable without depending on hash behavior.running, stopped (removed from config, data retained), or shipped (winning variant becomes the only code path, branch deleted). Retire aggressively; stale experiments keep assigning traffic and inflating exposure counts.Maintenance. The maintenance burden is ours, not the maintainer's. Cap exposure by keeping the package strictly behind the resolver interface, pinning the version, and owning a fork when necessary. The engineering cost is small; the analytical cost — dedupe, bots, attribution, reporting — is the ongoing line item. Estimate it as a standing platform-team responsibility, not a one-time integration.
Support. Assume self-support. Public signals (zero dependents, a first-time contributor shipping the only recent change, no visible issue activity) suggest no reliable upstream. Documentation is a release note and a thin README; the source is the contract. Plan for a named internal owner, a private fork, and a short patch SLA for Laravel major bumps.
Scaling. Assignment itself scales trivially: hash-based bucketing is O(1) per request and cache-friendly. The scaling pressure lands on the data layer — conversion tables grow monotonically, and reporting queries need indexing, date-range filtering, and explicit DISTINCT if the schema lacks a uniqueness constraint. At high traffic, the cost is your database and queries, not the resolver, and that's true of any in-house experimentation layer.
Failure modes to watch.
Ramp-up. Low for engineers (config plus a facade), high for the organization's decision discipline. The package makes shipping a test a two-hour task but supplies no dashboards, no significance testing, and no sample-size guardrails. PMs must own the pre-registered minimum runtime and sample size, resist daily peeking, and run a review-and-decide ritual — otherwise the tooling lowers activation energy for tests whose results nobody is willing to act on. Realistic ramp: one engineer productive in a day, the team trustworthy in a couple of experiments, provided the reporting and dedupe seams are built during experiment #1 rather than retrofitted later.
How can I help you explore Laravel packages today?