Skip to content

Formidable to Custom Plugin — Scalability Audit & Migration Decision

Created 2026-03-13
Tags infrastructurepractice-siteformidable-formsactivity-trackingtechnicalarchive

Archive record of the analysis and decision that led to the development of the BW Activity Plugin.

Decision made: 2026-03-13 Plugin deployed to production: 2026-03-20 See: BW Activity Plugin — Design & Implementation Plan


Through early 2026, the activity tracking system on practice.baseworks.com was built on Formidable Forms. Form 69 served as the core activity log — every practice session was stored as a Formidable entry, and Formidable views handled all display: the dashboard calendar, activity bars, streak tracker, and practice history page.

Two converging pressures triggered a scalability audit in March 2026:

  1. PrimerPrint moving online — the PrimerPrint visualization (see Primer Journey Visualizer — PrimerPrint) was being adapted as an interactive web feature. This required a REST API endpoint that could return a user’s complete lesson-visit history in chronological order — a query pattern Formidable’s EAV storage made expensive.

  2. Anticipated subscriber growth — as the Primer was being prepared for international marketing, the platform needed to be evaluated against higher user volumes than it had been built for.

The audit was conducted during the initial planning session on 2026-03-13 and concluded that Formidable Form 69 was not a viable long-term foundation for the activity log. The decision to build a custom plugin was made the same day, and development began immediately.


Form 69 used Formidable Forms’ standard entry storage: entries in wp_frm_items, field values in wp_frm_item_metas — an EAV (Entity-Attribute-Value) pattern where each field value is stored as a separate row. Each activity session created one row in frm_items and one row per field in frm_item_metas.

Typical entry fields: user_id, timestamp, lesson_id, duration, activity_type ≈ 5+ rows in frm_item_metas per session.

SubscribersDaily sessions/userEntries/dayEntries/year
100110036,500
1,00011,000365,000
10,000110,0003,650,000

At 1,000+ subscribers, the frm_item_metas table would reach millions of rows. The Formidable views handling the dashboard (calendar, streak, activity bars) required multi-table JOINs and aggregations across this EAV structure with no domain-specific indexes.

The EAV pattern is well-suited to form data where fields vary per entry. It is a poor fit for an activity log where:

  • The schema is fixed and known in advance
  • Queries are predominantly time-range lookups (WHERE user_id = X AND timestamp > Y)
  • Aggregates (streak, totals, session counts) are computed frequently on every page load
  • PrimerPrint requires fetching a user’s complete lesson-visit history in full chronological order

Verdict: Not viable at scale. A purpose-built table with proper indexing was necessary.


The audit led directly to the decision to replace Form 69 with a custom WordPress plugin and a purpose-built database table. Key design criteria at the time of the decision:

  • Fixed schema, indexed for time-range and per-user queries
  • PHP API for logging activity (callable from Uncanny Automator via custom action)
  • Shortcodes to replace Formidable views for all dashboard and history displays
  • REST API endpoint for PrimerPrint and future integrations

Proposed schema (as designed in the audit)

Section titled “Proposed schema (as designed in the audit)”

The original proposal defined a single table:

CREATE TABLE wp_bw_activity_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
recorded_at DATETIME NOT NULL,
lesson_id VARCHAR(100),
lesson_label VARCHAR(255),
duration_min SMALLINT UNSIGNED,
activity_type VARCHAR(50),
points SMALLINT,
source VARCHAR(50), -- 'presto', 'manual', etc.
INDEX idx_user_time (user_id, recorded_at),
INDEX idx_lesson (lesson_id)
);

Note: The final implementation added a cluster column and moved activity types into a separate wp_bw_activity_types table (editable via admin UI). See bw-activity-plugin-plan for the as-built schema.

  1. Build and test the custom plugin on pracstage.baseworks.com
  2. Export Form 69 entries to CSV
  3. Write a migration script to import CSV into the new table
  4. Replace Formidable view shortcodes in dashboard and history pages with new plugin shortcodes
  5. Update Automator recipes to call the new logging action instead of Formidable entry creation
  6. Verify streak, calendar, and activity bars match previous outputs
  7. Keep Form 69 in place (deactivated, not deleted) for legacy data access
  8. Keep Form 71 (timezone settings) — unrelated to the log

What would stay with Formidable (as planned)

Section titled “What would stay with Formidable (as planned)”
  • Form 70 (Smart Revisit) — to be rebuilt using the new plugin’s data
  • Form 68 (Progress bar) — to stay or be replaced
  • Form 71 (Timezone settings) — stays permanently; writes directly to an ACF user meta field

The BW Activity Plugin was built across March 13–20, 2026, and deployed to production on 2026-03-20 as v0.7.0. The migration ran successfully: 2,604 entries, 72 users, data back to December 2020. All Formidable activity views were replaced by plugin shortcodes on both English and Japanese pages. Automator Recipes 18036 and 18647 were deactivated and replaced by Recipe 22021. Code Snippets 30–34 (frm-date, dailymax, activity bars, streak, dots) were deactivated. GamiPress was deactivated.

See BW Activity Plugin — Design & Implementation Plan for the full implementation record.