Remove the current targeted messages feature: code removal
Summary
Remove the current targeted messages code from GitLab.com: the admin UI, the billing page banner, the GraphQL field, the models, the services and the entry points. Growth will redesign targeted messages as part of the Personalization Engine work, starting with the audience layer and then the delivery layer.
Decided in !263237 (comment 4096009314).
This issue is release M of the removal. The leftovers are split out because they span several milestones:
Both are blocked until the changes here are released.
Problem
The feature is only half built, and we don't plan to build on its current shape:
- Audiences come from an uploaded CSV of namespace IDs.
- The banner shows only hardcoded Premium promo copy, and only on the group Billing page.
- It's behind the
targeted_messages_admin_uigitlab_com_deriskflag and thetargeted_messagesSaaS feature. It still has to be maintained.
Scope
Remove in small MRs, in this order where it matters. Finish each part with grep -ri targeted_message to catch leftovers.
1. Frontend and entry points
- Billing banner:
ee/app/assets/javascripts/targeted_message_banner/, its init and mount point (#js-targeted-message-banner) inee/app/assets/javascripts/pages/groups/billings/index.jsand the billing view, andee/spec/frontend/targeted_messages/. - Admin form:
app/assets/javascripts/admin/targeted_messages/,ee/app/assets/javascripts/pages/admin/targeted_messages/(including the twovue3_migration.ymlfiles) andee/spec/frontend/admin/targeted_messages/. - Admin views:
ee/app/views/admin/targeted_messages/. - Admin sidebar: delete
ee/lib/ee/sidebars/admin/menus/messages_menu.rband its spec. Deleting only the targeted item would leave the menu in a wrong state. The FOSSMessagesMenualready links straight to broadcast messages. - Routes:
ee/config/routes/instance_admin.rb,ee/config/routes/user.rb. - Regenerate the path helpers
ee/app/assets/javascripts/lib/utils/path_helpers/user.jsandinstance_admin.js.
2. Backend
- Controllers and specs:
Admin::TargetedMessagesController,Users::TargetedMessageDismissalsController. - Services and specs:
Notifications::TargetedMessages::{BaseService,CreateService,UpdateService,NamespaceIdsBuilder}. Notifications::TargetedMessagePolicyand its spec.- Models and specs:
Notifications::TargetedMessage,TargetedMessageNamespace,TargetedMessageDismissal, and their factories inee/spec/factories/notifications/. - User association: remove
targeted_message_dismissalsfromEE::Users::DependentAssociationsand its spec. Before removing it, confirm the foreign key fromtargeted_message_dismissalstousershasON DELETE CASCADE. Otherwise user deletion breaks until the table is dropped. - Worker: stop enqueuing it (the controller removal does this). Make
Notifications::TargetedMessages::DestroyWorker#performa no-op that keeps its argument, per step 1 of worker removal. Keep the class. #637388 deletes it. - Config:
ee/config/saas_features/targeted_messages.ymlandee/config/feature_flags/gitlab_com_derisk/targeted_messages_admin_ui.yml, plus anypush_frontend_feature_flagandFeature.enabled?references. - Rubocop todo:
.rubocop_todo/rspec/rails_controller_testing.yml. - Strings: regenerate
locale/gitlab.pot(TargetedMessages|strings).
3. GraphQL
- Remove
Namespace.targetedMessages(ee/app/graphql/ee/types/namespace_type.rb),Types::Notifications::TargetedMessageTypeandResolvers::Notifications::TargetedMessageResolver, with their specs. The field is markedexperiment(18.7), so no deprecation period is needed. - Remove
type:TargetedMessagefromconfig/authz/graphql/authorization_todo.txt. - Regenerate the GraphQL reference docs.
4. Out of scope (separate issues)
- Worker removal: #637388. The queue files
config/sidekiq_queues.ymlandee/app/workers/all_queues.ymlchange there. - Database removal: #637389. The allowlists
spec/support/database/retention-policy-missing-allowlist.ymlandspec/support/database/destroy_service_foreign_keys_todo.ymland thedb/docsfiles change there.
Close or update the related open work: #634504 and the targeted messages epic. The MRs !263237 (closed) and !263236 (closed) are already closed.
What we learned (input for the design doc)
Requested in !263237 (comment 4096009314). @swiskow, please add the details of the 2 October incident and anything else that's missing.
- 2 October. Premium and Ultimate customers saw a date meant only for Free (from @swiskow). This is why plan targeting was requested.
- List audiences. Namespace IDs are written synchronously in the admin request. For the POC, that's why uploads are capped at 50,000. A list is also a snapshot. Larger or changing cohorts need background processing or rules-based audiences.
- Dismissals. Stored per user, namespace and message, so dismissing in one group doesn't hide the message in another. The endpoint doesn't check that the message targets the namespace or that the user is a member, and it has no rate limit.
- Schedule. The resolver doesn't filter on
starts_at/ends_at(fixed in !263236 (closed), unmerged). - Rules. Each new filter interacts with the others and needs its own performance check. Design the audience model up front instead of adding filters one at a time.
- Delivery. Planned per-message page placement (#528871), async loading (#535457 (closed)), and up to two CTAs with named routes (#527968).
- Interim. Use a one-off dismissible banner behind a feature flag that checks
namespace.plan_tier(!263237 (comment 4053062002)).