You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While investigating Rush performance telemetry, I noticed that the tracing infrastructure captures timing data starting after the CLI bootstrap phase. I'd like to understand if this is intentional and discuss whether capturing earlier timing would be valuable.
This is a discussion/question rather than a bug report.
Current Tracing Architecture
Entry Points
Rush has two distinct entry points that are often confused:
File
Purpose
When Used
apps/rush/src/start.ts
CLI frontend
Normal rush / rushx / rush-pnpm commands
libraries/rush-lib/src/start.ts
Direct library usage
Programmatic API consumers (isManaged: false)
Where Tracing Currently Starts
The existing performance.mark('rush:start') is located at:
libraries/rush-lib/src/start.ts:6
This file is on the direct library usage path, not the CLI path. The CLI path is:
apps/rush/bin/rush # Line 2: require('../lib-commonjs/start.js')
→ apps/rush/src/start.ts # 105 lines of bootstrap code
→ RushCommandSelector.execute() # apps/rush/src/RushCommandSelector.ts:28
→ Rush.launch() # libraries/rush-lib/src/api/Rush.ts:79
→ measureAsyncFn('rush:parser:executeAsync', ...) # Line 99 - first traced span
The first performance measurement on the CLI path is rush:parser:executeAsync at libraries/rush-lib/src/api/Rush.ts:99.
Existing Performance Instrumentation
For reference, here are the existing measureAsyncFn / measureFn calls in rush-lib:
Location
Measurement Name
What It Measures
Rush.ts:99
rush:parser:executeAsync
Parser execution (first span)
RushCommandLineParser.ts:242
rush:initializeUnassociatedPlugins
Plugin init
RushCommandLineParser.ts:323
rush:commandLineParser:onExecuteAsync
Command execution
BaseRushAction.ts:81
rush:<action>:runAsync
Action body
BaseRushAction.ts:127-134
rush:<action>:initializePluginsAsync
Per-action plugin init
PhasedScriptAction.ts:354+
rush:phased:*
~20 internal spans
BaseInstallAction.ts:289-333
rush:installManager:*
Install operations
GlobalScriptAction.ts:187
rush:globalScriptAction:prepareAutoinstaller
Autoinstaller prep
Observations
Bootstrap Code Outside Current Tracing
The following code executes before the first performance measurement:
MinimalRushConfiguration.loadFromDefaultLocation() - finds and parses rush.json
apps/rush/src/start.ts:40
1
Load own package.json
apps/rush/src/start.ts:44-84
40
Preview version handling, version selection logic
apps/rush/src/start.ts:89-91
3
Create terminal provider, build launch options
apps/rush/src/start.ts:95-104
10
Version selector or direct launch
Rush.ts:80-96
16
Banner, migration advisor, parser construction
Total
~96
Additionally, RushCommandLineParser constructor (RushCommandLineParser.ts:99-180) performs work before executeAsync() is called:
Line 134-144: Find rush.json, load .env, load full RushConfiguration
Line 149-153: Node.js compatibility checks
Line 155: Create RushGlobalFolder
Line 157-168: Create RushSession, PluginManager
Line 170-171: Read plugin command-line configurations
Line 180: Populate all actions
Timing Comparison
For a hypothetical rush build command with 50ms of actual build work:
Metric
What It Measures
Example Value
durationInSeconds (telemetry)
Stopwatch started inside action
0.05s
rush:parser:executeAsync (perf entry)
From parser.executeAsync() call
0.08s
Wall-clock time
From bin stub to exit
0.35s
The difference (~270ms in this example) represents startup overhead from module loading, config parsing, and plugin initialization.
Existing Mark Location
There is a performance.mark('rush:start') at libraries/rush-lib/src/start.ts:6, added in commit 25ecb0bc2f (#5269, July 2025). This mark fires when using rush-lib as a direct module import (the isManaged: false path), which is separate from the CLI path. I'm curious whether this placement was intentional for API consumers, or if it was meant to cover the CLI path as well.
Possible Approaches (If We Want Earlier Tracing)
Option 1: Add mark to CLI start.ts (1 line change)
Add a performance mark at the top of apps/rush/src/start.ts:
// apps/rush/src/start.ts - line 3 (after copyright)performance.mark('rush:cli:start');
Pros:
Minimal change (1 line)
Captures most of the bootstrap phase
Mark's startTime property shows ms since Node process start
Existing collectPerformanceEntries() automatically captures it
Cons:
Misses bin stub + initial require (~5ms typically)
Import still happens after the mark, so module load time for start.ts itself is captured but not the mark statement's own overhead
Questions
Is the current tracing scope intentional? Perhaps measuring from parser.executeAsync() is the intended design - focusing on command execution rather than CLI bootstrap. I'd like to understand the original intent.
Is there value in capturing startup overhead? For users investigating "why does rush build feel slow?", knowing the bootstrap cost could be helpful. But maybe that's out of scope for the telemetry system.
Should bin stubs remain logic-free? The current 2-line stubs are intentionally minimal. If earlier tracing is desirable, is adding a performance mark acceptable there?
What about the version selector path? When apps/rush/src/RushVersionSelector.ts downloads and launches a different Rush version, the timing story gets more complex. Would we want to trace across that boundary?
Separate metrics or combined? If we add earlier tracing:
Keep durationInSeconds as action-only time, add totalDurationInSeconds separately
Or add both and let consumers choose what matters to them
Details
Related Code References
Bin stubs: apps/rush/bin/rush, apps/rush/bin/rushx, apps/rush/bin/rush-pnpm
Summary
While investigating Rush performance telemetry, I noticed that the tracing infrastructure captures timing data starting after the CLI bootstrap phase. I'd like to understand if this is intentional and discuss whether capturing earlier timing would be valuable.
This is a discussion/question rather than a bug report.
Current Tracing Architecture
Entry Points
Rush has two distinct entry points that are often confused:
apps/rush/src/start.tsrush/rushx/rush-pnpmcommandslibraries/rush-lib/src/start.tsisManaged: false)Where Tracing Currently Starts
The existing
performance.mark('rush:start')is located at:This file is on the direct library usage path, not the CLI path. The CLI path is:
The first performance measurement on the CLI path is
rush:parser:executeAsyncatlibraries/rush-lib/src/api/Rush.ts:99.Existing Performance Instrumentation
For reference, here are the existing
measureAsyncFn/measureFncalls in rush-lib:Rush.ts:99rush:parser:executeAsyncRushCommandLineParser.ts:242rush:initializeUnassociatedPluginsRushCommandLineParser.ts:323rush:commandLineParser:onExecuteAsyncBaseRushAction.ts:81rush:<action>:runAsyncBaseRushAction.ts:127-134rush:<action>:initializePluginsAsyncPhasedScriptAction.ts:354+rush:phased:*BaseInstallAction.ts:289-333rush:installManager:*GlobalScriptAction.ts:187rush:globalScriptAction:prepareAutoinstallerObservations
Bootstrap Code Outside Current Tracing
The following code executes before the first performance measurement:
apps/rush/src/start.ts:9-15apps/rush/src/start.ts:17-20apps/rush/src/start.ts:22-34apps/rush/src/start.ts:37-38MinimalRushConfiguration.loadFromDefaultLocation()- finds and parses rush.jsonapps/rush/src/start.ts:40apps/rush/src/start.ts:44-84apps/rush/src/start.ts:89-91apps/rush/src/start.ts:95-104Rush.ts:80-96Additionally,
RushCommandLineParserconstructor (RushCommandLineParser.ts:99-180) performs work beforeexecuteAsync()is called:Timing Comparison
For a hypothetical
rush buildcommand with 50ms of actual build work:durationInSeconds(telemetry)rush:parser:executeAsync(perf entry)The difference (~270ms in this example) represents startup overhead from module loading, config parsing, and plugin initialization.
Existing Mark Location
There is a
performance.mark('rush:start')atlibraries/rush-lib/src/start.ts:6, added in commit25ecb0bc2f(#5269, July 2025). This mark fires when using rush-lib as a direct module import (theisManaged: falsepath), which is separate from the CLI path. I'm curious whether this placement was intentional for API consumers, or if it was meant to cover the CLI path as well.Possible Approaches (If We Want Earlier Tracing)
Option 1: Add mark to CLI start.ts (1 line change)
Add a performance mark at the top of
apps/rush/src/start.ts:Pros:
startTimeproperty shows ms since Node process startcollectPerformanceEntries()automatically captures itCons:
Option 2: Add mark to bin stubs (3-4 line change)
Add marks at the earliest possible point:
Optionally add a measure in rush-lib to compute the span:
Pros:
Cons:
Option 3: Environment variable timestamp (9 lines)
Set timestamp in bin stub, read in telemetry:
Pros:
env | grep RUSH)RUSH_*environment variablesCons:
Option 4: Wrap entire CLI in measurement
Create a new traced entry point that wraps the existing start.ts:
Pros:
Cons:
Questions
Is the current tracing scope intentional? Perhaps measuring from
parser.executeAsync()is the intended design - focusing on command execution rather than CLI bootstrap. I'd like to understand the original intent.Is there value in capturing startup overhead? For users investigating "why does
rush buildfeel slow?", knowing the bootstrap cost could be helpful. But maybe that's out of scope for the telemetry system.Should bin stubs remain logic-free? The current 2-line stubs are intentionally minimal. If earlier tracing is desirable, is adding a performance mark acceptable there?
What about the version selector path? When
apps/rush/src/RushVersionSelector.tsdownloads and launches a different Rush version, the timing story gets more complex. Would we want to trace across that boundary?Separate metrics or combined? If we add earlier tracing:
durationInSecondsas action-only time, addtotalDurationInSecondsseparatelyDetails
Related Code References
apps/rush/bin/rush,apps/rush/bin/rushx,apps/rush/bin/rush-pnpmapps/rush/src/start.tslibraries/rush-lib/src/start.tslibraries/rush-lib/src/api/Rush.ts:79-100libraries/rush-lib/src/cli/RushCommandLineParser.ts:99-180libraries/rush-lib/src/utilities/performance.tslibraries/rush-lib/src/logic/Telemetry.ts:172-17325ecb0bc2f- [rush] Enhance performance data in telemetry payload ([rush] Enhance performance data in telemetry payload #5269)Standard questions
@microsoft/rushglobally installed version?rushVersionfrom rush.json?node -v)?