diff --git a/src/docs/Coding-Standards/Dependencies.md b/src/docs/Coding-Standards/Dependencies.md index bf9297d..aef7a84 100644 --- a/src/docs/Coding-Standards/Dependencies.md +++ b/src/docs/Coding-Standards/Dependencies.md @@ -18,6 +18,8 @@ A pin has two independent parts; keep them separate. The strongest posture combines both: a verified identity **and** a deliberate version. Identity is the integrity control; tightness is the velocity-versus-risk control below. +Before you choose a pin, decide whether you should add the dependency at all. For modules and libraries we build, the default is to **avoid introducing a new third-party dependency when the capability can reasonably be implemented with PowerShell, the .NET base class library, or code we own**. Every external DLL, package, or module adds another update stream, trust boundary, and failure mode to carry for the lifetime of the module. Spend a bit more effort up front if that keeps the shipped surface smaller and the ownership clearer. + ## The locking spectrum From tightest to loosest, each step trades safety for speed: diff --git a/src/docs/Coding-Standards/Functions.md b/src/docs/Coding-Standards/Functions.md index 814ffce..6946a19 100644 --- a/src/docs/Coding-Standards/Functions.md +++ b/src/docs/Coding-Standards/Functions.md @@ -19,8 +19,8 @@ Before writing new logic, use what already exists — and build only what does n - Prefer a built-in. If the language or runtime already does the job, use it instead of a hand-rolled version. - 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. -- Take a dependency on a trusted module for a larger capability that already exists elsewhere; declare it explicitly instead of copying it in. -- 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. +- Prefer shared code you own before introducing a third-party dependency, especially in a shipped library or module. An external package is a lasting trust and maintenance commitment, so only take it when the capability is large or specialized enough that owning it would be the worse trade. +- Build it only when nothing fits — no built-in, no existing function, no shared code you own, and no justified trusted dependency. Size the build to the need: small logic lives inline where it is used; a larger, cohesive capability becomes its own module. ## Signatures are contracts diff --git a/src/docs/Coding-Standards/PowerShell/index.md b/src/docs/Coding-Standards/PowerShell/index.md index 2faf2f3..24cd48a 100644 --- a/src/docs/Coding-Standards/PowerShell/index.md +++ b/src/docs/Coding-Standards/PowerShell/index.md @@ -65,7 +65,8 @@ Beyond the basics, these language-specific habits keep PowerShell correct and fa - **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`. - **Match text with the operator built for it.** Use `-like` for wildcard patterns and `-match` for regular expressions instead of hand-rolled string surgery; both default to case-insensitive, so add the `-c` prefix (`-clike`, `-cmatch`, `-ceq`) when a comparison must be case-sensitive. - **Use the built-in intent checks for strings and wildcards.** Use `[string]::IsNullOrWhiteSpace($value)` for blank input and `[System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($pattern)` when deciding whether a value contains wildcard syntax. -- **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). State a dependency's acceptable versions as a [version range](Version-Constraints.md). +- **Prefer owned code over third-party dependencies in modules.** For a module we ship, exhaust PowerShell itself, the .NET base class library, and code we already own before adding an external module, DLL, or package. A third-party dependency is a long-term trust and maintenance commitment, so take it only when the capability is large enough or specialized enough that owning it ourselves is the worse trade. +- **Reuse before you build.** Work down the [reuse order](../Functions.md#reuse-before-you-build) inside the code we control — a built-in cmdlet or operator, then an existing function (public or private), then shared code we own, then new code. Reach for a trusted external module (`#Requires -Modules` / `RequiredModules`) only after that bar is cleared, and state its acceptable versions as a [version range](Version-Constraints.md). - **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. - **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. - **Suppress unwanted output with `$null = ...`** (or `[void]` for method calls), not `| Out-Null` — the pipeline form is markedly slower on hot paths. diff --git a/src/docs/Frameworks/Process-PSModule/repository-structure.md b/src/docs/Frameworks/Process-PSModule/repository-structure.md index 399c1fd..adcd00f 100644 --- a/src/docs/Frameworks/Process-PSModule/repository-structure.md +++ b/src/docs/Frameworks/Process-PSModule/repository-structure.md @@ -101,7 +101,7 @@ How the module is built. Declare module dependencies using [`#Requires -Modules`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires) -statements at the top of function files in `src/functions/public/` or `src/functions/private/` that require external modules. +statements at the top of function files in `src/functions/public/` or `src/functions/private/` that genuinely require external modules. For modules we build, the default is to avoid third-party module, DLL, and package dependencies when PowerShell, the .NET base class library, or code we own can carry the feature with reasonable effort. [Build-PSModule](https://github.com/PSModule/Build-PSModule) collects every `#Requires -Modules` declaration across all source files, de-duplicates the list, and writes it into the `RequiredModules` field of the compiled manifest automatically. For the full range of supported syntax variants, see the