Best Practices

Guidelines and conventions for writing high-quality workflows.

Table of Contents

  1. Naming Conventions
  2. Structure & Organization
  3. Type Safety
  4. Error Handling
  5. Performance
  6. Security
  7. Maintainability
  8. Testing

Naming Conventions

Workflow Names

Use lowercase, hyphenated, descriptive names:

✅ Good:

yaml
1name: requirements-analysis
2name: github-pr-review
3name: weekly-report-generator

❌ Bad:

yaml
1name: ReqAnalysis
2name: pr_review
3name: report1

Job Names

Use lowercase, hyphenated, action-oriented names:

✅ Good:

yaml
1jobs:
2  analyze-requirements:
3  create-specification:
4  notify-stakeholders:

❌ Bad:

yaml
1jobs:
2  job1:
3  doStuff:
4  req:

Variable Names

Use lowercase, underscored descriptive names:

✅ Good:

yaml
1vars:
2  max_concurrent_jobs: 5
3  organization_name: "Acme"
4  default_timeout_minutes: 60

❌ Bad:

yaml
1vars:
2  MAX_JOBS: 5
3  org: "Acme"
4  timeout: 60

Structure & Organization

Keep Workflows Focused

Each workflow should have a single responsibility:

✅ Good:

yaml
1# Single responsibility: Analyze requirements
2name: analyze-requirements
3jobs:
4  analyze:
5    ...
6  create-spec:
7    ...

❌ Bad:

yaml
1# Multiple responsibilities mixed
2name: full-pipeline
3jobs:
4  analyze:
5    ...
6  build:
7    ...
8  test:
9    ...
10  deploy:
11    ...

Order Jobs Logically

Place related jobs together:

yaml
1jobs:
2  # Input processing
3  validate-input:
4    ...
5
6  # Analysis phase
7  analyze:
8    ...
9  review-analysis:
10    ...
11
12  # Output generation
13  create-deliverable:
14    ...
15
16  # Notification
17  notify-complete:
18    ...

Use Descriptive Titles

Add title field for clarity:

✅ Good:

yaml
1jobs:
2  analyze-requirements:
3    title: Analyze Project Requirements
4    uses: xema/agent

❌ Bad:

yaml
1jobs:
2  analyze-requirements:
3    uses: xema/agent

Type Safety

Always Type Inputs

Specify types explicitly:

✅ Good:

yaml
1on:
2  workflow_dispatch:
3    inputs:
4      budget:
5        type: number
6        required: true
7      include_testing:
8        type: boolean
9        required: false
10        defaultValue: true
11      priority:
12        type: string
13        enum: [low, medium, high]
14        required: true

❌ Bad:

yaml
1on:
2  workflow_dispatch:
3    inputs:
4      budget:           # Type not specified
5      include_testing:  # Required missing
6      priority: null

Use Enums for Closed Sets

Don't accept arbitrary strings for known values:

✅ Good:

yaml
1inputs:
2  environment:
3    type: string
4    enum: [dev, staging, production]
5    required: true

❌ Bad:

yaml
1inputs:
2  environment:
3    type: string
4    required: true  # Accepts ANY string

Declare Outputs

Always extract outputs explicitly:

✅ Good:

yaml
1jobs:
2  analyze:
3    uses: xema/agent
4    with: { task: Analyze }
5    outputs:
6      summary: ${{ result.summary }}
7      issues: ${{ result.issues }}

❌ Bad:

yaml
1jobs:
2  analyze:
3    uses: xema/agent
4    with: { task: Analyze }
5    # Outputs not declared

Error Handling

Define Retry Policies

Set clear retry expectations:

✅ Good:

yaml
1defaults:
2  retry:
3    maxAttempts: 3
4    backoffCoefficient: 2.0
5    initialInterval: 1s
6
7jobs:
8  external-call:
9    retry:
10      maxAttempts: 5  # Override for flaky service

❌ Bad:

yaml
1# No retry policy defined

Handle Failures Explicitly

Don't let failures silently stop workflows:

✅ Good:

yaml
1jobs:
2  primary:
3    uses: xema/agent
4    with: { task: Primary }
5
6  fallback:
7    if: ${{ failure() }}
8    uses: xema/agent
9    with: { task: Fallback }
10
11  notify:
12    if: ${{ always() }}  # Always run
13    uses: xema/webhook

❌ Bad:

yaml
1jobs:
2  primary:
3    uses: xema/agent
4  # No fallback or notification

Set Timeouts

Prevent workflows from hanging:

✅ Good:

yaml
1defaults:
2  timeout: "24h"
3
4jobs:
5  approval:
6    timeout: "7 days"
7    uses: xema/decision-gate

❌ Bad:

yaml
1# No timeout defined
2# Workflow could hang indefinitely

Performance

Parallelize When Possible

Use matrix for parallel execution:

✅ Good:

yaml
1jobs:
2  test-all:
3    strategy:
4      matrix:
5        version: [20, 21, 22]
6      maxParallel: 4
7    uses: xema/agent

❌ Bad:

yaml
1jobs:
2  test-20:
3    uses: xema/agent
4  test-21:
5    needs: test-20        # Sequential!
6    uses: xema/agent

Avoid Sequential Dependencies

When not needed:

✅ Good:

yaml
1jobs:
2  build-backend:
3    uses: xema/agent
4
5  build-frontend:
6    uses: xema/agent  # No dependency
7
8  test-integration:
9    needs: [build-backend, build-frontend]
10    uses: xema/agent

❌ Bad:

yaml
1jobs:
2  build-backend:
3    uses: xema/agent
4
5  build-frontend:
6    needs: build-backend   # Unnecessary!
7    uses: xema/agent

Limit Matrix Expansion

Prevent runaway jobs:

✅ Good:

yaml
1strategy:
2  dynamic:
3    from: ${{ needs.discover.outputs.services }}
4    as: service
5    maxEntries: 50        # Safe limit

❌ Bad:

yaml
1strategy:
2  dynamic:
3    from: ${{ needs.discover.outputs.services }}
4    as: service
5    # Could explode to 1000+ jobs!

Security

Protect Secrets

Never hardcode secrets:

✅ Good:

yaml
1with:
2  api_key: ${{ secrets.API_KEY }}
3  password: ${{ secrets.DB_PASSWORD }}

❌ Bad:

yaml
1with:
2  api_key: "sk-1234567890abcdef"  # Exposed!

Declare Permissions

Be explicit about resource access:

✅ Good:

yaml
1permissions:
2  repos: read
3  kb: limited
4  backlog: write
5  artifacts: read

❌ Bad:

yaml
1# No permissions declared
2# Defaults to maximum?

Validate Webhook Filters

Don't trust all webhooks:

✅ Good:

yaml
1on:
2  webhook:
3    - event: scm.push
4      filters:
5        projectId: proj_123
6        branch: main

❌ Bad:

yaml
1on:
2  webhook:
3    - event: scm.push
4    # Triggers on ALL pushes to ALL projects!

Maintainability

Document Complex Workflows

Add descriptions and titles:

✅ Good:

yaml
1metadata:
2  name: requirements-analysis
3  version: 1.0.0
4  description: |
5    Analyzes project requirements, routes through technical and PM review,
6    and generates specification document. Supports simple/standard/enterprise
7    scope levels.
8
9vars:
10  max_scope: enterprise           # Comment on non-obvious values

Use Variables for Reusable Values

Don't repeat magic strings:

✅ Good:

yaml
1vars:
2  organization: "Acme Corp"
3  max_retries: 3
4  notification_url: "https://alerts.acme.com"
5
6jobs:
7  analyze:
8    with:
9      org: ${{ vars.organization }}
10  notify:
11    with:
12      url: ${{ vars.notification_url }}

❌ Bad:

yaml
1jobs:
2  analyze:
3    with:
4      org: "Acme Corp"
5  job2:
6    with:
7      org: "Acme Corp"  # Repeated!
8  notify:
9    with:
10      url: "https://alerts.acme.com"

Keep Jobs Small

One responsibility per job:

✅ Good:

yaml
1jobs:
2  analyze:
3    uses: xema/agent
4    with: { task: Analyze }
5
6  review:
7    needs: analyze
8    uses: xema/decision-gate
9
10  publish:
11    needs: review
12    uses: xema/emit-artifact

❌ Bad:

yaml
1jobs:
2  do-everything:
3    # Analyze + review + publish in one job
4    # Hard to debug, reuse, test

Version Everything

Track changes with semantic versioning:

✅ Good:

yaml
1metadata:
2  name: my-workflow
3  version: 1.2.3      # MAJOR.MINOR.PATCH

Changes:

  • 1.2.3 → 1.2.4 — Bug fix
  • 1.2.3 → 1.3.0 — New feature
  • 1.2.3 → 2.0.0 — Breaking change

Testing

Test with Mock Data

Before deploying:

bash
1# Trigger workflow with test inputs
2curl -X POST https://workflow-engine-api.xema.dev/workflows/wf_123/dispatch \
3  -H "Authorization: Bearer $TOKEN" \
4  -d '{
5    "inputs": {
6      "project_name": "Test Project",
7      "scope": "simple"
8    }
9  }'

Monitor Executions

Watch for issues:

bash
1# Check workflow runs
2curl https://workflow-engine-api.xema.dev/workflows/wf_123/runs \
3  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {runId, status}'

Validate YAML

Check syntax before deploying:

bash
1# Validate against schema
2yamllint workflow.yaml

Summary

✅ Do:

  • Use descriptive, consistent names
  • Type all inputs with enums
  • Declare outputs explicitly
  • Handle errors gracefully
  • Set timeouts
  • Use variables for reusable values
  • Parallelize when possible
  • Protect secrets
  • Document complex workflows
  • Version workflows semantically

❌ Don't:

  • Use generic names (job1, task)
  • Accept arbitrary string inputs
  • Leave workflows without error handling
  • Let workflows hang indefinitely
  • Hardcode configuration values
  • Create sequential dependencies unnecessarily
  • Expose secrets in code
  • Document nothing
  • Change workflows without versioning

Next: Read Troubleshooting for debugging help.