Skip to content

Commit f1ed5db

Browse files
Add dbatools PowerShell style guide for Claude
Introduces CLAUDE.md containing coding standards for dbatools PowerShell development. The guide covers critical syntax rules, comment preservation, string and hashtable formatting, variable naming, array formatting, Where-Object usage, resource management, and dbatools-specific conventions to ensure code consistency and maintainability.
1 parent 2ee8e10 commit f1ed5db

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed

CLAUDE.md

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
# dbatools PowerShell Style Guide for Claude Code
2+
3+
This style guide provides coding standards for dbatools PowerShell development to ensure consistency, readability, and maintainability across the project.
4+
5+
## CRITICAL COMMAND SYNTAX RULES
6+
7+
### NO BACKTICKS - ALWAYS USE SPLATS
8+
9+
**ABSOLUTE RULE**: NEVER suggest or use backticks (`) for line continuation. Backticks are an anti-pattern in modern PowerShell development.
10+
11+
### PARAMETER ATTRIBUTES - NO `= $true` SYNTAX
12+
13+
**MODERN RULE**: Do NOT use `Mandatory = $true` or similar boolean attribute assignments. Boolean attributes do not require explicit value assignment in modern PowerShell.
14+
15+
```powershell
16+
# CORRECT - Modern attribute syntax (no = $true)
17+
param(
18+
[Parameter(Mandatory)]
19+
[string]$SqlInstance,
20+
21+
[Parameter(ValueFromPipeline)]
22+
[object[]]$InputObject,
23+
24+
[switch]$EnableException
25+
)
26+
27+
# WRONG - Outdated PSv2 syntax (no longer needed)
28+
param(
29+
[Parameter(Mandatory = $true)]
30+
[string]$SqlInstance,
31+
32+
[Parameter(ValueFromPipeline = $true)]
33+
[object[]]$InputObject
34+
)
35+
```
36+
37+
**Guidelines:**
38+
- Use `[Parameter(Mandatory)]` not `[Parameter(Mandatory = $true)]`
39+
- Use `[switch]` for boolean flags, not `[bool]` parameters
40+
- Keep non-boolean attributes with values: `[Parameter(ValueFromPipelineByPropertyName = "Name")]`
41+
42+
### SPLAT USAGE REQUIREMENT
43+
44+
**USE SPLATS ONLY FOR 3+ PARAMETERS**
45+
46+
- **1-2 parameters**: Use direct parameter syntax
47+
- **3+ parameters**: Use splatted hashtables with `$splat<Purpose>` naming
48+
49+
```powershell
50+
# CORRECT - 2 parameters, direct syntax
51+
$database = Get-DbaDatabase -SqlInstance $instance -Name "master"
52+
53+
# CORRECT - 5 parameters, must use splat
54+
$splatConnection = @{
55+
SqlInstance = $instance
56+
SqlCredential = $TestConfig.SqlCredential
57+
Database = $dbName
58+
EnableException = $true
59+
Confirm = $false
60+
}
61+
$result = New-DbaDatabase @splatConnection
62+
63+
# WRONG - 3+ parameters without splat
64+
Get-DbaDatabase -SqlInstance $instance -Database $db -EnableException $true -WarningAction SilentlyContinue
65+
66+
# WRONG - Using backticks for continuation
67+
Get-DbaDatabase -SqlInstance $instance `
68+
-Database $db `
69+
-EnableException $true
70+
71+
# WRONG - Generic $splat without purpose
72+
$splat = @{
73+
SqlInstance = $instance
74+
Database = $db
75+
Confirm = $false
76+
}
77+
```
78+
79+
## COMMENT PRESERVATION REQUIREMENT
80+
81+
**ABSOLUTE MANDATE**: ALL COMMENTS MUST BE PRESERVED EXACTLY as they appear in the original code. This includes:
82+
- Development notes and temporary comments
83+
- End-of-file comments
84+
- CI/CD system comments (especially AppVeyor)
85+
- Seemingly unrelated comments
86+
- Any comment that appears to be a note or reminder
87+
- Do not delete anything that says `#$TestConfig.instance...` or similar metadata
88+
89+
**NO EXCEPTIONS** - Every single comment must remain intact in its original location and format.
90+
91+
## STRING AND QUOTE STANDARDS
92+
93+
- **Always use double quotes** for strings (SQL Server module standard)
94+
- Properly escape quotes when needed
95+
- Convert all single quotes to double quotes for string literals
96+
- Remove unnecessary quotes from parameter values
97+
98+
```powershell
99+
# CORRECT
100+
$database = "master"
101+
$query = "SELECT * FROM sys.databases"
102+
$message = "Database `"$dbName`" created successfully"
103+
104+
# WRONG
105+
$database = 'master'
106+
$query = 'SELECT * FROM sys.databases'
107+
```
108+
109+
## HASHTABLE ALIGNMENT (MANDATORY)
110+
111+
**CRITICAL FORMATTING REQUIREMENT**: ALL hashtable assignments must be perfectly aligned using spaces:
112+
113+
```powershell
114+
# REQUIRED FORMAT - Aligned = signs
115+
$splatConnection = @{
116+
SqlInstance = $TestConfig.instance2
117+
SqlCredential = $TestConfig.SqlCredential
118+
Database = $dbName
119+
EnableException = $true
120+
Confirm = $false
121+
}
122+
123+
# FORBIDDEN - Misaligned hashtables
124+
$splat = @{
125+
SqlInstance = $instance
126+
Database = $db
127+
EnableException = $true
128+
}
129+
```
130+
131+
The equals signs must line up vertically to create clean, professional-looking code.
132+
133+
## VARIABLE NAMING CONVENTIONS
134+
135+
- Use `$splat<Purpose>` for 3+ parameters (never plain `$splat`)
136+
- Use direct parameters for 1-2 parameters
137+
- Create unique variable names across all scopes to prevent collisions
138+
- Be descriptive with variable names to indicate their purpose
139+
140+
```powershell
141+
# GOOD - descriptive splat names with aligned formatting
142+
$splatPrimary = @{
143+
Primary = $TestConfig.instance3
144+
Name = $primaryAgName
145+
ClusterType = "None"
146+
FailoverMode = "Manual"
147+
Certificate = "dbatoolsci_AGCert"
148+
Confirm = $false
149+
}
150+
151+
$splatReplica = @{
152+
Secondary = $TestConfig.instance2
153+
Name = $replicaAgName
154+
ClusterType = "None"
155+
Confirm = $false
156+
}
157+
158+
# Direct parameters for 1-2 parameters
159+
$ag = Get-DbaLogin -SqlInstance $instance -Login $loginName
160+
161+
# WRONG - Generic splat name
162+
$splat = @{
163+
Primary = $TestConfig.instance3
164+
Name = $agName
165+
}
166+
```
167+
168+
### Unique Names Across Scopes
169+
170+
Use unique, descriptive variable names across scopes to avoid collisions:
171+
172+
```powershell
173+
Describe $CommandName {
174+
BeforeAll {
175+
$primaryInstanceName = "instance3"
176+
$splatPrimary = @{
177+
SqlInstance = $primaryInstanceName
178+
Database = "testdb"
179+
}
180+
}
181+
182+
Context "Specific scenario" {
183+
BeforeAll {
184+
# Different variable name - not $primaryInstanceName again
185+
$secondaryInstanceName = "instance2"
186+
$splatSecondary = @{
187+
SqlInstance = $secondaryInstanceName
188+
Database = "testdb"
189+
}
190+
}
191+
}
192+
}
193+
```
194+
195+
## ARRAY FORMATTING
196+
197+
Multi-line arrays must be formatted consistently:
198+
199+
```powershell
200+
$expectedParameters = @(
201+
"SqlInstance",
202+
"SqlCredential",
203+
"Database",
204+
"EnableException"
205+
)
206+
207+
# Multi-line hashtable arrays
208+
$instances = @(
209+
@{
210+
Name = "instance1"
211+
Version = "2019"
212+
},
213+
@{
214+
Name = "instance2"
215+
Version = "2022"
216+
}
217+
)
218+
```
219+
220+
## WHERE-OBJECT USAGE
221+
222+
Prefer direct property comparison for simple filters:
223+
224+
```powershell
225+
# Preferred - direct property comparison
226+
$master = $databases | Where-Object Name -eq "master"
227+
$systemDbs = $databases | Where-Object Name -in "master", "model", "msdb", "tempdb"
228+
229+
# Required - script block for complex filtering only
230+
$hasParameters = (Get-Command $CommandName).Parameters.Values.Name | Where-Object { $PSItem -notin ("WhatIf", "Confirm") }
231+
```
232+
233+
## FORMATTING RULES
234+
235+
- Apply OTBS (One True Brace Style) formatting to all code blocks
236+
- No trailing spaces anywhere
237+
- Use `$results.Status.Count` for accurate counting in dbatools context
238+
- Preserve all original parameter names exactly as written
239+
- 4-space indentation for consistency
240+
241+
```powershell
242+
# CORRECT - OTBS style
243+
if ($condition) {
244+
$result = Get-DbaDatabase -SqlInstance $instance
245+
} else {
246+
$result = $null
247+
}
248+
249+
# CORRECT - Foreach with OTBS
250+
foreach ($instance in $instances) {
251+
$splatQuery = @{
252+
SqlInstance = $instance
253+
Query = "SELECT @@VERSION"
254+
}
255+
$null = Invoke-DbaQuery @splatQuery
256+
}
257+
```
258+
259+
## TEMPORARY FILES AND RESOURCE MANAGEMENT
260+
261+
- Create temporary test files/directories with unique names using `Get-Random`
262+
- Always clean up temporary resources with `-ErrorAction SilentlyContinue`
263+
- Track all resources created for cleanup
264+
265+
```powershell
266+
BeforeAll {
267+
# Create unique temp path for this test run
268+
$backupPath = "$($TestConfig.Temp)\$CommandName-$(Get-Random)"
269+
$null = New-Item -Path $backupPath -ItemType Directory
270+
$filesToRemove = @()
271+
}
272+
273+
AfterAll {
274+
# Always clean up temp files
275+
Remove-Item -Path $backupPath -Recurse -ErrorAction SilentlyContinue
276+
Remove-Item -Path $filesToRemove -ErrorAction SilentlyContinue
277+
}
278+
```
279+
280+
## DBATOOLS-SPECIFIC CONVENTIONS
281+
282+
### Parameter Validation Pattern
283+
284+
```powershell
285+
Context "Parameter validation" {
286+
It "Should have the expected parameters" {
287+
$hasParameters = (Get-Command $CommandName).Parameters.Values.Name | Where-Object { $PSItem -notin ("WhatIf", "Confirm") }
288+
$expectedParameters = @(
289+
"SqlInstance",
290+
"SqlCredential",
291+
"Database",
292+
"EnableException"
293+
)
294+
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty
295+
}
296+
}
297+
```
298+
299+
### EnableException Handling
300+
301+
For integration tests, use EnableException to ensure test setup/cleanup failures are detected:
302+
303+
```powershell
304+
BeforeAll {
305+
# Set EnableException for setup to catch failures
306+
$PSDefaultParameterValues['*-Dba*:EnableException'] = $true
307+
308+
# Perform setup operations
309+
$null = New-DbaDatabase -SqlInstance $instance -Name $testDb
310+
311+
# Remove EnableException for actual test execution
312+
$PSDefaultParameterValues.Remove('*-Dba*:EnableException')
313+
}
314+
315+
AfterAll {
316+
# Re-enable for cleanup
317+
$PSDefaultParameterValues['*-Dba*:EnableException'] = $true
318+
$null = Remove-DbaDatabase -SqlInstance $instance -Database $testDb
319+
}
320+
```
321+
322+
## VERIFICATION CHECKLIST
323+
324+
**Comment and Parameter Preservation:**
325+
- [ ] All comments preserved exactly as in original
326+
- [ ] Parameter names match original exactly without modification
327+
- [ ] No backticks used for line continuation
328+
- [ ] No `= $true` used in parameter attributes (use modern syntax)
329+
- [ ] Splats used only for 3+ parameters
330+
331+
**Style Requirements:**
332+
- [ ] Double quotes used for all strings
333+
- [ ] **MANDATORY**: Hashtable assignments perfectly aligned
334+
- [ ] Splat variables use descriptive `$splat<Purpose>` format
335+
- [ ] Variable names are unique across scopes
336+
- [ ] OTBS formatting applied throughout
337+
- [ ] No trailing spaces anywhere
338+
339+
**dbatools Patterns:**
340+
- [ ] EnableException handling correctly implemented
341+
- [ ] Parameter validation follows dbatools pattern
342+
- [ ] Where-Object conversions applied appropriately
343+
- [ ] Temporary resource cleanup implemented properly
344+
- [ ] Splat usage follows 3+ parameter rule strictly
345+
346+
## SUMMARY
347+
348+
The golden rules for dbatools code:
349+
350+
1. **NEVER use backticks** - Use splats for 3+ parameters, direct syntax for 1-2
351+
2. **NEVER use `= $true` in parameter attributes** - Use modern syntax: `[Parameter(Mandatory)]` not `[Parameter(Mandatory = $true)]`
352+
3. **ALWAYS align hashtables** - Equals signs must line up vertically
353+
4. **ALWAYS preserve comments** - Every comment stays exactly as written
354+
5. **ALWAYS use double quotes** - SQL Server module standard
355+
6. **ALWAYS use unique variable names** - Prevent scope collisions
356+
7. **ALWAYS use descriptive splatnames** - `$splatConnection`, not `$splat`

0 commit comments

Comments
 (0)