Skip to content

Development Workflow

Trunk-Based Development for Forma3D.Connect

Status: This workflow is fully implemented in azure-pipelines.yml. The pipeline enforces trunk-based development with main as the only long-lived branch.


1. Purpose

This document describes how we develop, integrate, test, and deploy code in the Forma3D.Connect monorepo using:

  • Trunk-Based Development (TBD) with short-lived feature branches
  • Pull requests for code review and quality gates
  • NX monorepo for affected-only builds and tests
  • Azure DevOps pipelines for CI/CD
  • Docker artifacts promoted through staging to production

Goals

Goal How We Achieve It
Fast integration Short-lived branches (hours, not days)
Deployment confidence Build once, promote artifacts
Developer autonomy Feature flags, small PRs
System stability Protected trunk, automated testing

2. Core Principles

2.1 Trunk Is the Single Source of Truth

uml diagram

  • main (trunk) is the only long-lived branch
  • main must always be deployable
  • All production artifacts originate from main

⚠️ A broken trunk is a production risk and must be fixed immediately.


2.2 Build Artifacts Are the Unit of Value

uml diagram

Every successful pipeline produces versioned Docker images:

Principle Description
Build once Images are built in the Package stage
Promote across environments Same image goes staging → production
Never rebuild per environment Environment config is injected at deploy time

This ensures:

  • Reproducibility — same binary in staging and production
  • Traceability — every image links to a commit
  • Safe rollbacks — previous versions remain in registry

3. Branching Model

3.1 Feature Branches (Short-Lived)

uml diagram

We use feature branches with strict constraints:

Constraint Requirement
Branch from main only
Lifetime Hours to max 1–2 days
Scope Small, focused changes
Naming feature/*, fix/*, chore/*

Examples:

feature/shopify-webhook-handler
fix/order-sync-null-reference
chore/update-nx-version

❌ No long-lived feature branches
❌ No integration branches
❌ No "almost done" branches that linger


3.2 Branch Health Rules

Rule Feature Branch Trunk (main)
Must build ✅ Yes ✅ Yes
Must pass tests ⚠️ Expected but not blocking Mandatory
Stability guaranteed ❌ No ✅ Yes
Deployable ❌ No ✅ Always

Key Insight:

  • A failing feature branch is acceptable (work in progress)
  • A failing trunk is not acceptable (blocks everyone)

Branch failures must be investigated quickly and either fixed or abandoned.


4. Pull Requests

4.1 Purpose

Pull requests exist to:

  • ✅ Review code quality and design
  • ✅ Share context across the team
  • ✅ Trigger automated validation
  • ✅ Protect the trunk

They are not gatekeeping mechanisms. Avoid perfectionism.


4.2 Pull Request Requirements

uml diagram

Requirement Target
Size < 400 lines of code changed
Scope One concern per PR
Review time Same business day
Status All checks green before merge

Required CI Checks (enforced by Azure DevOps):

  • ✅ Lint passes (nx affected --target=lint)
  • ✅ Type check passes (nx run-many --target=typecheck)
  • ✅ Unit tests pass (nx affected --target=test)
  • ✅ Build succeeds (nx affected --target=build)

4.3 Merge Policy

Policy Setting
Merge strategy Squash merge (recommended)
Direct commits to main ❌ Blocked
Merge with failing checks ❌ Blocked
Required reviewers 1 (configure in Azure DevOps)

5. NX Monorepo Strategy

5.1 Project Structure

uml diagram

App/Lib Purpose
apps/api NestJS backend API
apps/web React 19 frontend
apps/api-e2e API integration tests
apps/acceptance-tests Playwright + Gherkin tests
libs/* Shared code libraries
prisma/ Database schema and migrations

5.2 Affected-Only Builds

NX analyzes the dependency graph to build/test only what changed:

# Feature branches: affected only (fast)
pnpm nx affected --target=lint
pnpm nx affected --target=test
pnpm nx affected --target=build

# Main branch: full validation (thorough)
pnpm nx run-many --target=lint --all
pnpm nx run-many --target=test --all
pnpm nx run-many --target=build --all

uml diagram

Benefits:

  • CI stays fast even as the monorepo grows
  • Feedback loops remain short
  • Parallelism is maximized

6. Azure DevOps Pipeline

6.1 Pipeline Architecture

uml diagram


6.2 Stage Details

Stage Trigger Purpose
Validate All branches Lint + type check
Test All branches Unit tests with PostgreSQL
Build All branches Compile apps, detect affected
Package main only Build & push Docker images
Deploy Staging main only Deploy to staging server
Acceptance Test main only Health check + Playwright tests
Deploy Production main + approval Deploy to production
Smoke Test After production Verify production health

6.3 Feature Branch Pipeline

Triggered on: Every push to any branch (for fast feedback)

Runs: Validate → Test → Build (affected only)

Stops at: Build stage — no packaging or deployment

Purpose: Get fast feedback on every commit without waiting for PR creation


6.4 Trunk Pipeline

Triggered on: Merge to main

Runs: Full validation → Package → Deploy Staging → Acceptance Test

Only green builds produce promotable artifacts.


7. Environment Promotion

7.1 Artifact Flow

uml diagram

7.2 Staging Environment

Aspect Details
Server DigitalOcean droplet (167.172.45.47)
Images Pulled from registry, same as will go to prod
Database Managed PostgreSQL (staging instance)
Purpose Integration testing, smoke testing, exploratory testing

No rebuilds occur. The same Docker image that passes staging goes to production.


7.3 Production Environment

Aspect Details
Approval Manual approval gate in Azure DevOps
Images Exact same images as staging
Traceability Every deploy links to a specific commit
Rollback Pull previous image tag from registry

📌 If it wasn't tested in staging, it doesn't go to production.


8. Feature Flags

Feature flags are mandatory for:

Scenario Example
Incomplete features New Shopify sync UI (not ready for users)
Risky changes New order processing algorithm
Gradual rollouts Enable for 10% of users first

uml diagram

Benefits:

  • Merge early, release when ready
  • Continuous deployment without continuous release
  • Easy rollback (disable flag, not redeploy)

9. Comparison: This Model vs GitFlow

Aspect Trunk-Based (This Model) GitFlow
Long-lived branches 1 (main) 3+ (main, develop, release/*)
Integration frequency Continuous (daily) Late (per release)
Branch lifetime Hours to days Weeks to months
Merge conflicts Rare Common
Artifacts Central to workflow Secondary concern
Release model Continuous Event-based
Delivery speed High Slower
Complexity Low High

10. Daily Workflow

10.1 Developer Workflow

uml diagram

10.2 Quick Reference Commands

# Start new work
git checkout main
git pull origin main
git checkout -b feature/my-feature

# During development
pnpm nx affected --target=lint    # Check your changes
pnpm nx affected --target=test    # Run affected tests
pnpm nx affected --target=build   # Verify build

# Ready for review
git push -u origin feature/my-feature
# Create PR in Azure DevOps

# After merge (branch deleted automatically or manually)
git checkout main
git pull origin main
git branch -d feature/my-feature

11. Responsibilities

Developers

Responsibility Details
Keep branches short-lived Hours to 1-2 days max
Fix broken branches quickly Don't let failures linger
Keep PRs small < 400 LOC, single concern
Respect trunk stability Never merge failing code
Use feature flags For incomplete/risky features

Reviewers

Responsibility Details
Review fast Same business day
Focus on design and risk Not style nitpicks
Avoid perfectionism Good enough is good enough
Provide context Help the developer learn

Team

Responsibility Details
Treat broken trunk as urgent Drop everything to fix
Optimize flow over ceremony Reduce friction
Improve pipeline continuously Faster is better
Communicate blockers Don't suffer in silence

12. Troubleshooting

12.1 What If Trunk Breaks?

uml diagram

12.2 What If My PR Is Too Big?

Split it into smaller PRs:

  1. Extract refactoring into a separate PR
  2. Add feature behind a flag in one PR
  3. Enable the feature in a follow-up PR

12.3 What If I Need a Long-Running Feature?

  1. Use feature flags — merge incomplete work behind a flag
  2. Slice vertically — ship thin end-to-end slices
  3. Merge daily — keep integrating even if hidden

13. Key Takeaway

We optimize for fast feedback, small changes, and artifact flow — accepting temporary instability in branches to ensure permanent stability in trunk and production.

uml diagram