Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upIs it time for "PowerShell vZeroTechnicalDebt" and/or for an opt-in mechanism into new/fixed features that break backward compatibility? #6745
Comments
|
Interesting. I've always wondered why the |
There expensive to fix problems which I think of as the technical debt of PowerShell not adopting new .NET concepts as they came along
There are a few regrettable features:
How about a bigger question:Would we be willing to reconsider the "many ways is better" approach and work on a "pit of success" approach? I mean, would we be willing to remove features that are considered "the easy way" but which are fundamentally worse, in favor of having only "the right way" to do things? E.g.:
|
|
All great suggestions, @Jaykul. Re making Re making |
|
Funny, on the Azure PowerShell community standup call right now, and they're going to move from AzureRm to a new Az module that is cross platform (no separate AzureRm.Core), and with all command prefixes changed from AzureRm to Az. The two will be side by side for a while, but they're moving to the new command set. Too bad PowerShell hasn't had an opportunity yet to create a new executable that would run side-by-side with Windows PowerShell, but that could break away from some of the legacy crud that drags it down. |
|
Just imagine what would happen should we go for it: As much as I wished that such a version existed, I think it will only be possible to get to it slowly one breaking change at a time. With v6 a lot of breaking changes were already accepted but I think if one includes too many breaking changes in one version, it will become too complex to upgrade existing scripts/modules. It's good to discuss the most valuable breaking changes but until there isn't an LTS version of pwsh, I do not think it is time to think about having a 2nd train of pwsh with more substantial changes in parallel to the existing mainstream version. |
|
@bergmeister Agreed. However even relatively small changes on a core path can seriously hinder adoption. Look at Python 3. It took 10 years to really catch on. With much bigger changes, who knows how long it will take for Perl 6 to be dominant (and it took them 15 years to come up with their right stuff so 1.5 years for PowerShell++ seems optimistic :-)) On the other hand PHP seems to break things on a regular basis, possibly due to the way and what it's used for. |
|
Python 3 is certainly the horror show, has it really caught on yet? I'm still running 2.7, and don't plan on upgrading any time soon. But I haven't heard much about Perl 6 recently either... I think the lessons to learn from Python there are to separate breaking changes to the language from a version change to the engine. Hypothetically, a PS 7 engine could still run earlier scripts (.PS1) files in a no-breaking changes mode, while if the script were marked as 7 aware (say with a .PS7 extension) they could declare they have been updated and that the require at-least PS 7 to run. Hope that makes sense. |
|
Perhaps the best success story is JavaScript/TypeScript/Babel. A transpiler (with source-map support) seems like the way-to-go for language evolution. |
|
Javascript is a special case. You're pretty much stuck with it so transpiling is really the only option. Typescript is "just" Javascript with extensions so it's easy for people to adopt. Any javascript program is a typescript program so you start with what you have and just add annotations from there. Dart, on the other hand, is it's own language but transpiles to either javascript or a native runtime in Chrome (at least that was the plan at one point). Dart doesn't seem to have picked up much adoption outside of Google, likely because it is its own language..
I was reading an article last week where the author was claiming critical mass had been achieved for Python 3. That all the core modules were available and now people where migrating in droves. We shall see..
WRT |
|
Valid concerns, but in the spirit of:
please share any that you may have in mind.
While a (lexically scoped) opt-in mechanism for incompatible changes is a solution, I'm concerned about two things:
I've said it before: to me - and this is just a hunch - the v6 GA was an unfortunate compromise between making old-timers unhappy with breaking changes while carrying forward enough baggage to hinder adoption In the Unix[-like] world. That said, given PowerShell's relative youth in the Unix[-like] world, perhaps there is (still) more of a willingness to work out problems by way of incompatible changes. As @BrucePay states, Windows PowerShell will have to be maintained anyway, and it can remain the safe haven for backward compatibility. |
|
I think the scale of the negative consequences of such breaking changes has already been covered, and I agree with most of those points. I am writing this because I am doubtful that, in the greater context, these changes would yield significant benefits in the first place. At least for the way I use PowerShell. The OP includes the following statement:
The implied premise of this statement seems to be that making these various breaking changes would alleviate the burden of memorizing exceptions. Indeed that would be a great outcome. However, I am skeptical that that would be the result. PowerShell's behavior is deliberately rich. This makes it both expressive and unpredictable. Expressiveness and predictability seem to work against one another, at least amongst the languages I am familiar with. While I agree that many of the breaking changes mentioned above would improve predictability somewhat in some cases, many unpredictable and surprising aspects of the language will remain. Despite spending years writing PowerShell, I am still frequently surprised by PowerShell behavior that seems to be by design and probably shouldn't be changed. Some recent examples that come to mind are as follows:
I expect that these examples are just a small fraction of the many other carefully-designed but surprising nuances I have not yet discovered. I selected these examples because
I'm fine with this. The overwhelming majority of surprising PowerShell behavior does not have lasting impact on my success with PowerShell. Surprising behavior is almost always caught immediately by testing during development. I learn from the things that slip through and use that to improve my testing strategy. This is true whether those surprising behaviors are the kind that could be eliminated or the kind that must remain. Whether the proposed breaking changes above are made to or not, PowerShell will never become so predictable that I can significantly reduce test coverage. In other words, the way I use PowerShell, I don't think there's much of an upside that's even possible by making the breaking changes proposed above. (BTW, thank you @mklement0 for directly asking this question. This has been in the back of my mind for a while. It's good to see the opportunity for everyone to say their piece.) |
|
Thanks, @alx9r. I think it's important to distinguish between intrinsic complexity and extrinsic complexity:
While the wealth of features and the joining of disparate worlds alone makes it hard to remember all requisite intrinsic complexity, minimizing the extrinsic one is still important. Having to test your intended approach first without just knowing and trusting that it will work - or to have things break unexpectedly due to surprising behavior - is a serious productivity (and enjoyment) hindrance (even though PowerShell commendably makes it very easy to interactively test behavior). It comes down to solid concepts (that don't contravene intuitive expectations), descriptive naming, and good documentation: If I don't know it / not sure if I remember correctly, I need to know where to look it up, and have faith that known problems and edge cases are also documented (either as part of the regular help topics or via links from there). So even if we decide that eliminating extrinsic complexity is not an option, we can at least document it systematically - and the discussion here can serve as the starting point for compiling a "pitfall gallery" (which may also include cases of unavoidable intrinsic complexity that may be surprising). |
Roman Kuzmin has been collecting such a gallery for a while, here: https://github.com/nightroman/PowerShellTraps |
|
Thanks, @HumanEquivalentUnit - that looks like a great collection. Along with the issues collected in this thread, it could form the basis for a gallery that is part of the official documentation, witch each entry augmented with, as appropriate:
|
It's strange that it has to be asked if it's time for "PowerShell vZeroTechnicalDebt"Exactly for this there is a required version command. If something breaks in a new version, the only pain we get is that we have to study and learn the usually small differences. But we (finally) get a platform, which really gets better with each iteration in the core. And much easier to maintain. No, it's never a question to kick off bad designs and decisions by new knowhow and technology. |
|
Also, to flog the living s**t out of a dead horse, it's still stupendously hard to write a well-behaved function or cmdlet that deals with the magical differences between the host's native filesystem and the filesystemprovider. Such nuanced code is difficult to write and often gotten wrong. Here's a post on stackoverflow I answered about seven or eight years ago that's still valid: |
|
Ref: #8495 There are some strange operator precedence rules that deserve revisions: PS> 1, 2, 2 + 1, 4, 4 + 1
1
2
2
1
4
4
1In most languages, one would more commonly expect this to be the output:
|
|
One option that has its own pros and cons is a new feature flag to enable all of the "ideal" behavior so that it's opt-in. If we had some telemetry indicating that most of the usage has moved to the new behavior, we could flip it so it's opt-out. This might all just be a pipe dream as I haven't seen any real world case where such a model worked... |
|
Indeed that would be nice, but given the rather... thorough... nature of some of these revisions in this thread, it risks potentially completely splitting compatibility of scripts between "normal" and "experimental", and potentially into several pieces, depending on which flags an individual has enabled. This means we can't really rely on anything that is an experimental feature in that way, nor write scripts and modules that rely on them, unless we attach a huge warning to the docs pages, or potentially prevent them from being imported unless certain flags are enabled. This might be avoidable, however... if we can, at will, enable and disable specific experimental features on a per-each-module-scope basis. But, given that that only further complicates matters, I'm not even sure if that's a particularly great solution, either. |
|
@vexx32 just to be clear, I wasn't suggesting an |
|
Oh, in that case... Yeah, absolutely. That would let us neatly package everything together under a single umbrella, and actually use it. Much better than what I was thinking! |
|
@jszabo98's addition to the list of issues compiled here, based on #8512:
Example expression that contravenes expectations: PS> $true -or $true -and $false
FalseDue to left-associativity and |
In fact, we already have a model that works all over the world - LTS. MSFT uses this to develop Windows 10. The model is used to develop Unix distributions. I used this approach when I migrated step by step (version by version) an old system from PHP 4 version to next PHP version until I reached the supported PHP 5.x version. At each step, I had to make relatively small and quick changes, after which the system continued to work for some time until the next step. Update: It should be in sync with .Net Core LTS versions. I expect that .Net Core 3.1 will be next LTS and PowerShell Core 6.x should have been on the version. |
|
@iSazonov Can you please edit and quote what you are replying to, and add some context, such as a hyperlink to documentation of said APIs? TYVM |
|
@jzabroski Look files in src\System.Management.Automation\namespaces\ folder. |
It's PowerShell can do that too, with It's just that we don't use exit codes very often in PowerShell (pretty much only for scheduled tasks and interop with other OSes), because it isn't a process-based shell, and functions should not exit. At the end of the day, it's the same reason why detecting the stdout handle type isn't useful in PowerShell (and why "redirecting" to out-null isn't the same as casting to |
|
I recommend people file new issues if they actually have feedback they want acted on -- since the team has clearly and unequivocally ruled out a not-backward-compatible version of PowerShell, I don't know why this thread just keeps going... |
That is news to me. How is this clear? Please provide links to posts or other documentation. It's a bit frustrating for some of us to hear "we'll never break compatibility" while we wrestle with some breakage every day. The point of this thread is so that the free market of ideas can solve some of these problems when upstream won't. Someone might find it advantageous enough to fork powershell and create a version with minimal technical debt. That group will benefit from the ideas presented in this thread. (This is how the world works now. May the best powershell win.) I'll reiterate that the team has already produced a 'not-backward compat' version of powershell by renaming the command from powershell to pwsh. Power(SHELL) is a shell. the job of a shell is to be the glue for humans that ties digital systems together. It's not a compiled binary with minimal external dependencies. Even traditional programming languages plan for and make breaking changes. |
I'm curious about other shells. What do they do? korn, csh, etc. Here is an article discussing the return statement in multiple languages: https://en.wikipedia.org/wiki/Return_statement It calls out that operating system [shells] allow for multiple things to be returned: return code and output. |
|
My team has a variety of scripts that only run in PowerShell 5 even though we use PowerShell 6 as much as possible. In my experience, the premise that PowerShell is completely backward-compatible is definitely false. There are at least some extreme cases (ex: |
|
@chriskuech is there an issue detailing your issue with |
|
@SteveL-MSFT I believe the Mosaic that @KirkMunro linked to directly below @chriskuech comments is the issue you are looking for. And yes, I squeeze the word Mosaic into a tech conversation. That said, @iSazonov closed @chriskuech original issue on October 1, 2018: See #7774 It seems this stuff keeps coming up, in different forms, and the Committee keeps closing issues around it. |
|
@jzabroski found my main issue targeting the root cause. I also filed an issue in the past around one of the symptoms: To summarize, terminating errors terminate the script because PowerShell creators believe the script cannot logically proceed beyond the error, but I don't think that is literally ever anyone but the scripter's decision to make on a case-by-case basis. Only the scripter can decide if they want the script to |
|
(Tangential to the issue above) If PowerShell implements an opt-in "ZeroTechDebt" mode, I definitely think that |
|
@chriskuech If you haven't already, please give this collection of RFCs a look: PowerShell/PowerShell-RFC#187. They speak directly to what you're talking about here without needing a new zero tech debt version of PowerShell. You can also find the four RFCs in separate issues on the Issues page in that repo if that makes them easier to read/digest. Just look for open issues posted by me and you'll find them |
|
@SteveL-MSFT Here is a similar issue that impedes my productivity. It's not Below is an ugly script I wrote for setting SQL Server disk volumes to 64kb. Import-Module Storage;
function Format-Drives
{
# See https://stackoverflow.com/a/42621174/1040437 (Formatting a disk using PowerShell without prompting for confirmation)
$currentconfirm = $ConfirmPreference
$ConfirmPreference = 'none'
Get-Disk | Where isOffline | Set-Disk -isOffline $false
# The next line of this script is (almost) copy-pasted verbatim from: https://blogs.technet.microsoft.com/heyscriptingguy/2013/05/29/use-powershell-to-initialize-raw-disks-and-to-partition-and-format-volumes/
Get-Disk | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -Confirm:$false -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize -IsActive | Format-Volume -FileSystem NTFS -AllocationUnitSize 64kb -Confirm:$false
# See https://stackoverflow.com/a/42621174/1040437 (Formatting a disk using PowerShell without prompting for confirmation)
$ConfirmPreference = $currentconfirm
}
Format-DrivesCouple of side points:
|
|
#Requires -Version has no way to specify a max version This is annoying when you are writing Advanced Functions that load .NET Framework libraries where the API is completely different between .NET Framework and .NET Core, such as how AccessControl API works. |
|
@jzabroski You can specify the edition, however, to separate that: #requires -PSEdition Desktop
# versus
#requires -PSEdition Core |
|
Just a quick note that @rjmholt has officially started a discussion about how to implement and manage breaking changes: #13129. Plus, #6817 and #10967 are more behaviors worth revisiting once breaking changes are allowed. |
|
The fact that |
I should say it's no more official than any other discussion |
|
I would love to have strict checking on imports and function signatures editing-time. |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

Note that the term technical debt is used loosely here to mean "accumulated broken behavior that can't be fixed without breaking backward-compatibility"; strictly speaking the term has a different, specific meaning.
Update: @rjmholt has officially started a discussion about how to implement and manage breaking changes: #13129.
Note: This issue, which arose out of #5551 (comment), is just to get the discussion started to see if there's a fundamental willingness to entertain such changes. Eventually, RFC(s) are needed.
PowerShell's steadfast commitment to backward compatibility has served the community very well over the years.
On the flip side, the inevitable by-product was the accumulation of technical debt that requires memorizing exceptions and is a barrier to newcomers.
Certain fundamental problems that exist today can only be solved at the expense of backward compatibility, and there are two - not mutually exclusive - ways to handle that:
Implementation of a "PowerShell vZeroTechnicalDebt" edition that sheds all technical debt, but forfeits backward compatibility (possibly with future versions using semantic versioning to indicate compatibility)
Integration of backward-compatibility-breaking features / fixes into the existing code base, available strictly on an opt-in basis only.
Which approaches, if any, are we willing to consider?
Once we have clarity on that, we can flesh out the processes.
Here are some of the existing fundamental problems that can only be solved at the expense of backward compatibility; I'm sure others will think of more:
The complexity and inconsistency of current error handling and problematic [lack of] integration with "native" (external) programs - #3996
Inconsistent, hard-to-predict preference-variable / common-parameter inheritance - #4568
Performance issues due to
[object[]]being the fundamental collection type - #5643 (comment)Problematic dynamic features that should work lexically: #3879 (comment) re
breakandcontinue, https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0003-Lexical-Strict-Mode.md reSet-StrictMode(though the latter may be fixed without breaking compatibility)[psobject]-related problems (though perhaps they can be fixed without breaking compatibility): #5551, #5579, #4343, #5763The unfortunate
-LiteralPath/-Pathsplit - brief rationale at #6714 (comment) and escaping woes at #6714 (comment) (not sure there's a good solution)Broken quoting for external programs - #3734, #5576 (and others) and the related languishing RFC
-Commandand-FileCLI argument parsing - #4024 (comment), #3223 - and general misalignment with the CLI of POSIX-like shells - #3743 - including user profiles getting loaded by default even in non-interactive (script) invocations - #992Inconsistent and surprising parsing of compound command-line arguments - #6467 - and non-parameter tokens that look like parameters - #6291, #6292, and #6360
Broken handling of
ValueFromRemainingArgumentsparameters, as argued in #2035 (comment), with broken behavior shown in #5955 and #5122 - going back to #2038Environment data
Written as of:
PowerShell Core v6.0.2