Skip to main content

Feature Flags

ForgeX uses feature flags to control which modules, roles, and features are enabled across the platform. This allows safe rollout of new functionality, gradual phase releases, and environment-specific configurations without code changes.

info

Feature flags are defined in packages/shared/constants/features.js and shared across all services for consistent behavior.


Why Feature Flags?​

πŸš€

Phased Rollout

Roll out Phase 2 (Projects) and Phase 3 (Field) features gradually without affecting Phase 1 (Bids) users.

πŸ–₯️

Environment Control

Enable features in dev/staging before production. Test new features safely.

πŸ‘₯

Role Management

Hide roles (OPS, ACCOUNTING, FOREMAN) until their corresponding modules are ready.

πŸ”€

Zero Downtime

Toggle features without deployments or server restarts.


Feature Flags Configuration​

All feature flags are defined in packages/shared/constants/features.js:

module.exports = {
// Module availability flags
ENABLED_MODULES: {
BID_MANAGER: true, // Phase 1 - LIVE βœ…
PROJECT_MANAGEMENT: false, // Phase 2 - Coming soon 🚧
FIELD_OPERATIONS: false, // Phase 3 - Coming soon 🚧
},

// Role visibility flags
ENABLED_ROLES: {
ADMIN: true, // Phase 1 βœ…
ESTIMATOR: true, // Phase 1 βœ…
PM: true, // Phase 1 (read-only bids) βœ…
OPS: false, // Phase 2 🚧
ACCOUNTING: false,// Phase 2 🚧
FOREMAN: false, // Phase 3 🚧
},

// Service API endpoints (environment-specific)
API_ENDPOINTS: {
BIDS: process.env.BIDS_API_URL || 'http://localhost:5001',
PROJECTS: process.env.PROJECTS_API_URL || 'http://localhost:5002',
FIELD: process.env.FIELD_API_URL || 'http://localhost:5003',
},

// Pub/Sub topic names
PUBSUB_TOPICS: {
BID_AWARDED: 'bid-awarded',
PROJECT_UPDATED: 'project-updated',
TIMESHEET_SUBMITTED: 'timesheet-submitted',
}
};

Module Flags​

Module flags control which apps/services are available to users.

ENABLED_MODULES​

FlagStatusDescription
BID_MANAGERβœ… truePhase 1 - Bid management system (live)
PROJECT_MANAGEMENT🚧 falsePhase 2 - PO tracking, cost management
FIELD_OPERATIONS🚧 falsePhase 3 - Timesheets, GPS tracking

How Module Flags Work​

Portal Service reads ENABLED_MODULES to show/hide app tiles:

import { ENABLED_MODULES } from '../../../../packages/shared/constants/features';

const availableApps = [
{ name: 'Bids', enabled: ENABLED_MODULES.BID_MANAGER, url: '/bids' },
{ name: 'Projects', enabled: ENABLED_MODULES.PROJECT_MANAGEMENT, url: '/projects' },
{ name: 'Field', enabled: ENABLED_MODULES.FIELD_OPERATIONS, url: '/field' }
];

// Only show enabled apps
const visibleApps = availableApps.filter(app => app.enabled);

Result:

  • User sees only the Bids tile (Phase 1)
  • Projects and Field tiles are hidden until flags are enabled

Role Flags​

Role flags control which user roles are visible in the UI (user management, dropdowns, etc.).

ENABLED_ROLES​

FlagStatusUsed ByDescription
ADMINβœ… trueAll modulesFull platform access
ESTIMATORβœ… trueBidsCreate/edit bids
PMβœ… trueBids (read), ProjectsProject management
OPS🚧 falseProjectsPurchase orders
ACCOUNTING🚧 falseProjectsCost reconciliation
FOREMAN🚧 falseFieldCrew management
note

All roles exist in the database (User model includes all 6 roles). Role flags only control UI visibility, not database schema.

How Role Flags Work​

Admin panel filters role dropdown to show only enabled roles:

import { ENABLED_ROLES } from '../../../../packages/shared/constants/features';
import { UserRole } from '../../../../packages/shared/types';

const allRoles = Object.values(UserRole);
const availableRoles = allRoles.filter(role => ENABLED_ROLES[role]);

// Render dropdown
<Select options={availableRoles} />

Result:

  • Dropdown shows: ADMIN, ESTIMATOR, PM
  • Hidden: OPS, ACCOUNTING, FOREMAN (until Phase 2/3)

API Endpoints Configuration​

API_ENDPOINTS defines service URLs for inter-service communication and frontend API calls.

Default Values​

API_ENDPOINTS: {
BIDS: process.env.BIDS_API_URL || 'http://localhost:5001',
PROJECTS: process.env.PROJECTS_API_URL || 'http://localhost:5002',
FIELD: process.env.FIELD_API_URL || 'http://localhost:5003',
}

Environment-Specific Overrides​

.env (local):

BIDS_API_URL=http://localhost:5001
PROJECTS_API_URL=http://localhost:5002
FIELD_API_URL=http://localhost:5003

Services communicate via localhost ports.

Usage​

import { API_ENDPOINTS } from '../../../../packages/shared/constants/features';

// Frontend: Make API call to correct environment
const response = await fetch(`${API_ENDPOINTS.BIDS}/api/bids`, {
method: 'GET',
credentials: 'include' // Sends session cookies
});

// Backend: Call another service
const projectsResponse = await axios.post(`${API_ENDPOINTS.PROJECTS}/api/projects`, data);

Pub/Sub Topics​

PUBSUB_TOPICS defines Google Cloud Pub/Sub topic names for inter-service events.

Topic Definitions​

PUBSUB_TOPICS: {
BID_AWARDED: 'bid-awarded',
PROJECT_UPDATED: 'project-updated',
TIMESHEET_SUBMITTED: 'timesheet-submitted',
}

Event Flow (Phase 2+)​

πŸ” Click diagram to expand

Usage (Bids Service):

const { PUBSUB_TOPICS } = require('../../../../packages/shared/constants/features');
const { PubSub } = require('@google-cloud/pubsub');

const pubsub = new PubSub();

// Publish event when bid is awarded
async function onBidAwarded(bid) {
const topic = pubsub.topic(PUBSUB_TOPICS.BID_AWARDED);

await topic.publishMessage({
json: {
bidId: bid.id,
jobName: bid.jobName,
clientId: bid.clientId,
totalCost: bid.totalCost,
awardedAt: new Date().toISOString()
}
});
}

Usage (Projects Service):

// Subscribe to bid awarded events
const subscription = pubsub.subscription('projects-service-sub');

subscription.on('message', async (message) => {
const data = JSON.parse(message.data.toString());

if (message.attributes.topic === PUBSUB_TOPICS.BID_AWARDED) {
await createProjectFromBid(data);
}

message.ack();
});

Enabling New Features​

1
Update Feature Flags

Edit packages/shared/constants/features.js:

ENABLED_MODULES: {
BID_MANAGER: true,
PROJECT_MANAGEMENT: true, // ← Enable Phase 2
FIELD_OPERATIONS: false,
}
2
Restart Services

Local development:

docker-compose restart

Production:

  • Redeploy services that import features.js
  • Or use environment variables for dynamic control (see below)
3
Verify Feature Availability

Portal: Projects tile should now be visible API: /api/projects endpoints should return 200 (not 503) Frontend: Projects navigation link should appear

4
Enable Related Roles

If the module requires new roles, enable them:

ENABLED_ROLES: {
ADMIN: true,
ESTIMATOR: true,
PM: true,
OPS: true, // ← Enable for Projects module
ACCOUNTING: true, // ← Enable for Projects module
FOREMAN: false,
}

Environment-Based Feature Flags​

For runtime control without code changes, use environment variables:

Setup​

Modify features.js to read from env:

module.exports = {
ENABLED_MODULES: {
BID_MANAGER: process.env.ENABLE_BID_MANAGER !== 'false', // Default: true
PROJECT_MANAGEMENT: process.env.ENABLE_PROJECT_MANAGEMENT === 'true', // Default: false
FIELD_OPERATIONS: process.env.ENABLE_FIELD_OPERATIONS === 'true', // Default: false
},

ENABLED_ROLES: {
ADMIN: true,
ESTIMATOR: true,
PM: true,
OPS: process.env.ENABLE_OPS_ROLE === 'true',
ACCOUNTING: process.env.ENABLE_ACCOUNTING_ROLE === 'true',
FOREMAN: process.env.ENABLE_FOREMAN_ROLE === 'true',
}
};

Usage​

.env (staging):

ENABLE_PROJECT_MANAGEMENT=true
ENABLE_OPS_ROLE=true
ENABLE_ACCOUNTING_ROLE=true

.env (production):

# Keep Phase 2 disabled in prod until fully tested
ENABLE_PROJECT_MANAGEMENT=false
ENABLE_OPS_ROLE=false
ENABLE_ACCOUNTING_ROLE=false

Result:

  • Staging has Projects module enabled for testing
  • Production keeps it disabled until release
  • No code changes needed β€” just env variable toggle

Best Practices​

πŸ”¬

Test in Staging First

Enable new features in staging before production. Verify functionality thoroughly.

πŸ“„

Document Flag Changes

Update this documentation when adding or changing flags. Include release notes.

πŸ”€

Use Environment Variables

For runtime control, use env vars instead of hardcoded booleans.

πŸ‘₯

Coordinate Deploys

When enabling multi-service features, deploy all affected services together.


Common Scenarios​

Enabling Phase 2 (Projects Module)

Steps:

  1. Set ENABLED_MODULES.PROJECT_MANAGEMENT = true
  2. Set ENABLED_ROLES.OPS = true
  3. Set ENABLED_ROLES.ACCOUNTING = true
  4. Deploy all services (portal, bids, projects)
  5. Verify Projects tile appears in Portal
  6. Test Projects API endpoints
Testing New Features in Dev

Steps:

  1. Enable flag in local .env file
  2. Restart Docker Compose: docker-compose restart
  3. Test feature thoroughly
  4. If stable, enable in staging
  5. After staging verification, enable in production
Gradual Role Rollout

Scenario: Enable OPS role only for specific users before full release.

Steps:

  1. Keep ENABLED_ROLES.OPS = false (hides from UI)
  2. Manually assign OPS role to test users via database
  3. Test users can access OPS features (permissions still work)
  4. After validation, set ENABLED_ROLES.OPS = true for all
Hotfix: Disable Buggy Feature

Scenario: Production feature has a critical bug. Disable immediately.

Steps (if using env vars):

  1. Update production .env: ENABLE_PROJECT_MANAGEMENT=false
  2. Restart services: gcloud run services update projects-backend
  3. Feature is disabled until hotfix is deployed

Steps (if hardcoded):

  1. Edit features.js: Set flag to false
  2. Commit and deploy emergency patch
  3. Re-enable after bug is fixed

Feature Flag History​

DateChangeReason
Jan 2025BID_MANAGER: truePhase 1 production launch
Jan 2025ENABLED_ROLES.ADMIN/ESTIMATOR/PM: truePhase 1 roles enabled
TBDPROJECT_MANAGEMENT: truePhase 2 release
TBDENABLED_ROLES.OPS/ACCOUNTING: truePhase 2 roles enabled
TBDFIELD_OPERATIONS: truePhase 3 release
TBDENABLED_ROLES.FOREMAN: truePhase 3 roles enabled

Future Enhancements​

note

Planned improvements for feature flag system:

  • Admin UI for flags: Toggle features from admin panel (no code changes)
  • Per-user flags: Enable features for specific users (beta testing)
  • Time-based flags: Auto-enable features on a scheduled date
  • A/B testing: Enable features for 50% of users
  • Feature analytics: Track usage of flagged features

Next Steps​