Skip to content

Commit 01105dd

Browse files
πŸ“– [Docs]: Coding standards put reuse before building (and .NET when you build) (#18)
The coding standards now make the reuse decision explicit: before writing new logic, reach for what already exists β€” a language or runtime built-in, then an existing function, then a trusted dependency β€” and build it yourself only when nothing fits. The PowerShell idiom's ".NET" guidance is reframed to fit that order: .NET is what you reach for when you _implement_ the work (or fix a slow internal function), not the first resort ahead of reusing a cmdlet or an existing function. > ⚠️ This PR has no linked issue. Consider creating one for traceability. ## Changed: reuse comes before building `src/docs/Coding-Standards/Functions.md` gains a **Reuse before you build** section (language-agnostic β€” DRY and single responsibility applied across the whole codebase): - Prefer a built-in. - Reuse an existing function; if it is the weak link on a hot path, fix it there so every caller benefits. - Take a dependency on a trusted module for a larger capability that already exists. - Build it only when nothing fits β€” sized to the need (small logic inline, a larger capability as its own module). ## Changed: PowerShell β€” reuse first, then reach for .NET The "do the work in .NET" idiom in `src/docs/Coding-Standards/PowerShell/index.md` now leads with the reuse order (built-in cmdlet/operator β†’ module function β†’ `#Requires` / `RequiredModules` dependency β†’ your own code) and links the baseline. The .NET specifics (hot-path calls, `[void]` / `$null =`, typed lists) remain for when you actually implement the work. ## Technical Details - Two files, docs-only; markdownlint, the link checker, and the doc-index check all pass locally. - Companion change in the mirrored AI-Platform/ai-platform standards β€” <https://dnb.ghe.com/AI-Platform/ai-platform/pull/325>.
1 parent 81edaa6 commit 01105dd

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

β€Žsrc/docs/Coding-Standards/Functions.mdβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ Functions are the unit of intent. A good function does one thing, says what it d
1313
- Keep it small enough to hold in your head. Whether it fits on a screen is a better test than a line count.
1414
- One level of abstraction per function β€” don't mix high-level orchestration with low-level detail in the same body.
1515

16+
## Reuse before you build
17+
18+
Before writing new logic, use what already exists β€” and build only what does not. This is DRY and one responsibility applied across the whole codebase, not just within one function.
19+
20+
- Prefer a built-in. If the language or runtime already does the job, use it instead of a hand-rolled version.
21+
- Reuse an existing function instead of re-implementing it. If it is the weak link β€” too slow or imprecise on a hot path β€” fix it there so every caller benefits, rather than working around it.
22+
- Take a dependency on a trusted module for a larger capability that already exists elsewhere; declare it explicitly instead of copying it in.
23+
- Build it only when nothing fits β€” no built-in, no existing function, no trusted dependency. Size the build to the need: small logic lives inline where it is used; a larger, cohesive capability becomes its own module.
24+
1625
## Signatures are contracts
1726

1827
- Type the parameters and return value where the language allows. The signature documents intent and lets tooling catch misuse before it runs.

β€Žsrc/docs/Coding-Standards/PowerShell/index.mdβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Beyond the basics, these language-specific habits keep PowerShell correct and fa
5757
- **Single-quote strings unless you need expansion.** Use `'literal'` by default; reserve `"...$var..."` for interpolation or escape sequences, and here-strings (`@'...'@`, `@"..."@`) for multi-line text β€” literal-versus-interpolated intent then stays obvious.
5858
- **Splat calls that carry many parameters.** Build a `@{}` of parameters and splat it (`Get-Thing @params`) instead of a long line of `-Param value` pairs or backtick continuations; it reads better and diffs cleanly.
5959
- **Put `$null` on the left of a comparison** β€” `$null -eq $x`, never `$x -eq $null`. Against a collection the right-hand form *filters* rather than tests. Use `-contains` / `-in` for membership, never `-eq`.
60+
- **Reuse before you build.** Work down the [reuse order](../Functions.md#reuse-before-you-build) β€” a built-in cmdlet or operator, then an existing function (public or private), then a trusted module (`#Requires -Modules` / `RequiredModules`), then your own code (small logic inline, a larger capability as its own module).
61+
- **PowerShell already *is* .NET; work at that level rather than wrapping it.** Casts, type accelerators (`[datetime]`, `[int]`), the `-split` / `-replace` / `-match` operators, and member methods (`.Trim()`, `.Where()`) all resolve to the base class library β€” using .NET means reaching for BCL types and methods for the computation, not restating everything as `[Namespace.Type]::Method(...)`. Where idiomatic PowerShell already resolves to the same .NET call, leave it; reach for explicit .NET only where it is measurably faster or more precise, and keep cmdlets and the pipeline where you need them for glue or readability.
62+
- **Do the work in .NET when you implement it.** When you write the logic yourself β€” or fix an internal function that is too slow or imprecise on a hot path β€” call the .NET base class library directly instead of a cmdlet pipeline: `[System.IO.File]::ReadAllText($path)` over `Get-Content -Raw`, `[System.IO.Path]::Combine(...)` for paths, `[System.Text.StringBuilder]` for repeated concatenation, `[int]::TryParse(...)` for parsing. .NET methods are faster and their contracts are precise; keep cmdlets where their clarity is worth more than the speed. The next two rules are specific cases.
6063
- **Suppress unwanted output with `$null = ...`** (or `[void]` for method calls), not `| Out-Null` β€” the pipeline form is markedly slower on hot paths.
6164
- **Build collections with a typed list, not `+=` in a loop.** `$a += $x` reallocates the whole array every iteration; use `[System.Collections.Generic.List[T]]` with `.Add()`, and prefer a cmdlet's `-Filter` over piping to `Where-Object` on large sets.
6265
- **Keep secrets out of source, and never `Invoke-Expression` untrusted input.** Accept credentials as a `[PSCredential]` parameter with the `[Credential()]` attribute rather than calling `Get-Credential` inside a reusable function, so a caller can pass one they already hold, and take other sensitive values as `[securestring]`. Guard state-changing commands with `ShouldProcess` (see [Functions](Functions.md)); the wider rules live in the [Security](../Security.md) baseline.

0 commit comments

Comments
Β (0)