Musings on “Clean Code” in PowerShell

Lately I’ve been reading Uncle Bob Martin’s Clean Code. For those who haven’t read it, the book focuses primarily on the tasks that a developer should go through after “getting the code working.” Specifically, the steps required to convert the initial mess into something that is easy to read, understand and maintain. (This basically amounts to “have a complete suite of unit tests, keep them passing, and refactor until you’ve removed all the code smells.”)

The book uses Java in all of its code examples, but the guidelines he proposes are applicable to any object-oriented language. However, PowerShell isn’t really an OO language at this point. It’s a consumer of the .NET Framework, but support for defining new types is very limited. (You can either embed .NET code and compile it on the fly with Add-Type, or use some halfway measures to create dynamic PSObjects with properties / methods created via Add-Member.) For the most part, PowerShell itself is a procedural language.

On functions, the book offers a few main points of advice: They should be extremely short, should only have one reason to change (the Single Responsibility Principle), and the number of arguments should be kept to a minimum (zero, if possible). In the book, that last bit with zero arguments is made possible by Object-Oriented programming. You would “promote” some method arguments to be fields on a class instead, and just call the method on that class without having to pass in anything. DoSomething(param1, param2, param3) could be refactored into a Widget class with those three values as fields, where you wind up calling myWidget.DoSomething(); .

For years, I’ve avoided using global (or script) scoped variables wherever I could, believing it to be a bad practice. By having my functions accept all of their input in the form of parameters, and having them return output rather than modifying some global state, they become self-contained units of code which can be easily reused in other scripts on an as-needed basis. (Though this is mainly a concern from my VBScript days; in PowerShell, shared code gets placed into a module, rather than copied and pasted around.) The downside to this is a lot of extra “noise” in the script code; parameter blocks are partially repeated across many functions, and calls to those functions involve passing a large number of arguments (usually at least two, and frequently four or more.) Because I’ve also been in the habit of making every PowerShell function an Advanced function, the parameter blocks themselves can be quite long (3-4 lines per parameter, generally).

Based on the advice in Clean Code, I’m considering changing these habits for some of my PowerShell script code. By making use of the Script scope, a PowerShell script or module can emulate a “class” in some ways, removing the need to pass around a bunch of common objects as parameters. Whenever accessing these variables, you can prefix their names with $script: in order to make it clear that the variable is outside of the local scope. I could also drastically shorten the param blocks of some of my internal module functions. (Do I really need to specify things like [Parameter(Mandatory = $true)] all over the place, when I’m the one calling the functions to begin with, and there are unit tests in place to make sure I didn’t screw up?) These changes would go against all of the scripting habits I’ve built up over the past decade, but would certainly result in function definitions and calls that are much, much shorter than they are right now.

Here’s an example of an internal module function currently in the ProtectedData module, and how it might have its length reduced by 60 percent by simply getting rid of the bulk of the param block and rearranging the code slightly. The function exists at a point where $IterationCount has already been validated anyway. Likewise for the $Password and $Salt parameters; if they make it to the Get-KeyGenerator function, they’ve already been validated by the publicly exported module functions.

function Get-KeyGenerator
{
    [CmdletBinding(DefaultParameterSetName = 'CreateNew')]
    [OutputType([System.Security.Cryptography.Rfc2898DeriveBytes])]
    param (
        [Parameter(Mandatory = $true)]
        [System.Security.SecureString]
        $Password,

        [Parameter(Mandatory = $true, ParameterSetName = 'RestoreExisting')]
        [byte[]]
        $Salt,

        [ValidateRange(1,2147483647)]
        [int]
        $IterationCount = 1000
    )

    $byteArray = $null

    try
    {
        $byteArray = Convert-SecureStringToPinnedByteArray -SecureString $Password

        if ($PSCmdlet.ParameterSetName -eq 'RestoreExisting')
        {
            $saltBytes = $Salt
        }
        else
        {
            $saltBytes = Get-RandomBytes -Count 32
        }

        New-Object System.Security.Cryptography.Rfc2898DeriveBytes($byteArray, $saltBytes, $IterationCount)
    }
    finally
    {
        if ($byteArray -is [IDisposable]) { $byteArray.Dispose() }
    }

} # function Get-KeyGenerator
function Get-KeyGenerator([System.Security.SecureString] $Password,  [byte[]] $Salt,
                          [int] $IterationCount = 1000)
{
    $byteArray = $null
    try
    {
        if ($null -eq $Salt) { $Salt = Get-RandomBytes -Count 32 }
        $byteArray = Convert-SecureStringToPinnedByteArray -SecureString $Password
        New-Object System.Security.Cryptography.Rfc2898DeriveBytes($byteArray, $Salt, $IterationCount)
    }
    finally
    {
        if ($byteArray -is [IDisposable]) { $byteArray.Dispose() }
    }
}
Advertisements

About Dave Wyatt

Microsoft MVP (PowerShell), IT professional.
This entry was posted in PowerShell, Professional. Bookmark the permalink.

3 Responses to Musings on “Clean Code” in PowerShell

  1. Larry Weiss says:

    I absolutely agree 100% with this demonstrated style. I’ve “sat on my hands” in many a presentation promoting the “evils” of global variables, and just kept quiet thinking to myself about the Emperor’s New Clothes that the audience was admiring.

    Like

  2. ghostsquad2013 says:

    I’ve long struggled with how much I love PowerShell yet I’m forced to write non-OO code. It makes me want to write most of my stuff in C# and call methods from there. That get’s hairy though, as the reason “why write it in powershell at all?” comes into play. UGH! There is this: http://psclass.codeplex.com/ but it hasn’t been updated since 2009.

    Like

    • Dave Wyatt says:

      PowerShell 5.0 is going to have some limited ability to define .NET classes without writing C#, but you may find that if you want to fully take advantage of OO concepts, you still need to go straight to a .NET language.

      From what we can see in the July experimental WMF release so far, you can’t use inheritance or implement interfaces; classes are just simple things with fields and methods. This is still a far cry from a true OO language, but it’s a step in the right direction.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s