← Back

Multi-App Content Strategy: Per-App Lesson Configuration

·content-duo

Multi-App Content Strategy: Per-App Lesson Configuration

A single adaptive curriculum engine serves three distinct apps: Amal (for children), Thurayya (for adults), and Ilham (for beginners). Each audience has different learning needs, attention spans, and optimal lesson structures. Children thrive with shorter, more challenging lessons. Adults prefer longer sessions with more review content. Beginners need smaller lesson sizes to avoid overwhelm.

Using a one-size-fits-all configuration would compromise all three experiences. We designed a per-app configuration system that allows each app to customize lesson size, slot distribution, and difficulty matching while sharing the same underlying adaptive curriculum engine.

The Problem: One Config for Three Audiences

Initially, Content Duo used global configuration settings applied uniformly across all apps. Every app received lessons with 5 items (40% new, 30% review, 30% challenge). This worked reasonably well for one audience but was suboptimal for others.

Before: Global Configuration

Single Config               All Apps
┌──────────────────┐       ┌──────────────────┐
│ Global Settings  │──────>│ Amal App         │
│ - lesson_size: 5 │       ├──────────────────┤
│ - new_slot: 40%  │──────>│ Thurayya App     │
│ - review_slot:30%│       ├──────────────────┤
└──────────────────┘──────>│ Ilham App        │
                            └──────────────────┘
                            Same experience

User feedback revealed the mismatch:

  • Amal (children): "Lessons are too long, kids lose focus"
  • Thurayya (adults): "Lessons are too short, I want to study longer"
  • Ilham (beginners): "Too many new words at once, I can't remember them"

The Solution: Per-App Configuration Tables

We introduced a content_duo_configurations table with app-specific settings. Each app references its own configuration row, allowing independent tuning of lesson parameters.

After: Per-App Configuration

Per-App Config              Customized Apps
┌──────────────────┐       ┌──────────────────┐
 Amal Config      │──────>│ Amal App         
 - lesson_size: 5         - Kids-friendly  
 - challenge: 30%        └──────────────────┘
├──────────────────┤       ┌──────────────────┐
 Thurayya Config  │──────>│ Thurayya App     
 - lesson_size: 7         - Adult-focused  
 - review: 40%           └──────────────────┘
├──────────────────┤       ┌──────────────────┐
 Ilham Config     │──────>│ Ilham App        
 - lesson_size: 3         - Beginner-heavy 
└──────────────────┘       └──────────────────┘

Configuration Schema Design

The configuration table defines customizable parameters for each app:

CREATE TABLE content_duo_configurations (
    id INT PRIMARY KEY AUTO_INCREMENT,
    app_name VARCHAR(50) UNIQUE NOT NULL,
    lesson_size INT DEFAULT 5,
    new_content_percentage INT DEFAULT 40,
    review_content_percentage INT DEFAULT 30,
    challenge_content_percentage INT DEFAULT 30,
    enabled BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Key Parameters

  1. lesson_size - Total number of items per lesson (range: 3-10)
  2. new_content_percentage - Proportion of new, never-seen concepts (0-100%)
  3. review_content_percentage - Proportion of due-for-review concepts (0-100%)
  4. challenge_content_percentage - Proportion of difficult concepts (0-100%)
  5. enabled - Feature flag to enable/disable adaptive curriculum per app

The percentages must sum to 100%, enforced at both database and application layers.

Per-App Customization Strategies

Amal (Children's App)

Target Audience: Ages 4-10 Attention Span: 2-5 minutes Optimal Configuration:

  • lesson_size: 5 - Short lessons to maintain engagement
  • new_content: 40% - Balanced new/review to prevent boredom
  • review_content: 30% - Moderate review for retention
  • challenge_content: 30% - Higher challenge to keep kids engaged

Rationale: Children need frequent novelty (higher challenge percentage) but can't handle long sessions (smaller lesson size).

Thurayya (Adult App)

Target Audience: Ages 18-65 Attention Span: 10-20 minutes Optimal Configuration:

  • lesson_size: 7 - Longer lessons for deeper study sessions
  • new_content: 30% - Moderate new content introduction
  • review_content: 40% - Higher review percentage for retention
  • challenge_content: 30% - Standard challenge level

Rationale: Adults can handle longer sessions and benefit from more review to combat busy schedules and longer intervals between study sessions.

Ilham (Beginner App)

Target Audience: Absolute beginners Skill Level: No prior Arabic knowledge Optimal Configuration:

  • lesson_size: 3 - Very small lessons to prevent overwhelm
  • new_content: 50% - High new content (building foundational vocabulary)
  • review_content: 30% - Standard review
  • challenge_content: 20% - Lower challenge (avoid discouragement)

Rationale: Beginners need slower pacing with smaller lesson sizes. Higher new content percentage builds foundational vocabulary quickly, while lower challenge percentage prevents discouragement.

Runtime Configuration Retrieval

The Content Duo service retrieves app-specific configuration at lesson generation time:

class ContentDuoService:
    def generate_lesson(self, user_id, app_name):
        # Fetch app-specific configuration
        config = self.get_configuration(app_name)

        if not config or not config.enabled:
            return None  # Adaptive curriculum disabled for this app

        # Use config parameters for slot allocation
        lesson_size = config.lesson_size
        new_count = int(lesson_size * config.new_content_percentage / 100)
        review_count = int(lesson_size * config.review_content_percentage / 100)
        challenge_count = lesson_size - new_count - review_count

        # Select content for each slot
        new_concepts = self.select_new_content(user_id, new_count)
        review_concepts = self.select_review_content(user_id, review_count)
        challenge_concepts = self.select_challenge_content(user_id, challenge_count)

        return self.assemble_lesson(new_concepts, review_concepts, challenge_concepts)

Database Design Considerations

Why Not Per-App Tables?

An alternative design would create separate tables for each app (e.g., amal_content_duo_sessions, thurayya_content_duo_sessions). We rejected this approach because:

  1. Data duplication - Same schema repeated 3 times
  2. Query complexity - Cross-app analytics require UNION queries
  3. Schema evolution - Every schema change requires 3 migrations
  4. Scalability - Adding a 4th app requires new tables, not just config rows

Shared Tables with App Column

Instead, we use shared tables with an app_name column:

CREATE TABLE content_duo_sessions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    app_name VARCHAR(50) NOT NULL,
    user_id INT NOT NULL,
    config_id INT NOT NULL,
    started_at TIMESTAMP,
    completed_at TIMESTAMP,
    FOREIGN KEY (config_id) REFERENCES content_duo_configurations(id),
    INDEX idx_app_user (app_name, user_id)
);

This design allows:

  • Single source of truth - One schema for all apps
  • Easy analytics - SELECT * FROM sessions WHERE app_name = 'amal'
  • Flexible configuration - Add apps by inserting config rows
  • Referential integrity - Foreign key to configurations table

Feature Flagging per App

The enabled boolean in the configuration table acts as a feature flag:

def is_adaptive_enabled(app_name):
    config = ContentDuoConfiguration.query.filter_by(app_name=app_name).first()
    return config and config.enabled

This allows:

  • Gradual rollout - Enable adaptive curriculum for Amal first, Thurayya later
  • Emergency disable - Turn off adaptive curriculum without code deployment
  • A/B testing - Randomly enable/disable for cohorts

Configuration Versioning (Future Enhancement)

While not yet implemented, we designed the schema to support versioning:

ALTER TABLE content_duo_configurations
  ADD COLUMN version INT DEFAULT 1;

This enables:

  • Config history - Track changes over time
  • Rollback capability - Revert to previous working config
  • A/B testing - Test multiple configs simultaneously (version A vs. B)

Implementation Details

Files Created:

  • src/models/curriculum/content_duo_configurations.py - SQLAlchemy model
  • migrations/versions/xxx_add_content_duo_configurations.py - Database migration
  • src/services/content_duo/config_service.py - Configuration retrieval service

Commits: 50dc2c0, ec8f72b, ba10388

Test Coverage: 12 unit tests covering config retrieval, validation, and slot calculation

Results: Flexible, App-Specific Learning

Since deploying per-app configurations, we've achieved:

  • 1 global config → 3+ app-specific configs - Each audience gets optimized lesson structure
  • Flexible lesson sizing - Range from 3 items (Ilham) to 7 items (Thurayya)
  • Customizable slot distribution - Each app balances new/review/challenge differently
  • Feature flag control - Enable/disable adaptive curriculum per app without deployment

Real-World Configuration Tuning

After initial deployment, we A/B tested different configurations for Amal:

Test A (initial): lesson_size=5, new=40%, review=30%, challenge=30% Test B (variant): lesson_size=4, new=50%, review=20%, challenge=30%

Results showed Test B improved completion rate by 18% (shorter lessons, more novelty). We updated Amal's configuration without touching Thurayya or Ilham.

Trade-offs and Limitations

Configuration Drift

With per-app configs, settings can drift and become inconsistent. We mitigate this by:

  • Documenting rationale for each configuration in code comments
  • Regular config audits to ensure percentages align with app goals
  • Shared base configuration with app-specific overrides

Testing Complexity

More configurations mean more test cases. We address this with:

  • Parameterized tests that run against all app configs
  • Integration tests that verify config loading for each app
  • Config validation at application startup

Over-Optimization Risk

It's tempting to continuously tweak configurations. We resist by:

  • Requiring A/B test data before changing configs
  • Batch config changes (quarterly reviews, not daily tweaks)
  • Documenting expected impact before changes

What's Next

Future enhancements include:

  • User-level overrides - Let individual users customize lesson size
  • Dynamic config adjustment - Adjust config based on user engagement metrics
  • Time-of-day configs - Shorter lessons in morning, longer in evening
  • Seasonal configs - Different configs during school year vs. summer

Per-app configuration transformed Content Duo from a rigid system into a flexible platform that adapts to each audience's unique learning needs. By centralizing configuration in the database, we enable rapid experimentation and optimization without code changes.


Implementation Files: src/models/curriculum/content_duo_configurations.py Commits: 50dc2c0, ec8f72b, ba10388 Schema: Per-app configuration table with foreign keys