Skip to content

build(client): introduce build task naming guidelines#25730

Closed
tylerbutler wants to merge 3 commits intomicrosoft:mainfrom
tylerbutler:task-rename
Closed

build(client): introduce build task naming guidelines#25730
tylerbutler wants to merge 3 commits intomicrosoft:mainfrom
tylerbutler:task-rename

Conversation

@tylerbutler
Copy link
Member

@tylerbutler tylerbutler commented Oct 22, 2025

Task Naming Convention

Orchestrators just define dependencies - executors just execute work. While we can have arbitrary levels of orchestration, too many gets hard to reason about, and only two seem useful.

  • Workflow Orchestrators: Single word, no colons

    • build (builds everything)
    • test (runs all tests)
    • lint (runs all linters)
    • check (runs all checks) (checks don't require a build while tests do)
    • Special case: Conventional npm scripts (start, test) remain at this tier even if occasionally used as executors
  • Stage Orchestrators: Semantic category names with single colon

    • build:compile (compiles to all output formats)
    • build:api (generates API documentation)
    • test:unit (runs unit tests in all formats)
  • Tool Executors: Two naming patterns

    • Pattern A - Direct executors: tsc, eslint, jest, esnext, coverage (tool names or semantic operations)
      • Single-word: tsc, jest, esnext, coverage
      • Multi-word semantic: generate-version, api-reports-current (dash-separated)
      • Semantic variants: esnext-experimental, tsc-test-cjs (dash-separated for different purposes)
    • Pattern C - Tool variants: tsc:watch, jest:verbose (tool + mode/flag with colon separator)

Decision Framework for Task Classification

When classifying or renaming a task, follow this decision tree:

Step 1: Analyze Classification Percentage

What percentage of instances are orchestrators vs executors?

├─ >90% Orchestrator → Orchestrator classification
│   ├─ Top-level workflow? → Tier 1 (no colon): build, test, lint
│   └─ Stage coordination? → Tier 2 (single colon): build:compile, test:unit
│
├─ >90% Executor → Executor classification → Continue to Step 2
│
└─ <90% either way (MIXED) → Default to orchestrator (safer)
    Examples: start (85.7% executor), test:stress (71.4% executor)

Step 2: Determine Executor Naming Pattern

What best describes this executor's implementation?

├─ Pattern A: Direct Executor or Semantic Operation
│   Question: Is this a tool name or semantic operation?
│   Examples:
│     - Direct tool: tsc, eslint, jest, copyfiles
│     - Semantic operation: esnext, coverage, generate-version
│     - Semantic variant: api-reports-current, esnext-experimental, tsc-test-cjs
│   Naming:
│     - {tool} (single word)
│     - {semantic-operation} (single word or dash-separated multi-word)
│     - {semantic-base}-{variant} (dash for different purposes/configurations)
│   Decision: "Is this >90% executor and NOT a mode variant of a tool?"
│
│   Dash Usage in Pattern A:
│     - Multi-word semantic names: generate-version, api-reports-current
│     - Semantic variants (different purposes): esnext-experimental, tsc-test-cjs
│     - NOT for tool modes (use Pattern C instead)
│
└─ Pattern C: Tool Variant
    Question: Is this a variant (mode/flag) of an existing tool?
    Examples: tsc:watch, jest:verbose
    Naming: {tool}:{mode}
    Decision: "Does this use the same base tool with different flags/modes?"

    Colon Usage in Pattern C:
      - Tool with mode flag: tsc:watch (tsc --watch)
      - Tool with mode flag: jest:verbose (jest --verbose)
      - Separates tool name from mode/flag

Step 2.5: Dash vs Colon Decision

When to use DASH (Pattern A):

  • Multi-word semantic operation names: generate-version, api-reports-current
  • Semantic variants with different purposes: esnext-experimental (different entry point), tsc-test-cjs (different file set)
  • The variant represents a DIFFERENT PURPOSE or CONFIGURATION, not just a flag

When to use COLON (Pattern C):

  • Tool with mode/flag: tsc:watch, jest:verbose
  • The variant is the SAME TOOL with a different MODE (just adding a flag)

Examples:

  • esnext-experimental ✓ (dash) - Different entry point configuration = different purpose
  • jest:verbose ✓ (colon) - Same jest tool with --verbose flag = mode change
  • api-reports-current ✓ (dash) - Different API level configuration = different purpose
  • tsc:watch ✓ (colon) - Same tsc tool with --watch flag = mode change

Step 3: Pattern Family Consistency Check

Verify related tasks follow consistent patterns:

TypeScript family:
├─ tsc (Pattern A: Direct executor)
├─ esnext (Pattern A: Semantic executor - ESM compilation)
└─ tsc:watch (Pattern C: Tool variant - watch mode)

Jest family:
├─ jest (Pattern A: Direct executor)
├─ jest:verbose (Pattern C: Tool variant - verbose mode)
└─ jest:coverage (Pattern C: Tool variant - coverage mode)

Coverage family:
└─ coverage (Pattern A: Semantic executor - testing with coverage)
    Note: NOT test:coverage - no category prefix needed

Simplified Decision Logic

classification:
  step_1: "Calculate executor percentage"
  step_2_if_gt_90: "Executor → Choose Pattern A or C"
  step_2_if_lt_90: "Mixed → Default to orchestrator"

executor_pattern:
  question: "Is this a variant of an existing tool?"
  yes: "Pattern C ({tool}:{mode})"
  no: "Pattern A ({tool} or {operation})"

🔄 Rename Plan:
INFO:   build:esnext → esnext (154 packages)
INFO:   check:biome → biome-check (163 packages)
INFO:   format:biome → biome-format (163 packages)
INFO:   test:jest:verbose → jest:verbose (31 packages)
INFO:   api-extractor:commonjs → generate-entrypoints-commonjs (67 packages)
INFO:   api-extractor:esnext → generate-entrypoints-esnext (67 packages)
INFO:   build:api-reports:current → api-reports-current (45 packages)
INFO:   build:api-reports:legacy → api-reports-legacy (45 packages)
INFO:   build:docs → api-extractor (90 packages)
INFO:   build:test:cjs → tsc-test-cjs (74 packages)
INFO:   build:test:esm → tsc-test-esm (81 packages)
INFO:   check:are-the-types-wrong → attw (96 packages)
INFO:   check:exports:bundle-release-tags → api-extractor-exports-bundle-release-tags (88 packages)
INFO:   check:exports:cjs:legacy → api-extractor-exports-cjs-legacy (45 packages)
INFO:   check:exports:cjs:public → api-extractor-exports-cjs-public (65 packages)
INFO:   check:exports:esm:legacy → api-extractor-exports-esm-legacy (45 packages)
INFO:   check:exports:esm:public → api-extractor-exports-esm-public (65 packages)
INFO:   ci:build:api-reports:current → api-extractor-ci-api-reports-current (45 packages)
INFO:   ci:build:api-reports:legacy → api-extractor-ci-api-reports-legacy (45 packages)
INFO:   ci:build:docs → api-extractor-ci-docs (89 packages)
INFO:   format-and-build → build:format-first (8 packages)
INFO:   format-and-compile → compile:format-first (8 packages)
INFO:   test:coverage → coverage (69 packages)
INFO:   build:copy → copyfiles (9 packages)
INFO:   check:exports:cjs:alpha → api-extractor-exports-cjs-alpha (11 packages)
INFO:   check:exports:esm:alpha → api-extractor-exports-esm-alpha (11 packages)
INFO:   check:exports:cjs:index → api-extractor-exports-cjs-index (23 packages)
INFO:   check:exports:esm:index → api-extractor-exports-esm-index (23 packages)
INFO:   build:genver → generate-version (38 packages)
INFO:   build:api-reports:browser:current → api-reports-browser-current (1 packages)
INFO:   build:api-reports:browser:legacy → api-reports-browser-legacy (1 packages)
INFO:   build:exports:browser → generate-exports-browser (1 packages)
INFO:   build:exports:node → generate-exports-node (1 packages)
INFO:   build:test:jest → jest (1 packages)
INFO:   build:test:mocha:cjs → tsc-test-mocha-cjs (1 packages)
INFO:   build:test:mocha:esm → tsc-test-mocha-esm (1 packages)
INFO:   build:test:types → tsc-test-types (1 packages)
INFO:   ci:build:api-reports:browser:current → api-extractor-ci-api-reports-browser-current (1 packages)
INFO:   ci:build:api-reports:browser:legacy → api-extractor-ci-api-reports-browser-legacy (1 packages)
INFO:   ci:build:api-reports:node:current → api-extractor-ci-api-reports-node-current (1 packages)
INFO:   ci:build:api-reports:node:legacy → api-extractor-ci-api-reports-node-legacy (1 packages)
INFO:   build:test:esm:no-exactOptionalPropertyTypes → tsc-test-esm-no-exactOptionalPropertyTypes (1 packages)
INFO:   check:exports:cjs:beta → api-extractor-exports-cjs-beta (9 packages)
INFO:   check:exports:esm:beta → api-extractor-exports-esm-beta (9 packages)
INFO:   build:esnext:experimental → esnext-experimental (1 packages)
INFO:   build:esnext:main → esnext-main (1 packages)
INFO:   build:test → tsc-test (6 packages)
INFO:   check:release-tags → api-extractor-release-tags (1 packages)

@github-actions github-actions bot added area: dds Issues related to distributed data structures area: dds: propertydds area: dds: sharedstring area: dds: tree area: dev experience Improving the experience of devs building on top of fluid area: driver Driver related issues area: examples Changes that focus on our examples area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct area: loader Loader related issues area: odsp-driver area: runtime Runtime related issues area: tests Tests to add, test infrastructure improvements, etc base: main PRs targeted against main branch labels Oct 22, 2025
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave feedback here about the task names or the translation from old name to new name. I don't love the placement of the -ci in the CI tasks for example.

@github-actions
Copy link
Contributor

🔗 Found some broken links! 💔

Run a link check locally to find them. See
https://github.com/microsoft/FluidFramework/wiki/Checking-for-broken-links-in-the-documentation for more information.

linkcheck output


> fluid-framework-docs-site@0.0.0 ci:check-links /home/runner/work/FluidFramework/FluidFramework/docs
> start-server-and-test "npm run serve -- --no-open" 3000 check-links

1: starting server using command "npm run serve -- --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> fluid-framework-docs-site@0.0.0 serve
> docusaurus serve --no-open

[SUCCESS] Serving "build" directory at: http://localhost:3000/

> fluid-framework-docs-site@0.0.0 check-links
> linkcheck http://localhost:3000 --skip-file skipped-urls.txt

 ELIFECYCLE  Command failed with exit code 1.

@tylerbutler
Copy link
Member Author

I made an alternative plan that I like a little better.

Alternative Naming Scheme: Colon for Orchestrators, Dash for Executors

Comparison: Current PR vs Proposed Alternative

Summary of Approach

Current PR: Remove category prefixes from executors entirely

  • Example: build:esnextesnext
  • Problem: Loses context, namespace pollution, harder discovery

Proposed Alternative: Keep category prefixes, use dash separator for executors

  • Example: build:esnextbuild-esnext
  • Benefit: Preserves context, clear distinction, better grouping

Naming Rules

Orchestrators (use colon :):

  • Tier 1 (Workflows): build, test, lint, check — no separator
  • Tier 2 (Stages): build:compile, build:api, test:unit — single colon

Executors (use dash -):

  • Categorized executors: build-esnext, check-biome, test-coverage
  • Tool variants: jest-verbose or keep jest:verbose for mode flags
  • Multi-word: Words within a tool name use dash: generate-version, api-extractor

Rename Mapping Comparison

Current Name Current PR → Proposed Alternative → Packages
build:esnext esnext build-esnext 154
check:biome biome-check check-biome 163
format:biome biome-format format-biome 163
test:jest:verbose jest:verbose jest-verbose 31
api-extractor:commonjs generate-entrypoints-commonjs generate-entrypoints-commonjs 67
api-extractor:esnext generate-entrypoints-esnext generate-entrypoints-esnext 67
build:api-reports:current api-reports-current build-api-reports-current 45
build:api-reports:legacy api-reports-legacy build-api-reports-legacy 45
build:docs api-extractor build-docs (keep, orchestrator) 90
build:test:cjs tsc-test-cjs build-test-cjs 74
build:test:esm tsc-test-esm build-test-esm 81
check:are-the-types-wrong attw check-attw 96
check:exports:bundle-release-tags api-extractor-exports-bundle-release-tags check-exports-bundle-release-tags 88
check:exports:cjs:legacy api-extractor-exports-cjs-legacy check-exports-cjs-legacy 45
check:exports:cjs:public api-extractor-exports-cjs-public check-exports-cjs-public 65
check:exports:esm:legacy api-extractor-exports-esm-legacy check-exports-esm-legacy 45
check:exports:esm:public api-extractor-exports-esm-public check-exports-esm-public 65
ci:build:api-reports:current api-extractor-ci-api-reports-current ci-build-api-reports-current 45
ci:build:api-reports:legacy api-extractor-ci-api-reports-legacy ci-build-api-reports-legacy 45
ci:build:docs api-extractor-ci-docs ci-build-docs 89
format-and-build build:format-first build:format-first 8
format-and-compile compile:format-first compile:format-first 8
test:coverage coverage test-coverage 69
build:copy copyfiles build-copyfiles 9
check:exports:cjs:alpha api-extractor-exports-cjs-alpha check-exports-cjs-alpha 11
check:exports:esm:alpha api-extractor-exports-esm-alpha check-exports-esm-alpha 11
check:exports:cjs:index api-extractor-exports-cjs-index check-exports-cjs-index 23
check:exports:esm:index api-extractor-exports-esm-index check-exports-esm-index 23
build:genver generate-version build-generate-version 38
build:api-reports:browser:current api-reports-browser-current build-api-reports-browser-current 1
build:api-reports:browser:legacy api-reports-browser-legacy build-api-reports-browser-legacy 1
build:exports:browser generate-exports-browser build-generate-exports-browser 1
build:exports:node generate-exports-node build-generate-exports-node 1
build:test:jest jest build-test-jest or just jest 1
build:test:mocha:cjs tsc-test-mocha-cjs build-test-mocha-cjs 1
build:test:mocha:esm tsc-test-mocha-esm build-test-mocha-esm 1
build:test:types tsc-test-types build-test-types 1
ci:build:api-reports:browser:current api-extractor-ci-api-reports-browser-current ci-build-api-reports-browser-current 1
ci:build:api-reports:browser:legacy api-extractor-ci-api-reports-browser-legacy ci-build-api-reports-browser-legacy 1
ci:build:api-reports:node:current api-extractor-ci-api-reports-node-current ci-build-api-reports-node-current 1
ci:build:api-reports:node:legacy api-extractor-ci-api-reports-node-legacy ci-build-api-reports-node-legacy 1
build:test:esm:no-exactOptionalPropertyTypes tsc-test-esm-no-exactOptionalPropertyTypes build-test-esm-no-exactOptionalPropertyTypes 1
check:exports:cjs:beta api-extractor-exports-cjs-beta check-exports-cjs-beta 9
check:exports:esm:beta api-extractor-exports-esm-beta check-exports-esm-beta 9
build:esnext:experimental esnext-experimental build-esnext-experimental 1
build:esnext:main esnext-main build-esnext-main 1
build:test tsc-test build-test 6
check:release-tags api-extractor-release-tags check-release-tags 1

Key Advantages of Proposed Alternative

1. Preserves Category Context

# Current PR - loses context
npm run esnext          # Is this a build? test? check?
npm run coverage        # Related to what?

# Proposed - maintains context
npm run build-esnext    # Clearly a build executor
npm run test-coverage   # Clearly a test executor

2. Better Task Discovery

# List all build-related executors
npm run build-<tab>
  build-esnext
  build-test-cjs
  build-test-esm
  build-api-reports-current
  build-copyfiles
  build-generate-version

# List all check-related executors
npm run check-<tab>
  check-biome
  check-attw
  check-exports-cjs-legacy
  check-release-tags

3. Consistent Category-First Ordering

# Current PR - inconsistent ordering
npm run biome-check     # tool-category (reversed)
npm run esnext          # no category
npm run tsc-test-cjs    # tool-category-variant

# Proposed - always category-first
npm run check-biome           # category-tool
npm run build-esnext          # category-tool
npm run build-test-cjs        # category-subcategory-variant

4. Clear Visual Distinction

  • Colon : = Orchestrator (coordinates multiple tasks)

    • build:compile - orchestrates compilation for all formats
    • test:unit - orchestrates all unit tests
  • Dash - = Executor (runs a specific tool/operation)

    • build-esnext - executes TypeScript compilation to ESNext
    • check-biome - executes Biome linting

5. Simpler Mental Model

Orchestrator Pattern: category:subcategory
└─ Delegates to multiple executors or other orchestrators

Executor Pattern: category-tool-variant
└─ Runs a single tool or operation

6. Reduced Namespace Pollution

# Current PR - top-level namespace gets crowded
esnext, coverage, attw, copyfiles, biome-check, ...

# Proposed - top-level reserved for workflows only
build, test, lint, check, start
└─ Everything else is categorized with prefix

7. Easier Migration

# Most renames are simple character substitution
sed 's/build:esnext/build-esnext/g'
sed 's/check:biome/check-biome/g'
sed 's/test:coverage/test-coverage/g'

# Not structural reorganization like:
build:esnext → esnext (remove category entirely)
check:biome → biome-check (reverse order)

Special Cases & Decisions

Multi-level tasks (3+ colons)

Current: build:test:mocha:cjstsc-test-mocha-cjs
Alternative Option 1: build-test-mocha-cjs (fully executor, all dashes)
Alternative Option 2: build:test-mocha-cjs (build:test is orchestrator, -mocha-cjs is executor variant)

Recommendation: Use Option 1 (all dashes) for consistency - these are all executors.

Tool variants (modes/flags)

Current: test:jest:verbosejest:verbose
Alternative Option 1: jest-verbose (fully executor)
Alternative Option 2: jest:verbose (keep colon for mode flags)

Recommendation: Use Option 2 (jest:verbose) to distinguish tool variants from different tools.

Standalone tools (no ambiguity)

Tasks like: tsc, eslint, jest (when used alone)
Recommendation: Keep as-is. No category prefix needed when unambiguous.

Tasks that are actually orchestrators

Example: build:docs → current PR suggests api-extractor

This is actually an orchestrator (coordinates doc generation), not an executor!
Recommendation: Keep as build:docs (orchestrator with colon).


Implementation Notes

  1. Automated migration: Most renames can be done with regex find/replace
  2. Gradual rollout: Can migrate category-by-category (build first, then test, then check)
  3. Backward compat: Can add aliases during transition period
  4. Documentation: Update build docs with clear orchestrator vs executor distinction

Questions for Discussion

  1. Tool variants: Should jest:verbose keep colon or use jest-verbose?

    • Colon emphasizes it's the same tool with different mode
    • Dash would be fully consistent with executor pattern
  2. CI tasks: Should ci:build:* become ci-build-* or is ci: a valid orchestrator tier?

    • Proposed: ci-build-* since CI tasks are executors
  3. Multi-word tools: Keep dash within tool names (api-extractor, generate-version)?

    • Proposed: Yes, dash for word separation is conventional
  4. Length concerns: Some names get long (build-api-reports-browser-current)

    • But they're descriptive and group well
    • Can use aliases for common tasks

@CraigMacomber
Copy link
Contributor

I didn't look at all the details, but I think "esnext" is a very poor way to say ESM, since esnext typically refers to an upcoming standard and thus involves non standardized APIs and such. The esm build is the one actually using standard modules, and has been incorrect to call esnext since ES6 was standardized in 2015 (10 years ago!). If we want to do renames, that should be fixed.

build:test:mocha:esm already uses "esm": the build should align with that.

@tylerbutler
Copy link
Member Author

I didn't look at all the details, but I think "esnext" is a very poor way to say ESM, since esnext typically refers to an upcoming standard and thus involves non standardized APIs and such. The esm build is the one actually using standard modules, and has been incorrect to call esnext since ES6 was standardized in 2015 (10 years ago!). If we want to do renames, that should be fixed.

build:test:mocha:esm already uses "esm": the build should align with that.

Agreed. The esnext name hasn't been accurate for a long time.

@alexvy86
Copy link
Contributor

alexvy86 commented Nov 3, 2025

Orchestrators just define dependencies

That sounds to me like all orchestrator scripts in package.json should be script: false? Some of them still have script: true (e.g. "build:readme" and "build:manifest"). I do like the idea that orchestrators should not actually execute anything though.

@microsoft-github-policy-service
Copy link
Contributor

This PR has been automatically marked as stale because it has had no activity for 60 days. It will be closed if no further activity occurs within 8 days of this comment. Thank you for your contributions to Fluid Framework!

@tylerbutler tylerbutler deleted the task-rename branch January 12, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: dds: propertydds area: dds: sharedstring area: dds: tree area: dds Issues related to distributed data structures area: dev experience Improving the experience of devs building on top of fluid area: driver Driver related issues area: examples Changes that focus on our examples area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct area: loader Loader related issues area: odsp-driver area: runtime Runtime related issues area: tests Tests to add, test infrastructure improvements, etc base: main PRs targeted against main branch status: stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants