Skip to content

Commit bf3fa1a

Browse files
Add pipeline output and output mode switch rules
Documented the critical rule to emit objects to the pipeline immediately instead of collecting them in arrays or ArrayLists. Added guidance against using -Detailed/-Simple output mode switches and updated checklists and golden rules to reflect these best practices for memory efficiency, user experience, and pipeline compatibility.
1 parent bee449a commit bf3fa1a

1 file changed

Lines changed: 134 additions & 7 deletions

File tree

CLAUDE.md

Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,129 @@ $result = Invoke-DbaQuery @splatQuery -SqlInstance $server
405405
- **Always prefer parameterized queries** when using T-SQL with dynamic values
406406
- **Document your choice** when T-SQL is used instead of SMO for non-obvious reasons
407407

408+
### PIPELINE OUTPUT - EMIT OBJECTS IMMEDIATELY
409+
410+
**CRITICAL RULE**: NEVER collect objects in an ArrayList or array and output them at the end. Output objects to the pipeline immediately as they are created.
411+
412+
**Why Immediate Output:**
413+
- **Memory Efficiency**: Objects are released to the pipeline immediately, not held in memory
414+
- **User Experience**: Users see results streaming in real-time, not waiting until the end
415+
- **Pipeline Compatibility**: Enables proper pipeline chaining and early termination (Ctrl+C)
416+
- **Error Resilience**: Partial results are available even if the command fails partway through
417+
418+
**CORRECT - Output immediately:**
419+
420+
```powershell
421+
foreach ($instance in $SqlInstance) {
422+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential
423+
424+
foreach ($db in $server.Databases) {
425+
# Output each object immediately to the pipeline
426+
[PSCustomObject]@{
427+
ComputerName = $server.ComputerName
428+
InstanceName = $server.ServiceName
429+
SqlInstance = $server.DomainInstanceName
430+
Database = $db.Name
431+
Size = $db.Size
432+
}
433+
}
434+
}
435+
```
436+
437+
**WRONG - Collecting in ArrayList (OLD PATTERN - DO NOT USE):**
438+
439+
```powershell
440+
# WRONG - This is an outdated anti-pattern
441+
$results = New-Object System.Collections.ArrayList
442+
443+
foreach ($instance in $SqlInstance) {
444+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential
445+
446+
foreach ($db in $server.Databases) {
447+
$null = $results.Add([PSCustomObject]@{
448+
ComputerName = $server.ComputerName
449+
Database = $db.Name
450+
})
451+
}
452+
}
453+
454+
# WRONG - Holding everything until the end
455+
$results
456+
```
457+
458+
**WRONG - Using += with arrays (even worse performance):**
459+
460+
```powershell
461+
# WRONG - Array concatenation is extremely slow
462+
$results = @()
463+
464+
foreach ($db in $databases) {
465+
$results += [PSCustomObject]@{
466+
Name = $db.Name
467+
}
468+
}
469+
470+
$results
471+
```
472+
473+
**NO -Detailed or -Simple Parameters:**
474+
475+
Do NOT create `-Detailed` or `-Simple` switch parameters that change the output object structure. This is an outdated pattern that creates confusion and breaks pipeline expectations.
476+
477+
```powershell
478+
# WRONG - Do not create output mode switches
479+
param(
480+
[switch]$Detailed,
481+
[switch]$Simple
482+
)
483+
484+
if ($Detailed) {
485+
# Return more properties
486+
} elseif ($Simple) {
487+
# Return fewer properties
488+
}
489+
```
490+
491+
Instead, return a consistent object with all relevant properties. Users can select the properties they want with `Select-Object`.
492+
493+
**Proper Pattern for Process Block:**
494+
495+
When using `ValueFromPipeline`, output in the `process` block, not `end`:
496+
497+
```powershell
498+
function Get-DbaExample {
499+
[CmdletBinding()]
500+
param (
501+
[Parameter(ValueFromPipeline)]
502+
[DbaInstanceParameter[]]$SqlInstance,
503+
[PSCredential]$SqlCredential
504+
)
505+
506+
process {
507+
foreach ($instance in $SqlInstance) {
508+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential
509+
510+
foreach ($item in $server.SomeCollection) {
511+
# Output immediately in process block
512+
[PSCustomObject]@{
513+
ComputerName = $server.ComputerName
514+
InstanceName = $server.ServiceName
515+
SqlInstance = $server.DomainInstanceName
516+
Name = $item.Name
517+
}
518+
}
519+
}
520+
}
521+
}
522+
```
523+
524+
**Summary:**
525+
- Output objects immediately as they are created
526+
- Never use ArrayList, Generic.List, or array += to collect results
527+
- Never use `-Detailed`/`-Simple` output mode switches
528+
- Process pipeline input in the `process` block, not `end`
529+
- Let PowerShell's pipeline handle the collection if the user needs it
530+
408531
### SPLAT USAGE REQUIREMENT
409532

410533
**USE SPLATS ONLY FOR 3+ PARAMETERS**
@@ -1018,6 +1141,8 @@ This allows you to manually test commands against actual SQL Server instances be
10181141
**dbatools Patterns:**
10191142
- [ ] SMO used first for object manipulation, scripting, and property access
10201143
- [ ] T-SQL only used when appropriate (system views, DMVs, stored procedures, performance, version-specific)
1144+
- [ ] Pipeline output emitted immediately (no ArrayList/array collection)
1145+
- [ ] No `-Detailed`/`-Simple` output mode switches
10211146
- [ ] EnableException handling correctly implemented
10221147
- [ ] Parameter validation follows dbatools pattern
10231148
- [ ] Where-Object conversions applied appropriately
@@ -1049,10 +1174,12 @@ The golden rules for dbatools code:
10491174
2. **NEVER use `= $true` in parameter attributes** - Use modern syntax: `[Parameter(Mandatory)]` not `[Parameter(Mandatory = $true)]`
10501175
3. **NEVER use `::new()` syntax** - Use `New-Object` for PowerShell v3 compatibility
10511176
4. **NEVER be dismissive about SQL Server versions** - Support SQL 2000 when feasible, skip gracefully when not
1052-
5. **ALWAYS prefer SMO first** - Use T-SQL only when SMO doesn't provide functionality or for better performance/UX
1053-
6. **ALWAYS align hashtables** - Equals signs must line up vertically
1054-
7. **ALWAYS preserve comments** - Every comment stays exactly as written
1055-
8. **ALWAYS use double quotes** - SQL Server module standard
1056-
9. **ALWAYS use unique variable names** - Prevent scope collisions
1057-
10. **ALWAYS use descriptive splatnames** - `$splatConnection`, not `$splat`
1058-
11. **ALWAYS register new commands** - Add to both dbatools.psd1 and dbatools.psm1
1177+
5. **NEVER collect pipeline output** - Emit objects immediately, never use ArrayList/array to hold results until the end
1178+
6. **NEVER use `-Detailed`/`-Simple` switches** - Return consistent objects, let users use `Select-Object`
1179+
7. **ALWAYS prefer SMO first** - Use T-SQL only when SMO doesn't provide functionality or for better performance/UX
1180+
8. **ALWAYS align hashtables** - Equals signs must line up vertically
1181+
9. **ALWAYS preserve comments** - Every comment stays exactly as written
1182+
10. **ALWAYS use double quotes** - SQL Server module standard
1183+
11. **ALWAYS use unique variable names** - Prevent scope collisions
1184+
12. **ALWAYS use descriptive splatnames** - `$splatConnection`, not `$splat`
1185+
13. **ALWAYS register new commands** - Add to both dbatools.psd1 and dbatools.psm1

0 commit comments

Comments
 (0)