Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/docs/Coding-Standards/PowerShell/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,33 @@ function Get-UserData {
}
```

### `[OutputType]` and parameter sets

When every parameter set returns the same type, one `[OutputType]` with no scoping is correct. When different parameter sets return different types, scope each `[OutputType]` to its parameter set using the `ParameterSetName` argument β€” `[OutputType]` may appear multiple times on the same function, once per type/set combination:

```powershell
[OutputType([System.String], ParameterSetName = 'By display name')]
[OutputType([System.IO.FileInfo], ParameterSetName = 'From file path')]
[CmdletBinding(DefaultParameterSetName = 'By display name')]
param(
[Parameter(Mandatory, ParameterSetName = 'By display name')]
[string] $DisplayName,

[Parameter(Mandatory, ParameterSetName = 'From file path')]
[string] $FilePath
)
```

The types listed in `[OutputType]` must match what `.OUTPUTS` documents in the comment-based help β€” they are the contract between the function and its callers.

## Parameters

- **Type every parameter** and validate at the boundary β€” `[Parameter(Mandatory)]`, `[ValidateSet(...)]`, `[ValidateNotNullOrEmpty()]` β€” so bad input is rejected early, not deep in the call stack.
- **Give every parameter a `[Parameter()]` attribute**, even when it carries no arguments β€” it is where `Mandatory`, `ValueFromPipeline`, and the rest attach, and it keeps every parameter declared the same way.
- **Attribute order**, each on its own line: `[Parameter()]`, then validation attributes, then `[ArgumentCompleter()]`, then `[Alias()]`, then the typed declaration.
- **Separate parameters with a blank line**, so each one's inline doc comment, attributes, and typed declaration read as a single block.
- **`[switch]` for boolean flags** β€” never a `[bool]` parameter.
- **Name every parameter set** with an intent-revealing name when a function has more than one mode; never `Default` or `__AllParameterSets`. Set `DefaultParameterSetName` to the most common intent.
- **Name every parameter set** with a prose phrase that states the caller's scenario β€” parameter set names appear verbatim in `Get-Help` syntax output and must read naturally to anyone calling the command. **Spaces are supported** and encouraged: `'By display name'`, `'From file path'`, `'As computer name'`, `'With credential'`, `'As session'`. Single-word PascalCase names (`ByName`, `ByPath`, `ByGuid`) are acceptable when the scenario is self-evident, but multi-word names with spaces read even better in help output. Never acceptable: `Default`, `__AllParameterSets`, `__DefaultParameterSet`, `ParameterSetA`, `Set1`, `Mode1`, or any name a caller would have to decode. `DefaultParameterSetName` on `[CmdletBinding()]` must name one of the declared parameter sets using the same prose convention and is never omitted when multiple sets exist.

## State changes and the pipeline

Expand Down