DocsRecipes

Recipes

Feature lifecycle and deprecation analytics

4 min readReviewed July 2026

Feature adoption is not one click. A feature can be discovered, tried, repeated, embedded in a workflow, abandoned, replaced, and eventually removed. A lifecycle view helps a product team distinguish novelty from durable value and retire old behavior without surprising important customers.

GraphJSON stores the analytical facts and runs the SQL. Your application, feature-management system, and release process remain responsible for access, rollout, migration, and removal.

Define the feature contract#

Create one stable record outside the event stream:

feature_key: scheduled_exports
owner: Reporting
eligible entity: account
eligible population: paid accounts with reporting enabled
discovery event: feature_entry_viewed
first-value event: scheduled_export_delivered
repeat-value rule: 3 successful deliveries in 30 days
replacement feature: report_subscriptions
definition version: 2

Do not rename the analytical key when marketing copy changes. A stable feature_key should survive UI labels, routes, and implementation rewrites.

Model exposure, use, and value separately#

Useful events:

{
  "event": "feature_entry_viewed",
  "event_id": "evt_01J...",
  "account_id": "acct_42",
  "user_id": "usr_91",
  "feature_key": "scheduled_exports",
  "entry_point": "reports_toolbar",
  "eligibility_version": "paid-v4",
  "release": "web-2026.07.26",
  "schema_version": 1
}
{
  "event": "scheduled_export_delivered",
  "event_id": "evt_01K...",
  "account_id": "acct_42",
  "user_id": "usr_91",
  "feature_key": "scheduled_exports",
  "artifact_type": "csv",
  "outcome": "succeeded",
  "schema_version": 1
}

Opening a menu is discovery. Completing the useful workflow is value. Keep both, and make the adopted outcome a valid product fact even after the feature analysis ends.

Measure the lifecycle stages#

Define mutually understandable stages:

Stage Example definition
Eligible Account could use the feature during the window
Discovered Entry point viewed
Tried Workflow started
First value First successful outcome
Repeated Value event repeated within a defined interval
Habitual Repeated use across several periods
Dormant Previously adopted, no recent use
Migrated Replacement feature reached equivalent value

These stages are calculations, not hidden user-profile state. Record the definition version and evaluation time whenever you publish membership.

Calculate eligible adoption#

The denominator should be eligible entities, not every account ever created:

WITH eligible AS (
  SELECT DISTINCT
    JSONExtractString(json, 'account_id') AS account_id
  FROM account_state_events
  WHERE
    JSONExtractString(json, 'event') = 'feature_eligibility_effective'
    AND JSONExtractString(json, 'feature_key') = 'scheduled_exports'
    AND JSONExtractBool(json, 'eligible') = true
),
value AS (
  SELECT DISTINCT
    JSONExtractString(json, 'account_id') AS account_id
  FROM product_events
  WHERE
    JSONExtractString(json, 'event') = 'scheduled_export_delivered'
    AND JSONExtractString(json, 'outcome') = 'succeeded'
    AND timestamp >= now() - INTERVAL 30 DAY
)
SELECT
  count() AS eligible_accounts,
  countIf(value.account_id != '') AS adopted_accounts,
  round(
    100 * adopted_accounts / nullIf(eligible_accounts, 0),
    2
  ) AS adoption_pct
FROM eligible
LEFT JOIN value USING account_id

If eligibility lives in an authoritative database, publish a versioned eligibility snapshot or derive the denominator there. Do not infer eligibility from who happened to see the entry point.

Distinguish breadth, depth, and durability#

Report together:

  • breadth: percentage of eligible accounts reaching value
  • depth: value outcomes per adopted account
  • frequency: active periods per account
  • time to first value
  • repeat rate after first value
  • durable adoption after 30, 60, or 90 days
  • multi-user participation for collaborative features

A widely tried feature with no repeat use has a different problem from a deeply valuable feature that few customers discover.

Build adoption cohorts#

Cohort accounts by first-value week, then measure later value activity:

WITH first_value AS (
  SELECT
    JSONExtractString(json, 'account_id') AS account_id,
    toStartOfWeek(toDateTime(min(timestamp)), 1, 'UTC') AS cohort_week
  FROM product_events
  WHERE
    JSONExtractString(json, 'event') = 'scheduled_export_delivered'
    AND JSONExtractString(json, 'outcome') = 'succeeded'
  GROUP BY account_id
),
activity AS (
  SELECT
    JSONExtractString(json, 'account_id') AS account_id,
    toStartOfWeek(toDateTime(timestamp), 1, 'UTC') AS active_week
  FROM product_events
  WHERE
    JSONExtractString(json, 'event') = 'scheduled_export_delivered'
    AND JSONExtractString(json, 'outcome') = 'succeeded'
  GROUP BY account_id, active_week
)
SELECT
  cohort_week,
  dateDiff('week', cohort_week, active_week) AS week_number,
  uniqExact(activity.account_id) AS active_accounts
FROM first_value
INNER JOIN activity USING account_id
WHERE active_week >= cohort_week
GROUP BY cohort_week, week_number
ORDER BY cohort_week, week_number

Wait for cohorts to mature before comparing later weeks.

Detect decline correctly#

Falling event volume can mean:

  • fewer eligible accounts
  • fewer adopted accounts
  • lower frequency among adopters
  • seasonality
  • tracking or delivery failure
  • replacement-feature migration
  • genuine abandonment

Decompose the movement before deciding to remove the feature. Use Explain why a metric changed for the full method.

Instrument deprecation communication#

When retirement becomes a product decision, emit:

{
  "event": "feature_deprecation_notice_presented",
  "event_id": "evt_notice_01J...",
  "account_id": "acct_42",
  "user_id": "usr_91",
  "feature_key": "scheduled_exports",
  "deprecation_version": "scheduled-exports-sunset-v1",
  "surface": "feature_banner",
  "replacement_feature_key": "report_subscriptions",
  "schema_version": 1
}

Track:

  • eligible affected accounts
  • notice reach by channel
  • acknowledgment where meaningful
  • help or migration action
  • replacement first value
  • continued legacy use
  • support contact
  • blocked migration reason

Communication delivery is not comprehension, and replacement discovery is not migration.

Define migration completion#

Write an equivalent-value rule:

replacement first value occurred
AND no legacy value event in the following 30 complete days

For low-frequency workflows, use a longer observation window or an explicit customer confirmation. Do not call a monthly feature migrated after three quiet days.

Create an affected-account table:

SELECT
  legacy.account_id,
  legacy.last_legacy_at,
  replacement.first_replacement_at,
  if(
    replacement.first_replacement_at > 0
      AND legacy.last_legacy_at < replacement.first_replacement_at,
    'candidate_migrated',
    'action_required'
  ) AS migration_status
FROM legacy_usage AS legacy
LEFT JOIN replacement_usage AS replacement USING account_id
ORDER BY legacy.last_legacy_at DESC

Use the application or customer-success system for the authoritative migration workflow. The query is an analytical view.

Set removal gates#

A safe removal decision includes:

  • no critical internal dependency
  • affected accounts identified
  • replacement covers the required outcome
  • high-value and contractual customers reviewed
  • notice period completed
  • settled legacy-use window
  • support and exception plan
  • rollback or restoration plan where practical
  • dashboards, alerts, docs, and event catalog updated

An adoption percentage alone is not a removal gate.

Retire the analytics assets#

After removal:

  1. stop emitting deprecated events deliberately
  2. keep the event definition in the catalog as retired
  3. preserve the deprecation decision and migration query
  4. update dashboards and alerts
  5. remove dead feature-flag and instrumentation code
  6. monitor replacement outcomes and support demand
  7. shorten retention only after reproduction needs are satisfied

Follow Analytics asset change management before renaming or removing shared events and queries.

Lifecycle checklist#

  • The feature has a stable analytical key and owner.
  • Eligibility, discovery, use, and value are separate concepts.
  • Adoption uses an eligible entity denominator.
  • Breadth, depth, frequency, and durability are reported together.
  • Cohorts are mature before long-term comparisons.
  • Decline is decomposed before being labeled abandonment.
  • Deprecation notices and replacement outcomes are instrumented.
  • Migration requires equivalent value and a settled legacy-free window.
  • Customer tier, contracts, support, and internal dependencies are reviewed.
  • Product systems remain authoritative for access and migration state.
  • Retired events remain documented in the catalog.
  • Removal includes post-cutover monitoring and an owner.
Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support