Do developers really dislike PowerShell?
My friend Aleksandar Nikolić asked a question on twitter
Why do developers dislike #PowerShell?
Why is it hard to find a developer to work on PowerShell cmdlets? … Is it really that hard to support basic PowerShell concepts?
Which can’t be answered in a tweet. If you create non-PowerShell command lines programs in C# (or any .Net language), then I really encourage you to read to the end and not go off in a huff after you have read the following opinion about what you do.
The act of creating a non-trivial .NET command-line program, instead of one or more
PowerShell cmdlets demonstrates a lack of understanding of what the program is for.
Read it again.
And again.
I’ve also said that Microsoft (or any other large software company) should
“Treat shipping such .NET programs (or trying to) as inadequate job performance”.
This only applies to .NET – programs written in anything else can’t assume PowerShell, but these are provocative things to say to and about .NET programmers, so I had better explain and justify them
Snobbery and Gatekeeping.
In plenty of places I’ve met the attitude “What I do is the true form of the art”: in photography, “that is taking snaps” or in Scuba diving “that’s just swimming”, and it applies to “it’s only proper programming if…”
When I learned to program, BASIC, C, FORTRAN, LISP, Pascal etc. were called “High level” languages (BASIC was still an acronym, Java hadn’t been thought of);
“low-level” details of what CPUs do were left to those who wrote in assembly
language and to writers of compilers / interpreters, nobody else cares. To
at least some of those who write in those languages, running the resulting programs makes
someone a “user”; and gluing programs together in batches doesn’t elevate users to
programmer status. “User” is sneery - I was told once “Only drug pedlars and
computer programmers call the people who pay them ‘users’”.
Having written Assembly language and Batch files and in more high-level languages than I can remember, I have no time for anyone who says their gatekeeping criteria for programming excludes writing some sequences of instructions for a computer on grounds of the tool or language used to do it. And with PowerShell calling .NET APIs in a more streamlined way than C#, the boundaries such people might draw to exclude a .bat
file get fuzzy.
PowerShell, particularly, attracts complaints about verbosity. Recently I’ve been working with Octopus Deploy, which runs operations as projects. A project has a deployment process which is a sequence of steps, and each step contains at least one action like “run a script”, “deploy a website”, “import a database”. I’ve been developing PowerShell tools for Octopus that let me write something like this
For “Get the Octopus project(s) matching ran*
(there’s one named random
quotes), and give its deployment process, get the process’s steps, their actions
and each action’s properties (Properties hold the parameters for the action).
Turning verbosity up to 11 would re-write it like this:
The first line alone is longer than the original! Staying with Octopus for another example, a release of a project creates deployments to carry out its actions in one or more environments, and the task performing the actions, creates a log file. It takes half a dozen API calls to get key points from the log of the last release, I can do that like this:
For “get all releases of the project, select the first (most recent), get its
deployments and the task for each one. Then get tasks’ raw logs – they are single
strings that needs splitting at new-lines, and pick out the lines
which contain “complete” or “fail”
That’s a terse way to get do half a dozen different calls to Octopus’ Rest API. and it came from my command history: writing for
others to read it might look more like this:
PowerShell verbosity is optional; in C# it’s mandatory. After saying PowerShell was more streamlined, it might seem I’m trying to
be provocative, but I’m not: scripts in “glue languages”, depend on sophisticated objects –
like a Deployment
object with a .Task()
method - and they try to smooth
out type differences between types. C# code ported to PowerShell has tell-tales like
if ( ! [string]::IsNullOrEmpty($s) )
or
if ($a.count -gt 0)
where PowerShell writes
if ($s)
or
if ($a)
because empty strings and empty arrays are treated as Boolean false.
If you’ve come to think that strict type-enforcement is always good there’s
something “improper” about allowing anything to act as a Boolean
, or implicit
.ToString()
operations or treating a type-name before a parameter as a request
to cast to that type, not a requirement to be of that type. But if you expect
to be able to perform the same filter, sort or export operations on Octopus Projects
that you do on virtual hard disks, or on firewall rules, then strict type-enforcement
seems like needless pedantry. Neither position is wholly right or wrong.
Mental mutilation
It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration - Dijkstra
Early in my Computer Science degree, a lecturer put that quote up on the board – to the shock of the majority who’d started with BASIC. The code below may be the best known in the history of programming
Why do I, Dijkstra-like, blame the seemingly innocuous hello world
for harm akin to BASIC’s GOTO
statement?
Because it teaches that programs are about printing output: programmers start out with a model “take user input”, “do things with numbers
and text strings”, “print output”. This model simply doesn’t apply when we’re
telling Octopus to make a release or setting a firewall rule or asking for a VM to
be shut down - the purpose of many programs isn’t to print anything on the
screen.
In fact printing is often actively unhelpful on two fronts. How so?
Here’s a screen shot from the git
command-line tool. Git repositories have been
a boon for developers, but the user-mendacity of the command line tool costs
time and causes frustration.
The screen shot above shows what I’ve dubbed developers going Berserk with
crayons. Origin/main
and Origin/HEAD
are not dangerous, wrong or urgent so why
are they red?
Is main
especially good/ok/working? And if not why is it in green? And why is HEAD
in cyan?
Tags would be in bright yellow; I think there’s only a desire to distinguish types
of things linked to a commit, so there’s no message in the colours but I always get
an uneasy feeling that there IS but that I don’t understand what. Yellow, used
for the 40-digit hash, renders inconsistently - here it’s the next most
prominent thing.
Git log
is flexible, but to answer “who committed things for work item 1234” it
must be told print in a way that can be parsed by something else. With a
PowerShell Get-GitLog
command (which automatically has a short form Gitlog
- no
more verbose!) you could add
| ? Comment -match '#1234' | % author
Printed output is, too often, what the person “doing stuff with numbers and
strings”, thought was important, take git push
for example
I contend that most users want that to be taken care of inside a black box.
A lot of effort goes into what gets printed, but it is delivered badly if it is wanted at all.
But PowerShell would change how my program is called…
Input should be able to come from parameters. When C# authors opt to deliver command-line functionality through PowerShell each of their cmdlets can say “support these parameters, help users complete them like this, validate like that” and so on. An author who opts to create a C# console app must write code to process its arguments. Remember: users care most about how stuff goes in and what comes out, but programmers can be pre-occupied with what happens between, meaning that (like outputs) inputs can be shaped too much by internals and not enough by user requirements.
Git
is far from the only case of a single executable which runs multiple
commands. I often cite IPConfig
as an example: its sub-commands DisplayDNS
, FlushDNS
etc could be
separate programs, and arguably that would be better done that way. net.exe
goes back to the 1980s and has added functions over
time. I doubt if many users have a strong preference for syncing time with the
existing Time
command, or with net Time
or some other executable and doubt even
more that there is a consensus.
But lashing more onto single commands doesn’t make for a good user
experience, commands in net.exe
aren’t consistent with each other, net computer
and net user
can’t agree whether to use /del
or /delete
; these two use
net <type of thing> <name> /action
so you’d expect net service <name> /start
(or /stop
) instead of the actual net start <servicename>
.
Another developer complaint is PowerShell makes me pick form a list of verbs
and specify a noun, but net.exe
(like git
) shows developers can choose badly. Why “use
” for
“connect a local name to a share”, why not “mount
” or “connect
” or something
else?
The objection to having Add-Computer.exe
and Delete-computer.exe
with
a lot of duplication (like seeing if the computer exists) melts away when they
are cmdlets in the same C# class.
PowerShell approves “Remove
” but not “Delete
” and I’ve heard developers object that they would need to use new
instead of create
, they’re synonyms! It really doesn’t matter which one is picked; if the original authors of git
had chosen Copy
instead of Clone
would anyone care today? Git
uses both pull
and fetch
for “bring something here”,
but you need to know what each synonym brings: Here’s the help for those two.
The help which begins “fetch” isn’t for the fetch
command. If fetch
downloads
and pull
fetches why are the names that way (Why pull
and push
, why not send
and receive
or get
and put
? I don’t think there’s any strong reason).
One of the following descriptions is for git’s show
command but 4 of the 5 begin
“show”
Is getting the history showing how a branch got to its current state, part of the git
branch
command, or is it one of the commands above, and if so, which? How does the
program score on taking the inputs we want in the way we want? 2 or 3 out of 10?
In the end developers argue that as much as possible should be left to them, but some of that is burning time without adding value for users. With a choice between “C# to work as PowerShell cmdlets” and “C# to work as a console app” how should we react to those who say “We’ll make something which works less well by expending spend extra effort writing print routines and input handling” . I’m for telling them bluntly “that’s doing a lousy job.”