jawabapp / remote-config
A standalone Laravel package for A/B testing, remote configuration, and feature experimentation
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Language:Blade
pkg:composer/jawabapp/remote-config
Requires
- php: ^8.1|^8.2|^8.3
- illuminate/cache: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- jfcherng/php-diff: ^6.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0|^11.0
README
A powerful Laravel package for A/B testing, remote configuration management, and feature experimentation. Manage your app's configuration remotely, run experiments, and deploy winning variants without code changes.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Quick Start
- Integration Guide
- Usage Examples
- API Reference
- Database Structure
- Admin Panel
- Testing
- Best Practices
Features
- ✅ A/B Testing: Create experiments with multiple variants and ratio-based distribution
- ✅ Remote Configuration: Manage app configurations remotely without deploying
- ✅ Winner Deployment: Deploy winning variants to specific user segments
- ✅ Test Overrides: IP-based testing for QA teams using Laravel Cache
- ✅ Polymorphic User Support: Works with any user model
- ✅ Comprehensive Targeting: Platform, country, and language targeting
- ✅ Audit Logging: Complete audit trail of all changes with diffs
- ✅ Flexible Architecture: Configurable table prefixes, routes, and middleware
- ✅ Helper Functions: Convenient helper functions for common operations
- ✅ Admin Panel: Built-in admin interface for managing experiments
Requirements
- PHP >= 8.1
- Laravel >= 10.0
- Redis (for test overrides and experiment counters)
- MySQL/PostgreSQL/SQLite (for data persistence)
Installation
Step 1: Install Package via Composer
composer require qanah/remote-config
Step 2: Publish Configuration File
php artisan vendor:publish --tag=remote-config-config
This creates config/remote-config.php in your Laravel application.
Step 3: Configure Database Connection
Make sure your .env file has database credentials:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_username DB_PASSWORD=your_password
Step 4: Configure Redis (Required for Test Overrides)
Add Redis configuration to your .env:
REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 REDIS_CACHE_DB=1
Step 5: Run Migrations
php artisan migrate
This creates 11 tables for the package.
Step 6: (Optional) Publish Views for Customization
php artisan vendor:publish --tag=remote-config-views
Step 7: (Optional) Publish Assets
php artisan vendor:publish --tag=remote-config-assets
Configuration
The config/remote-config.php file contains all package settings. Here's a complete breakdown:
Basic Settings
return [ /* |-------------------------------------------------------------------------- | Enable/Disable Remote Config & Experiments |-------------------------------------------------------------------------- | Master switch - when disabled, API returns base configurations only */ 'enabled' => env('REMOTE_CONFIG_ENABLED', true), /* |-------------------------------------------------------------------------- | Testing Mode |-------------------------------------------------------------------------- | Enable IP-based test overrides for QA teams (requires Redis) */ 'testing_enabled' => env('REMOTE_CONFIG_TESTING_ENABLED', true), /* |-------------------------------------------------------------------------- | User Created After Date |-------------------------------------------------------------------------- | Only users created after this date participate in experiments | Format: Y-m-d */ 'user_created_after_date' => env('REMOTE_CONFIG_USER_CREATED_AFTER', '2022-08-29'), /* |-------------------------------------------------------------------------- | Cache TTL (Time To Live) |-------------------------------------------------------------------------- | TTL in seconds for caching experiment assignments | Default: 604800 (7 days) */ 'cache_ttl' => env('REMOTE_CONFIG_CACHE_TTL', 604800), ];
Routes Configuration
/* |-------------------------------------------------------------------------- | Web Routes (Admin Panel) |-------------------------------------------------------------------------- */ 'routes' => [ 'enabled' => true, 'prefix' => env('REMOTE_CONFIG_ROUTE_PREFIX', 'remote-config'), 'middleware' => ['web', 'auth'], // Add your auth middleware 'as' => 'remote-config.', ], /* |-------------------------------------------------------------------------- | API Routes (Client-facing) |-------------------------------------------------------------------------- */ 'api_routes' => [ 'enabled' => true, 'prefix' => env('REMOTE_CONFIG_API_PREFIX', 'api/config'), 'middleware' => ['api'], // Add rate limiting if needed 'as' => 'remote-config.api.', ],
User Model Configuration
/* |-------------------------------------------------------------------------- | Experimentable Model |-------------------------------------------------------------------------- | The model that participates in experiments (usually User model) */ 'experimentable_model' => env('REMOTE_CONFIG_USER_MODEL', \App\Models\User::class), /* |-------------------------------------------------------------------------- | Attribute Mapping |-------------------------------------------------------------------------- | Maps standard attributes to your model's field names */ 'attribute_mapping' => [ 'platform' => ['platform', 'os'], // Tries 'platform' first, then 'os' 'country' => ['country_code', 'geo_country_code'], 'language' => ['language', 'lang'], ],
Flow Types Configuration
/* |-------------------------------------------------------------------------- | Flow Types |-------------------------------------------------------------------------- | Define the types of configurations available in your app */ 'flow_types' => [ 'general' => 'General Configuration', 'onboarding' => 'Onboarding Flow', 'feature' => 'Feature Flags', 'ui' => 'UI Settings', 'content' => 'Content Configuration', 'checkout' => 'Checkout Flow', 'pricing' => 'Pricing Configuration', ],
Targeting Options
/* |-------------------------------------------------------------------------- | Targeting Options |-------------------------------------------------------------------------- | Define available platforms, countries, and languages */ 'targeting' => [ 'platforms' => [ 'ios' => 'iOS', 'android' => 'Android', 'web' => 'Web', 'mobile_web' => 'Mobile Web', ], 'countries' => [ 'SA' => 'Saudi Arabia', 'AE' => 'United Arab Emirates', 'KW' => 'Kuwait', 'QA' => 'Qatar', 'BH' => 'Bahrain', 'OM' => 'Oman', 'EG' => 'Egypt', 'JO' => 'Jordan', 'LB' => 'Lebanon', 'US' => 'United States', 'GB' => 'United Kingdom', // Add your countries ], 'languages' => [ 'ar' => 'Arabic', 'en' => 'English', 'fr' => 'French', 'es' => 'Spanish', // Add your languages ], ],
Redis Configuration
/* |-------------------------------------------------------------------------- | Redis Configuration |-------------------------------------------------------------------------- | For experiment counters and test overrides */ 'redis' => [ 'connection' => env('REMOTE_CONFIG_REDIS_CONNECTION', 'default'), 'key_prefix' => 'remote_config:counter:', ], /* |-------------------------------------------------------------------------- | Testing Override Configuration |-------------------------------------------------------------------------- | Cache store for test overrides (redis, memcached, etc.) */ 'testing' => [ 'cache_store' => env('REMOTE_CONFIG_TESTING_CACHE_STORE', 'redis'), ],
Audit Logging
/* |-------------------------------------------------------------------------- | Audit Logging |-------------------------------------------------------------------------- | Enable automatic audit logging for all changes */ 'audit_logging' => [ 'enabled' => true, 'log_assignments' => true, 'log_confirmations' => true, 'user_id_column' => 'user_id', // Column name in your user table ],
Database Configuration
/* |-------------------------------------------------------------------------- | Database Table Prefix |-------------------------------------------------------------------------- | Prefix for all package tables. Leave empty for no prefix. */ 'table_prefix' => env('REMOTE_CONFIG_TABLE_PREFIX', ''),
Quick Start
1. Add Trait to Your User Model
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Qanah\RemoteConfig\Traits\Experimentable; class User extends Authenticatable { use Experimentable; // Your existing user model code... }
2. Create Your First Flow
use Qanah\RemoteConfig\Models\Flow; // Create a base configuration $baseFlow = Flow::create([ 'type' => 'onboarding', 'content' => [ 'steps' => ['welcome', 'profile', 'preferences'], 'theme' => 'light', 'show_tutorial' => false, ], 'is_active' => true, ]);
3. Get Configuration in Your App
// In your controller use Qanah\RemoteConfig\Models\Flow; public function getOnboardingConfig(Request $request) { $user = $request->user(); // Get configuration for user $config = get_user_config($user, 'onboarding', [ 'platform' => $request->input('platform', 'web'), 'country' => $user->country_code ?? 'US', 'language' => $user->language ?? 'en', ]); return response()->json([ 'success' => true, 'config' => $config, ]); }
Integration Guide
Step-by-Step Integration for a New Laravel App
1. Install and Configure
Follow the Installation steps above.
2. Update User Model
Add the Experimentable trait to your User model:
<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; use Qanah\RemoteConfig\Traits\Experimentable; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable, Experimentable; // Add these attributes if you want to use targeting protected $fillable = [ 'name', 'email', 'password', 'platform', // ios, android, web 'country_code', // SA, US, etc. 'language', // ar, en, etc. ]; }
3. Add User Attributes Migration (Optional)
If your users table doesn't have platform/country/language columns:
php artisan make:migration add_targeting_fields_to_users_table
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->string('platform')->nullable()->index(); $table->string('country_code', 2)->nullable()->index(); $table->string('language', 2)->nullable()->index(); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn(['platform', 'country_code', 'language']); }); } };
php artisan migrate
4. Create API Controller
Create a controller to serve configurations to your app:
php artisan make:controller Api/ConfigController
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Qanah\RemoteConfig\Services\ConfigService; class ConfigController extends Controller { protected ConfigService $configService; public function __construct(ConfigService $configService) { $this->configService = $configService; } /** * Get remote configuration for user */ public function index(Request $request) { $user = $request->user(); $validated = $request->validate([ 'type' => 'required|string', 'platform' => 'nullable|string', 'country' => 'nullable|string', 'language' => 'nullable|string', ]); $config = $this->configService->getConfig( $user, $validated['type'], [ 'platform' => $validated['platform'] ?? null, 'country' => $validated['country'] ?? null, 'language' => $validated['language'] ?? null, ], $request->ip() // For test overrides ); return response()->json([ 'success' => true, 'data' => $config, ]); } }
5. Register API Routes
In routes/api.php:
use App\Http\Controllers\Api\ConfigController; Route::middleware('auth:sanctum')->group(function () { Route::get('/config', [ConfigController::class, 'index']); });
6. Protect Admin Routes
In config/remote-config.php, configure admin routes:
'routes' => [ 'enabled' => true, 'prefix' => 'admin/remote-config', 'middleware' => ['web', 'auth', 'admin'], // Add your admin middleware 'as' => 'remote-config.', ],
Create an admin middleware if you don't have one:
php artisan make:middleware IsAdmin
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class IsAdmin { public function handle(Request $request, Closure $next) { if (!$request->user() || !$request->user()->is_admin) { abort(403, 'Unauthorized'); } return $next($request); } }
Register in app/Http/Kernel.php:
protected $middlewareAliases = [ // ... other middleware 'admin' => \App\Http\Middleware\IsAdmin::class, ];
Usage Examples
Example 1: Feature Flags
// Create feature flags configuration $featureFlags = Flow::create([ 'type' => 'feature', 'content' => [ 'new_checkout' => false, 'dark_mode' => true, 'social_login' => true, 'referral_program' => false, ], 'is_active' => true, ]); // In your app $config = get_user_config($user, 'feature'); if ($config['new_checkout']) { // Show new checkout flow } else { // Show old checkout flow }
Example 2: A/B Testing Onboarding Flow
use Qanah\RemoteConfig\Models\Flow; use Qanah\RemoteConfig\Models\Experiment; // Create two onboarding variants $variantA = Flow::create([ 'type' => 'onboarding', 'content' => [ 'steps' => ['welcome', 'profile', 'preferences', 'complete'], 'skip_enabled' => false, ], 'is_active' => true, ]); $variantB = Flow::create([ 'type' => 'onboarding', 'content' => [ 'steps' => ['welcome', 'quick_setup'], // Shorter flow 'skip_enabled' => true, ], 'is_active' => true, ]); // Create experiment $experiment = Experiment::create([ 'name' => 'Quick Onboarding Test', 'type' => 'onboarding', 'platforms' => ['ios', 'android'], 'countries' => ['US', 'SA'], 'languages' => ['en'], 'is_active' => true, ]); // Attach variants with 50/50 split $experiment->flows()->attach($variantA, ['ratio' => 50]); $experiment->flows()->attach($variantB, ['ratio' => 50]); // Users automatically get assigned $config = get_user_config($user, 'onboarding', [ 'platform' => 'ios', 'country' => 'US', 'language' => 'en', ]); // Track completion $user->confirmExperiment('Quick Onboarding Test', [ 'completed' => true, 'time_spent' => 120, ]);
Example 3: Fetching Multiple Configuration Types
// Fetch multiple types in a single API request // GET /api/config?type[]=onboarding&type[]=feature&type[]=pricing $response = Http::withToken($token)->get('https://api.example.com/api/config', [ 'type' => ['onboarding', 'feature', 'pricing'], 'platform' => 'ios', 'country' => 'US', 'language' => 'en', ]); // Response format for multiple types: { "success": true, "data": { "onboarding": { "steps": ["welcome", "profile", "preferences"], "theme": "light" }, "feature": { "new_checkout": true, "dark_mode": false }, "pricing": { "monthly_price": 9.99, "trial_days": 14 } }, "meta": { "onboarding": { "has_experiment": true, "experiment_id": 5, "flow_id": 12 }, "feature": { "has_experiment": false, "experiment_id": null, "flow_id": 8 }, "pricing": { "has_experiment": true, "experiment_id": 3, "flow_id": 15 } } } // Single type request // GET /api/config?type=onboarding { "success": true, "data": { "onboarding": { "steps": ["welcome", "profile", "preferences"], "theme": "light" } }, "meta": { "onboarding": { "has_experiment": true, "experiment_id": 5, "flow_id": 12 } } } // No type parameter - returns ALL active types // GET /api/config { "success": true, "data": { "onboarding": {...}, "feature": {...}, "pricing": {...} }, "meta": { "onboarding": {...}, "feature": {...}, "pricing": {...} } }
Example 4: Deploying Winners
use Qanah\RemoteConfig\Models\Winner; // After analyzing experiment results, deploy the winner $winner = Winner::create([ 'type' => 'onboarding', 'platform' => 'ios', 'country_code' => 'US', 'language' => 'en', 'content' => [ 'steps' => ['welcome', 'quick_setup'], 'skip_enabled' => true, ], 'is_active' => true, ]); // Now all iOS/US/English users get this configuration // Winners override experiments
Example 4: QA Testing with Test Overrides
use Qanah\RemoteConfig\Models\TestOverride; use Qanah\RemoteConfig\Models\Flow; // Create a test flow for QA $qaFlow = Flow::create([ 'type' => 'feature', 'content' => [ 'new_feature_x' => true, 'debug_mode' => true, ], 'is_active' => true, ]); // Create test override for specific IP TestOverride::create([ 'ip' => '192.168.1.100', // QA tester's IP 'type' => 'feature', 'flow_id' => $qaFlow->id, ]); // QA tester at this IP will now see the test flow // Test overrides have highest priority
Example 5: Multi-Platform Targeting
$experiment = Experiment::create([ 'name' => 'Premium Features Test', 'type' => 'pricing', 'platforms' => ['ios'], // iOS only 'countries' => ['US', 'GB', 'CA'], 'languages' => ['en'], 'user_created_after_date' => '2024-01-01', // Only new users 'is_active' => true, ]); $premiumFlow = Flow::create([ 'type' => 'pricing', 'content' => [ 'monthly_price' => 9.99, 'annual_price' => 99.99, 'trial_days' => 14, ], 'is_active' => true, ]); $experiment->flows()->attach($premiumFlow, ['ratio' => 100]);
API Reference
API Endpoints
POST /api/config/issue - Report Validation Issues
Report validation issues from client applications. Supports both single and multiple issues in one request.
Single Issue:
curl -X POST https://api.example.com/api/config/issue \ -H "Authorization: Bearer token" \ -H "Content-Type: application/json" \ -d '{ "path": "features", "invalid_value": [], "platform": "android", "type": "features", "error_message": "Invalid data type: expected Map, got List" }' # Response: { "success": true, "message": "Validation issue reported successfully", "data": { "id": 123, "path": "features", "invalid_value": [], "platform": "android", "type": "features", "error_message": "Invalid data type: expected Map, got List", "created_at": "2024-01-15T10:30:00.000000Z" } }
Multiple Issues (Bulk):
curl -X POST https://api.example.com/api/config/issue \ -H "Authorization: Bearer token" \ -H "Content-Type: application/json" \ -d '[ { "path": "features", "invalid_value": [], "platform": "android", "type": "features", "error_message": "Invalid data type: expected Map, got List" }, { "path": "ui", "invalid_value": [], "platform": "android", "type": "ui", "error_message": "Invalid data type: expected Map, got List" } ]' # Response: { "success": true, "message": "2 validation issues reported successfully", "data": [ { "id": 123, "path": "features", ... }, { "id": 124, "path": "ui", ... } ] }
Helper Functions
get_user_config($experimentable, string $type, array $attributes = [])
Get configuration for a user with automatic experiment assignment.
$config = get_user_config($user, 'onboarding', [ 'platform' => 'ios', 'country' => 'SA', 'language' => 'ar', ]);
is_in_experiment($experimentable, string $experimentName): bool
Check if user is assigned to an experiment.
if (is_in_experiment($user, 'Quick Onboarding Test')) { // User is in this experiment }
experiment_variant($experimentable, string $type)
Get the flow variant a user is assigned to.
$variant = experiment_variant($user, 'onboarding'); echo $variant->content['steps']; // Array of steps
experiment_stats(Experiment $experiment): array
Get statistics for an experiment.
$stats = experiment_stats($experiment); // Returns assignment counts and percentages per variant
Trait Methods (Experimentable)
assignToExperiment(string $type, ?int $flowId = null, array $attributes = [])
Manually assign user to an experiment.
$assignment = $user->assignToExperiment('onboarding', null, [ 'platform' => 'ios', 'country' => 'US', 'language' => 'en', ]);
confirmExperiment(string $experimentName, array $metadata = [])
Confirm/track experiment completion.
$user->confirmExperiment('Quick Onboarding Test', [ 'completed_steps' => 4, 'time_spent' => 180, 'converted' => true, ]);
hasConfirmedExperiment(string $experimentName): bool
Check if user confirmed an experiment.
if ($user->hasConfirmedExperiment('Quick Onboarding Test')) { // User completed this experiment }
ConfigService Methods
use Qanah\RemoteConfig\Services\ConfigService; $configService = app(ConfigService::class); // Get configuration with test override support $config = $configService->getConfig( $user, 'feature_flags', ['platform' => 'ios', 'country' => 'SA', 'language' => 'ar'], $request->ip() // For test overrides ); // Get assignment statistics $stats = $configService->getAssignmentStats($experiment);
ExperimentService Methods
use Qanah\RemoteConfig\Services\ExperimentService; $experimentService = app(ExperimentService::class); // Select flow variant for user (with ratio-based distribution) $flow = $experimentService->selectFlow($experiment); // Get experiment statistics $stats = $experimentService->getExperimentStats($experiment); // Clear experiment counters $experimentService->clearExperimentCounters($experiment);
TestOverride Methods
use Qanah\RemoteConfig\Models\TestOverride; // Create test override TestOverride::create([ 'ip' => '192.168.1.1', 'type' => 'onboarding', 'flow_id' => 5, ]); // Check if exists $exists = TestOverride::exists('192.168.1.1', 'onboarding'); // Find by IP and type $override = TestOverride::findByIpAndType('192.168.1.1', 'onboarding'); // Delete TestOverride::deleteByIpAndType('192.168.1.1', 'onboarding'); // Get all for a type $overrides = TestOverride::getAllForType('onboarding'); // Returns: ['192.168.1.1' => 5, '10.0.0.1' => 7] // Get all $all = TestOverride::all(); // Clear all TestOverride::clear();
Database Structure
The package creates 11 tables:
| Table | Purpose |
|---|---|
flows |
Configuration variants (JSON content) |
flow_logs |
Audit log for flow changes |
experiments |
A/B test definitions with targeting |
experiment_logs |
Audit log for experiment changes |
experiment_flow |
Pivot table (experiments ↔ flows with ratios) |
experiment_assignments |
User-to-experiment assignments (polymorphic) |
experiment_assignment_logs |
Historical assignment logs |
winners |
Deployed winning configurations |
winner_logs |
Audit log for winner changes |
confirmations |
User experiment confirmations/conversions |
validation_issues |
Client-reported configuration errors |
Configuration Priority Order
- Test Override (highest) - IP-based override for testing
- Winner - Deployed winning variant for specific targeting
- Experiment - Active A/B test assignment
- Base Flow (lowest) - Default configuration
Admin Panel
Access the admin panel at /remote-config (or your configured prefix):
/remote-config/flows- Manage configuration variants/remote-config/experiments- Create and manage A/B tests/remote-config/winners- Deploy winning configurations/remote-config/testing- Manage test overrides for QA
The admin panel includes:
- JSON editor for configuration content
- Experiment flow attachment with ratio configuration
- Assignment statistics and analytics
- Audit logs with change tracking
Testing
Testing in Development
Option 1: Use Test Overrides
// Set up test override for your IP TestOverride::create([ 'ip' => request()->ip(), 'type' => 'onboarding', 'flow_id' => $testFlowId, ]);
Option 2: Use Helper Functions
// In your tests $config = get_user_config($testUser, 'onboarding', [ 'platform' => 'ios', 'country' => 'US', 'language' => 'en', ]); $this->assertEquals(['steps' => ['welcome', 'profile']], $config);
PHPUnit Testing
use Qanah\RemoteConfig\Models\Experiment; use Qanah\RemoteConfig\Models\Flow; use Tests\TestCase; class ExperimentTest extends TestCase { public function test_user_gets_assigned_to_experiment() { // Create flows $flowA = Flow::factory()->create([ 'type' => 'test', 'content' => ['variant' => 'A'], ]); $flowB = Flow::factory()->create([ 'type' => 'test', 'content' => ['variant' => 'B'], ]); // Create experiment $experiment = Experiment::factory()->create([ 'type' => 'test', 'platforms' => ['web'], 'countries' => ['US'], 'languages' => ['en'], 'is_active' => true, ]); // Attach flows with 50/50 split $experiment->flows()->attach($flowA, ['ratio' => 50]); $experiment->flows()->attach($flowB, ['ratio' => 50]); // Test assignment $user = User::factory()->create([ 'platform' => 'web', 'country_code' => 'US', 'language' => 'en', ]); $config = get_user_config($user, 'test'); $this->assertTrue( $config['variant'] === 'A' || $config['variant'] === 'B' ); // Assignment should be sticky $config2 = get_user_config($user, 'test'); $this->assertEquals($config, $config2); } public function test_winner_overrides_experiment() { $winner = Winner::create([ 'type' => 'test', 'platform' => 'web', 'country_code' => 'US', 'language' => 'en', 'content' => ['variant' => 'winner'], 'is_active' => true, ]); $user = User::factory()->create([ 'platform' => 'web', 'country_code' => 'US', 'language' => 'en', ]); $config = get_user_config($user, 'test'); $this->assertEquals('winner', $config['variant']); } }
Best Practices
1. Configuration Structure
Keep your configurations consistent:
// Good: Structured, typed configuration [ 'features' => [ 'new_checkout' => true, 'dark_mode' => false, ], 'limits' => [ 'max_uploads' => 10, 'file_size_mb' => 5, ], ] // Avoid: Flat, untyped configuration [ 'setting1' => 'value', 'setting2' => 123, 'random_key' => true, ]
2. Experiment Naming
Use clear, descriptive experiment names:
// Good 'Checkout Flow Redesign 2024 Q1' 'Premium Pricing Test - iOS Only' // Avoid 'test1' 'experiment_abc'
3. Ratio Configuration
Always ensure ratios sum to a reasonable distribution:
// 50/50 split $experiment->flows()->attach($flowA, ['ratio' => 50]); $experiment->flows()->attach($flowB, ['ratio' => 50]); // 70/30 split $experiment->flows()->attach($control, ['ratio' => 70]); $experiment->flows()->attach($variant, ['ratio' => 30]);
4. Targeting
Be specific with targeting to avoid polluting data:
$experiment = Experiment::create([ 'name' => 'iOS Premium Feature Test', 'type' => 'pricing', 'platforms' => ['ios'], // Specific platform 'countries' => ['US', 'GB'], // Specific countries 'languages' => ['en'], // Specific language 'user_created_after_date' => '2024-01-01', // Only new users 'is_active' => true, ]);
5. Monitoring and Cleanup
Regularly review and deactivate old experiments:
// Deactivate experiment after conclusion $experiment->update(['is_active' => false]); // Deploy winner $winner = Winner::create([ 'type' => $experiment->type, 'platform' => 'ios', 'country_code' => 'US', 'language' => 'en', 'content' => $winningFlow->content, 'is_active' => true, ]);
6. Test Before Deploying
Always test configurations with test overrides before deploying:
// Create test override for your IP TestOverride::create([ 'ip' => '203.0.113.1', 'type' => 'feature', 'flow_id' => $newFeatureFlow->id, ]); // Test in browser/app // Then clear when done TestOverride::clear();
Environment Variables Reference
Add these to your .env file:
# Enable/Disable System REMOTE_CONFIG_ENABLED=true # Testing REMOTE_CONFIG_TESTING_ENABLED=true REMOTE_CONFIG_TESTING_CACHE_STORE=redis # User Filter REMOTE_CONFIG_USER_CREATED_AFTER=2022-08-29 # Cache REMOTE_CONFIG_CACHE_TTL=604800 # Routes REMOTE_CONFIG_ROUTE_PREFIX=remote-config REMOTE_CONFIG_API_PREFIX=api/config # Database REMOTE_CONFIG_TABLE_PREFIX= # Redis REMOTE_CONFIG_REDIS_CONNECTION=default # User Model REMOTE_CONFIG_USER_MODEL=App\Models\User
Contributing
Contributions are welcome! Please submit pull requests or open issues on GitHub.
License
MIT License
Credits
Developed by Jawab Team
Support
For issues and questions:
- GitHub Issues: qanah/remote-config
- Email: support@jawab.app