Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command Prefixes #23

Open
Jaykul opened this issue May 29, 2015 · 13 comments
Open

Command Prefixes #23

Jaykul opened this issue May 29, 2015 · 13 comments

Comments

@Jaykul
Copy link
Member

Jaykul commented May 29, 2015

Should we be embedding Command Prefixes in function names, or using the PSD1 to specify the default?

I'm going to start this debate by saying that I think putting prefixes into the code is wrong.

  1. You break the -Prefix feature on Import-Module (nobody wants two prefixes)
  2. You break discoverability, because nobody can Get-Command -Noun and find your commands (because your noun is "ADUser" instead of "User" and "ADGroup" instead of "Group").

The counter argument seems to be that "ADUser" and "ADGroup" are actually nouns, not just nouns with a module prefix, and I guess I would be OK with that -- but that should be the distinction you make, and the bar you set for hardcoding a prefix: the prefixed noun should be so obvious that it's discoverable, and that all of your users will intuitively know to search for it. E.g.; "ADUser" might be ok, but "QADUser" isn't 😉

@nightroman
Copy link

Some facts (if I am not mistaken, double check would not hurt).

DefaultCommandPrefix in PSD1 manifests was introduced in v3:

New DefaultCommandPrefix key in module manifests that avoids name conflicts without changing module code.

Something was amended in 4.0:

In Windows PowerShell 4.0, if a module uses the DefaultCommandPrefix key in its manifest, or if the user imports a module with the Prefix parameter, the ExportedCommands property of the module shows the commands in the module with the prefix. When you run the commands by using the module-qualified syntax, ModuleName\CommandName, the command names must include the prefix.

Thus, it looks like if I want to support 2.0 or if I want the same behavior for 3.0 and 4.0 then I should not use DefaultCommandPrefix in module manifests. Hardcoded prefixes may be not perfect but they may have less issues.

@mwjcomputing
Copy link

I have added the word Sec as a prefix to all functions and files that I create for PoshSec. I could go back and remove them and have the module manifest pick them up.

This is mostly right now for v2 compatibility. However, I am probably moving in a direction to move away from V2 support to take advantage of some of the language benefits of later releases.

I think we should use the prefix’s but put a note in for v2 compatibility this might not be wise.

@KirkMunro
Copy link
Contributor

The issues that are identified above are exactly why I recommend very strongly against adopting this style until v4 becomes the community baseline. The bugs/discrepancies between v3/v4 on manifest defined prefixes are such that this feature should be avoided for all modules that will work on v2 or v3 IMHO. Plus, thinking about the large number of script or command authors that won't think about version when they create and share content, I prefer erring on the cautious side, so that their content has a better chance of working in downlevel versions as is, consistent across versions.

All that said, if the vote is against me then my only request is that manifest-defined prefixes is listed as a recommended style specifically for modules that require PSv4+ due to the bugs in v3.

@jrich523
Copy link

I guess that brings up a good point, should the docs be based on PS versions?

i use to avoid the defined prefix because i had run in to problems with it in V3, but with V5 (using it for psgit) it seems pretty ironed out..

@Jaykul
Copy link
Member Author

Jaykul commented May 30, 2015

Frankly, I really dislike prefixes. I always have. I would much rather just make people type PoshSec\Get-Noun if they have more than one Get-Noun on their system.

Using "SecNoun" when I mean "Noun from the PoshSec module" breaks about a quarter of the usefulness of nouns, and isn't guaranteed to make it unique anyway, because unlike module names which are unique because they have to go in the file system, and because PowerShellGallery will enforce it 😜 prefixes don't have any guarantee of uniqueness at all.

For what it's worth, I don't think most people can write code to target PS2 (you have to have PS2 to target it, if you're just testing with PS4 and -Version 2 your chances are very slim).

In any case, PS2 was so long ago that I do not want to take it into account in the style guide, but I do think it's worth at least mentioning in the Best Practice stuff (like this), and even suggesting an alternate best practice for people who want to target it.

I don't even want to talk about prefixes, but since the Microsoft Team has required people to use prefixes, but completely failed to address the issues (in fact, I think they're lying about insisting on the use of prefixes "from the very early days"), I think we have to. They don't even mention that there's a module manifest way of specifying them, or that users can specify their own prefixes at import time.

Bottom line, I think we have to document what @nightroman wrote, and then recommend hard-coding the prefix if you need to support PS2 (or PS3?).

Is there actually any reason the fully-qualified name difference matters? bothers you for PS3 -- not sure why it would, if you're using prefixes it's because you don't expect people to type the full name, right?).

We've got some consultants here right? Have any of you ever seen people using the fully qualified Module\Verb-Noun command syntax?

@JacodeWeerd
Copy link
Contributor

I have created some modules for our company, for internal use. Other departments are on the edge of creating modules, and I have made a template for internal modules. Why invent a wheel twice?

We have had some nasty problems with duplicate commands, so in the template I use a default prefix. This "guides" others to use the prefix. (Most of them just don't bother for any name convention)
The prefix is, by the way, a well known term in our company.

So I was a big fan of prefixes.
However, recently we started using the modules more company wide and 'discovered' 180 2008 servers with PowerShell 2 in a dark corner. Old applications, no budget, ....

Now I ended up here, while trying to make the code compatible for PowerShell 2.
I do not like the idea of hardcoding the prefix in all functions, but there seems no other way.

At this point not sure if we 'leave the old servers behind', or put a lot of effort in making the code work for PowerShell 2.

@Jaykul: I have never seen someone using Module\Verb-Noun command syntax
But I am not a consultant.

@rkeithhill
Copy link

Considering Microsoft is deprecating v2 do we still need to consider v2 in this issue?

Also, when I write a module I expect to be used by a lot of folks I usually module-name prefix commands so that I'm sure I'll get the built-in command instead of someone's poorly written proxy. Seen a few too many bugs due to command proxies.

@Jaykul
Copy link
Member Author

Jaykul commented Aug 30, 2017

My feelings about this have changed a bit in the last two years, but frankly, deprecating PS2 (or even PS3) doesn't figure into that. So here's a few random thoughts:

Microsoft hard-codes the prefixes into their command names.

They do this for everything. Even when the module name is, say, "AzureRm" and your prefix is "AzureRm" -- how is Get-AzureRmVm any better than AzureRm\Get-VM? Azure is an extreme example: they have modules like "AzureRm.DevTestLabs" and the prefix ends up being the rather arduous "AzureRmDtl" ...

If you rely on DefaultCommandPrefix it's just a default.

It gets replaced when I specify a -Prefix when I import the module.

Remember that the solution for implicit remoting is to use prefixes.

What is your command going to look like when I do something like:

Import-Module YourModule -PSSession $S1 -Prefix Server1
Import-Module YourModule -PSSession $S2 -Prefix Server2

Prefixes still have no guarantee of uniqueness.

Even if you own the module "AzureRm" and you use "AzureRm" as your prefix, you have no way to prevent someone from colliding with your command name -- the only way to unambiguously be completely sure is to use the fully qualified name, even if that means you have to write AzureRm\Get-AzureRmVm.

That's why the PowerShell Gallery is so worried about command collisions that it won't let you install a module that has command-name collisions without acknowledging it...

But the bottom line is ...

Personally, I still don't like them, and I don't want to recommend prefixing everything.

@KirkMunro
Copy link
Contributor

the only way to unambiguously be completely sure is to use the fully qualified name, even if that means you have to write AzureRm\Get-AzureRmVm

But even then, someone could create aliases and redirect your command invocations (because you can create aliases that look like fully qualified names). Just mentioning that because it comes as a surprise to most folks.

Personally I still recommend prefixing every command. I suspect I won't change that stance unless/until PowerShell provides a way to control the priority used in command name resolution via a using statement (e.g. using modulename; <# For the remainder of this scope, commands from modulename will take priority over other commands of the same name #>} -- something like that anyway).

@Jaykul
Copy link
Member Author

Jaykul commented Aug 31, 2017

It's true that basically anything can be stepped on in PowerShell -- if it's deliberate. Even properties and methods:

Update-TypeData -TypeName System.String -MemberName Length -MemberType ScriptProperty -Value { 0 }

@KirkMunro
Copy link
Contributor

A few years later, and this just came up again for me.

I'm watching a video talking about the alpha Microsoft.Graph module that uses AutoRest to generate over 2000 commands in dozens of auto-generated modules. This module bundle highlights one key thing that prefixes have that fully qualified command names don't: the ability to use one constant prefix across many modules. Looking at the most recent updates of the Az module, all cmdlets are prefixed with Az, no matter which module they come from. That model supports faster loading time (only load the modules that are actually required) while maintaining prefix consistency across a very large set of cmdlets that spans many modules.

On the flip-side though, some auto-generated cmdlets have very long names already, so prefixes really should be kept short. Like 2-4 characters short. Otherwise, they're just adding more noise to a very noisy command set. Also, what would you pick for an prefix for commands in Microsoft.Graph? Az is meaningful for Azure, because the commands run against Azure, but Graph (or some shorthand version of Graph) isn't as meaningful because Microsoft.Graph is "the gateway to data and intelligence in Microsoft 365". With that in mind, maybe M365 would be a better noun prefix, or just 365. Then we could have Get-365User, and so on.

Or maybe we should just do away with module names and nouns altogether, and just use emojis since they are supported in modern consoles.

☁️\Get-👤
Get-👥

etc. 🤣

@Jaykul
Copy link
Member Author

Jaykul commented Dec 18, 2019

First of all, as far as using module <name> -- that actually works. Given two modules with Get-Number, using module works, the same as Import-Module (but also imports the types), except that Import-Module can be run anywhere (whereas using can only be the first thing in a script).

IpMo AllOnes
Get-Number # calls AllOnes\Get-Number
IpMo AllTwos
Get-Number # calls AllTwos\Get-Number
Ipmo AllOnes
Get-Number # calls AllOnes\Get-Number
AllTwos\Get-Number  # calls AllTwos\Get-Number

screenshot example with output

In my opinion, the Azure modules are a great example of why prefixes are awful.

Although I'm glad it's "Az" in place of "AzureRM" now (AzureRmApplicationInsights was always entirely too much prefix), the "Az" is not the whole prefix. That is to say, for most modules, the entire module name is in the prefix:

  • The Az.Media module commands are all prefixed with AzMedia
  • The Az.KeyVault module commands are all prefixed with AzKeyVault
  • The Az.NotificationHubs module commands are all prefixed with AzNotificationHub
  • The Az.AnalysisServices module commands are all prefixed with AzAnalysisServices
  • The Az.OperationalInsights module commands are all prefixed with AzOperationalInsights
  • etc.

On top of that, the Az prefix prefix is the third iteration on the module prefix. We've gone from Get-AzureFoo to Get-AzureRmFoo to Get-AzFoo -- The modules are basically the same. In the most recent change, the actual AzureRm and Az commands are all mostly identical, but we all had to do multiple rewrites just because the team decided to change prefixes.

If you care, I bet I can come up with a rough estimate of how much that name change cost our company, because we had stories in the backlog and tasks. Not cool.

Ultimately, the prefixes aren't applied consistently. A few of the modules, like Az.MachineLearning and Az.DevTestLabs and Az.Compute use a different prefix (AzMl and AzDtl and AzVm respectively) and there are a few commands in some modules (well, at least in the Az.Compute module) that are only prefixed with "Az" ...

Hypothetically, it's good they're shipping a collection of modules rather than a single module, but the prefix has nothing to do with that. Not having a prefix isn't going to force anyone to publish only one module.

In fact, the net result of the prefix is that it leads people down the wrong path: they write scripts calling functions from multiple modules and don't even know which modules they're using, so they don't import them explicitly -- nor document which ones they are using, so you have to install "Az" (all 120 modules worth of it) and load the modules implicitly on-demand, resulting in slower scripts.

@KirkMunro
Copy link
Contributor

using module is disappointing. I was really hoping it would be a better successor to Import-Module, such that I could use it to indicate which modules were a priority over a script block or file.

For example:

nmo -name Test1 {function Get-Thing {'Thing1'}} | ipmo
nmo -name Test2 {function Get-Thing {'Thing2'}} | ipmo
Get-Thing # returns 'Thing2'
using module Test1 {
Get-Thing # I want this to return 'Thing1'
}
Get-Thing # This would return 'Thing2' again

That doesn't work though, because as you point out, using module must be the first thing in the file, like a precompiler directive.

As for all of the rest of the comments you made @Jaykul, while I would prefer a world without prefixes, I don't believe that no prefix is a realistic option given what we have available today as a way to define the module we want to use in scripts/functions, which is amazing given that modules came out in 2009 -- 10 years later, and it's still quite messy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants