Skip to content

Code layout#

Brace style (OTBS)#

Practice: Opening braces on the same line as the statement. Closing braces on their own line. Always use braces, even for single-statement blocks.

Why: Consistent braces make diffs single-line and prevent the PowerShell parser quirk where a newline before { silently breaks constructs. Adding a statement later cannot introduce a logic bug when braces are always present (Make change easy).

How:

# Good
function Get-Example {
    if ($Name) {
        Write-Output "Hello, $Name"
    } else {
        Write-Output 'Hello, World'
    }
}
# Bad
function Get-Example
{                                          # Brace must be on same line
}

if ($condition)
    Write-Output 'Missing braces'          # Always use braces

Indentation and whitespace#

Practice: 4-space indentation (no tabs). One space around operators and after commas. No trailing whitespace. One blank line between logical blocks. Files end with a single newline.

Why: Reviewers — human and agent — should focus on behaviour, not formatting (4-eyes principle). Mechanical consistency removes noise from diffs, keeping structural and behavioural changes visibly separate (Make change easy).

How:

# Good
$value = Get-Something -Name 'Test'

foreach ($item in $Items) {
    $processed = Format-Item $item
}
# Bad
$value=Get-Something -Name 'Test'
foreach($item in $Items){
$processed=Format-Item $item}

Line length#

Practice: Wrap at 115 characters. Prefer splatting or natural line breaks (after commas, pipes) over backtick continuations.

Why: Backtick continuation is fragile — a trailing space silently breaks it and the bug is invisible in most editors. Splatting achieves the same result deterministically (Determinism before intelligence) and diffs cleanly (Make change easy).

How:

# Good — splatting
$params = @{
    Path        = 'C:\Temp'
    Filter      = '*.txt'
    Recurse     = $true
    ErrorAction = 'Stop'
}
Get-ChildItem @params

# Good — natural break after pipe
Get-ChildItem -Path $root |
    Where-Object { $_.Length -gt 1MB }
# Bad — backtick continuation
Get-ChildItem -Path 'C:\Temp' `
    -Filter '*.txt' `
    -Recurse

Avoid semicolons as line terminators#

Practice: Do not use ; to terminate lines or separate statements within a line. One statement per line.

Why: Semicolons are unnecessary in PowerShell and produce noisy edits when someone later removes them. One statement per line makes diffs atomic and readable (Make change easy).

How:

# Good
$name = 'John'
$age = 30

$hashtable = @{
    Name = 'John'
    Age  = 30
}
# Bad
$name = 'John'; $age = 30

$hashtable = @{ Name = 'John'; Age = 30 }

Regions#

Practice: Use #region/#endregion to mark logical sections. Label every region.

Why: Regions create named, collapsible blocks in VS Code without premature file-splitting (DRY — with judgment). Labels double as in-file documentation (Documentation lives close to the thing it documents).

How:

# Good
#region Input validation
if (-not (Test-Path $ConfigPath)) {
    throw "Config not found: $ConfigPath"
}
#endregion Input validation

#region Helper functions
function Get-ConfigData { }
function New-UserAccount { }
#endregion Helper functions
# Bad
#region
function Get-ConfigData { }   # Unlabelled — useless for navigation
#endregion

Lowercase keywords#

Practice: All PowerShell keywords must be lowercase: function, filter, param, begin, process, end, if, else, elseif, switch, foreach, for, while, do, return, throw, try, catch, finally.

Why: Consistent casing for language keywords separates them visually from PascalCase user-defined names. Lowercase keywords are the community convention enforced by formatters and linters (Clean Code).

How:

# Good
function Get-Item {
    param()
    begin { }
    process { }
    end { }
}
# Bad
Function Get-Item {
    Param()
    Begin { }
    Process { }
    End { }
}

No ternary operators#

Practice: Do not use ternary operators (condition ? a : b). Use if/else instead.

Why: Ternary operators are not available in PowerShell 5.1. Using if/else maintains cross-version compatibility (Build for all developers).

How:

# Good
if ($condition) {
    $value = 'yes'
} else {
    $value = 'no'
}
# Bad
$value = $condition ? 'yes' : 'no'   # Breaks on PowerShell 5.1