Revert "revert(pricing): remove A/B experiment, keep control (#44293) (#45361)"

This reverts commit 34241f1f66.
This commit is contained in:
Mert YEREKAPAN authored and GitHub committed 2026-04-29 16:09:44 +02:00
1 parent c65935d0d3
commit 40712e94c8
4 files changed
+174 -18

No files matched your search

+10
View File
@@ -13,6 +13,16 @@ import HashAnchorScroll from './HashAnchorScroll'
import PricingComparisonSection from './PricingComparisonSection'
import PricingPlansSection from './PricingPlansSection'
export type PricingPageExperimentVariant =
| 'control'
// GROWTH-694: flexibility/hourly billing
| 'flexibility' // concrete FAQ example as section between cards and buttons
| 'flexibility_card' // "no lock-in" messaging on Pro card warning
| 'hourly_rate' // actual hourly compute rate on Pro card warning
// GROWTH-697: pro project cost visibility
| 'multi_project' // "first project included, additional from $10/mo" on Pro card
| 'estimate_cta' // "estimate your cost" link on Pro card scrolling to calculator
export default function PricingContent() {
return (
<DefaultLayout>
+60 -4
View File
@@ -2,15 +2,71 @@
import PricingPlans from '~/components/Pricing/PricingPlans'
import { useOrganizations } from '~/data/organizations'
import { hasConsented, posthogClient } from 'common'
import { useEffect, useState } from 'react'
import type { PricingPageExperimentVariant } from './PricingContent'
const EXPERIMENT_ID = 'pricingPageExperiment' as const
const VALID_VARIANTS: PricingPageExperimentVariant[] = [
'control',
'flexibility',
'flexibility_card',
'hourly_rate',
'multi_project',
'estimate_cta',
]
export default function PricingPlansSection() {
const { isLoading, organizations } = useOrganizations()
const hasExistingOrganizations = !isLoading && organizations.length > 0
// Pricing value/flexibility A/B experiment.
// Uses client-side PostHog directly — server-side evaluation lacks full person context for www pages.
// DevToolbar overrides (x-ph-flag-overrides cookie) are respected in local dev via posthogClient.getFeatureFlag.
const [flagValue, setFlagValue] = useState<PricingPageExperimentVariant | false | undefined>(
() =>
posthogClient.getFeatureFlag(EXPERIMENT_ID) as
| PricingPageExperimentVariant
| false
| undefined
)
useEffect(() => {
return posthogClient.onFeatureFlags(() => {
const value = posthogClient.getFeatureFlag(EXPERIMENT_ID)
setFlagValue(value as PricingPageExperimentVariant | false | undefined)
})
}, [])
const isInExperiment = VALID_VARIANTS.includes(flagValue as PricingPageExperimentVariant)
const showFlexibilitySection = flagValue === 'flexibility'
useEffect(() => {
if (!isInExperiment) return
posthogClient.captureExperimentExposure(EXPERIMENT_ID, { variant: flagValue }, hasConsented())
}, [isInExperiment, flagValue])
return (
<PricingPlans
organizations={organizations}
hasExistingOrganizations={hasExistingOrganizations}
/>
<>
<PricingPlans
organizations={organizations}
hasExistingOrganizations={hasExistingOrganizations}
experimentVariant={isInExperiment ? (flagValue as PricingPageExperimentVariant) : undefined}
/>
{showFlexibilitySection && (
<div className="mx-auto max-w-xl px-8 mt-10 xl:mt-16 text-center">
<p className="text-foreground text-sm">
Need more power for a launch? Scale up instantly.{' '}
<span className="text-foreground-light">
Scale back down after — you only pay for the hours you use.
</span>
</p>
</div>
)}
</>
)
}
+74 -14
View File
@@ -1,5 +1,6 @@
'use client'
import type { PricingPageExperimentVariant } from '~/app/pricing/PricingContent'
import { Organization } from '~/data/organizations'
import { useSendTelemetryEvent } from '~/lib/telemetry'
import { Check } from 'lucide-react'
@@ -12,9 +13,14 @@ import UpgradePlan from './UpgradePlan'
interface PricingPlansProps {
organizations?: Organization[]
hasExistingOrganizations?: boolean
experimentVariant?: PricingPageExperimentVariant
}
const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansProps) => {
const PricingPlans = ({
organizations,
hasExistingOrganizations,
experimentVariant,
}: PricingPlansProps) => {
const sendTelemetryEvent = useSendTelemetryEvent()
return (
@@ -66,10 +72,36 @@ const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansP
)}
</div>
</div>
<div className={cn('flex flex-col', isProPlan && 'xl:mb-12')}>
<p className="text-foreground-light text-sm 2xl:pr-4 mb-4">
<div
className={cn(
'flex flex-col',
isProPlan &&
(!experimentVariant || experimentVariant === 'control') &&
'xl:mb-12'
)}
>
<p
className={cn(
'text-foreground-light text-sm 2xl:pr-4',
isUpgradablePlan &&
(experimentVariant === 'flexibility_card' ||
experimentVariant === 'hourly_rate')
? 'mb-2'
: 'mb-4'
)}
>
{plan.description}
</p>
{isUpgradablePlan && experimentVariant === 'flexibility_card' && (
<p className="text-brand text-xs mb-4">
No lock-in. Scale up or down anytime — you only pay for the hours you use.
</p>
)}
{isUpgradablePlan && experimentVariant === 'hourly_rate' && (
<p className="text-brand text-xs mb-4">
Compute from $0.01/hr. Scale anytime, pay only for what you use.
</p>
)}
</div>
{isUpgradablePlan && hasExistingOrganizations ? (
<UpgradePlan
@@ -120,20 +152,48 @@ const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansP
</p>
</div>
{plan.warning && (
{isUpgradablePlan && experimentVariant === 'multi_project' ? (
<div className="mt-4 flex flex-col gap-1">
<span className="text-[13px] leading-4 inline-flex gap-1 items-center">
{plan.warning}
<span className="text-[13px] leading-4">
First project included. Additional projects from $10/mo.
</span>
{(plan.name === 'Pro' || plan.name === 'Team') && (
<Link
href="#addon-compute"
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
>
Need more compute?
</Link>
)}
<Link
href="#addon-compute"
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
>
See how pricing scales
</Link>
</div>
) : isUpgradablePlan && experimentVariant === 'estimate_cta' ? (
<div className="mt-4 flex flex-col gap-1">
<span className="text-[13px] leading-4">{plan.warning}</span>
<Link
href="#addon-compute"
className="hover:underline text-brand text-[13px] m-0 p-0 leading-3 font-medium"
>
Estimate your cost &darr;
</Link>
</div>
) : (
plan.warning && (
<div className="mt-4 flex flex-col gap-1">
<span
className={cn(
'text-[13px] leading-4 inline-flex gap-1 items-center'
)}
>
{plan.warning}
</span>
{(plan.name === 'Pro' || plan.name === 'Team') && (
<Link
href="#addon-compute"
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
>
Need more compute?
</Link>
)}
</div>
)
)}
</div>
</div>
+30
View File
@@ -3115,6 +3115,35 @@ export interface StoragePublicBucketSelectPolicyWarningDismissButtonClickedEvent
groups: TelemetryGroups
}
/**
* User was exposed to the pricing value/flexibility experiment on the /pricing page.
*
* @group Events
* @source www
* @page /pricing
*/
export interface PricingPageExperimentExposedEvent {
action: 'pricing_page_experiment_exposed'
properties: {
/**
* Experiment identifier for tracking
*/
experiment_id: 'pricingPageExperiment'
/**
* Experiment variant.
* GROWTH-694: flexibility (section), flexibility_card (on card), hourly_rate (on card)
* GROWTH-697: multi_project (on card), estimate_cta (on card)
*/
variant:
| 'control'
| 'flexibility'
| 'flexibility_card'
| 'hourly_rate'
| 'multi_project'
| 'estimate_cta'
}
}
/**
* Triggered when an access token is successfully created.
*
@@ -3377,6 +3406,7 @@ export type TelemetryEvent =
| ComputeBadgeUpgradeClickedEvent
| FreeMicroUpgradeBannerDismissedEvent
| FreeMicroUpgradeBannerCtaClickedEvent
| PricingPageExperimentExposedEvent
| HeaderUpgradeCtaClickedEvent
| AccessTokenCreatedEvent
| AccessTokenRemovedEvent