Development Workflow¶
Trunk-Based Development for Forma3D.Connect¶
✅ Status: This workflow is fully implemented in
azure-pipelines.yml. The pipeline enforces trunk-based development withmainas 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¶
main(trunk) is the only long-lived branchmainmust 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¶
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)¶
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¶
| 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¶
| 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
Benefits:
- CI stays fast even as the monorepo grows
- Feedback loops remain short
- Parallelism is maximized
6. Azure DevOps Pipeline¶
6.1 Pipeline Architecture¶
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¶
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 |
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¶
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?¶
12.2 What If My PR Is Too Big?¶
Split it into smaller PRs:
- Extract refactoring into a separate PR
- Add feature behind a flag in one PR
- Enable the feature in a follow-up PR
12.3 What If I Need a Long-Running Feature?¶
- Use feature flags — merge incomplete work behind a flag
- Slice vertically — ship thin end-to-end slices
- 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.