PowerShell / PowerShell Public
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
What is the right way to implement deprecation in PowerShell? #11674
Comments
|
Thanks @mklement0! Copying my comment here so I can mark the one in the other thread as off topic.
It definitely can break things (I know there is a difference between something breaking the change contract and a change technically breaking a thing, but bear with me): function Test-Something {
[CmdletBinding()]
param()
end {
if ($theWorldIsEnding) {
$PSCmdlet.WriteWarning('some bad stuff is happening')
}
}
}This is part of why I don't really like the warning system at all. Most of the things I would want to warn about are better off as a non-terminating error anyway. If something is writing a warning, I'm either silencing it, or throwing on it. If I'm throwing on it with I know that if it's considered a breaking change than that makes the attribute useless, but that's how it feels currently. Personally I think
That's true up until the point where something reads that metadata and changes behavior based on it. Adding |
|
Simply emitting a new warning message can also break systems that detect success/failure based on stdout/err. For example, script based application detection methods in SCCM. The SCCM client will invoke a PowerShell script and use stdout/err to determine if the subject application was installed correctly. Here's a table (from About custom script detection methods): Zero exit code
Non-zero exit code
So since warning counts as stdout, if a detection method uses a command that has been deprecated, all of those applications will start reporting as if they are already installed. |
|
GitHub search results:
I still believe that the transfer of the system to a new version involves testing. I don’t think anyone can easily jump from 5.1 version to 7.0. To migrate a large system like SCCM MSFT team and consumers must test all used scripts. For warning messages we could play with preference level to ignore such messages in scripts and show in console session. |
That's honestly way more than I expected to see. I would have guessed like low 300s.
Well yeah for sure. I've supported many breaking changes in the past and will continue to do so - when the benefit outweighs the risk. You could argue (and I'd find it hard to disagree) that the risk is pretty small for adding a warning. That said, it's often discussed as zero risk, like it's an interactive only change. The risk is definitely higher than zero, and in my opinion higher than should be acceptable for the simple act of marking something as obsolete. I'm mainly suggesting that a design time warning should be sufficient. |
|
A new The SCCM scenario is not likely an issue. |
That's a cool idea, but a lot of work. Problems with that approach aside, they're realistically not going to do that.
When I say stdout, I'm not referring to PowerShell's output stream. I'm referring to the console process standard output. Anything that writes to the console window and isn't classified as an error message is written to standard output. Open up powershell -nop -noni -c "Write-Warning visible" > test.logIn the file will be "WARNING: visible". |
I was too, but I see Another option is to just write to the console only and not implement as a full stream. I don't believe the old |
The issue was that it wasn't going through a PowerShell controlled stream. It did still write to stdout though. |
|
To illustrate that with a quick example: The following C:\>pwsh -nop -c "write-host hi; [console]::writeline('ho')" > NULAs an aside: That all streams, including the error stream, go to stdout is, of course, a problem, but changing that would be a breaking change: #7989 (comment)
I agree; in order to implement that we'd have to do the following (I can't speak to the technical feasibility):
|
|
I see the discussion is only about"Runtime warnings". I think that the SCCM example is not successful, because SCCM starts an external process. Sometimes it seems to me that it has no design at all :-). Since PowerShell merges everything into one output stream, this will always be a problem, regardless of whether there are warnings. This mechanism is, by definition, completely unreliable. Now we should look how deprecation is used. We have 2 main scenario - user console session and application session (I mean all without user interaction). For applications the warnings are pointless. Main concern in the discussion is that warnings can break something. I believe we are thinking not in right direction because besides this minimal risk there are many, many changes that represent a real risk. |
This is a separate problem, which can be tackled via the planned separate PowerShell executable for not loading
This is not just about SCCM. It is unrealistic and unreasonable to expect all external (build, CI / CD, ....) tooling to host PowerShell, so the problem will persist, even with the above fix in place. Aside from that, even a host that checks streams separately could be thrown off by the appearance of new warnings.
Agreed, in interactively submitted individual commands (as opposed to scripts) that print to the host it would be helpful for the engine to surface deprecation warnings, along with helpful links.
Interesting - I suggest you create a new issue for that. But if I interpret your comments correctly, you agree that there is no need for runtime deprecation warnings for unattended (update: and captured / redirected) execution, correct? |
I find it useful for users in interactive session and pointless for scenarios like task scheduler (here we could only write a log event and telemetry if we need). |
|
One option would be to only write the warning when |
|
As a IT admin, if I see 'deprecated' in any way/shape/form you finally decide on, I am going to expect to see 6 different alternatives/examples in help showing me why it is deprecated and what are my options to change to the New/Right/Approved way. Everything you guys don't like about PowerShell, you have to tell me why and how to change to the New Way. |
We could create an internal method for this and use it in Send-MailMessage and Invoke-Expression BeginProcessing(). |
Forgot about invoke expression. That one definitely shouldn't warn interactively since that's the only scenario it makes sense to use it for. And tbh what I proposed would be pretty hard to reason about for newer folks. I'm back to thinking a design time warning is the right route. |
|
Tagged the @PowerShell/powershell-committee so that they can discuss this, since I think they need to be aware of our desire to standardise deprecation in PowerShell on a method that is discoverable but is unlikely to break code. |
|
Thank you for the breakdown here, @mklement0. You've articulated really well our difficulty in being anything but tactical and reactionary when it comes to deprecated (or "pseudo-deprecated") components. Before we get too far along on the discussion, can we start enumerating all the deprecated, almost-deprecated, or "wish we could have deprecated before we broke" features/components from PS 5.1 to 7? Off the top of my head:
|
|
Some thoughts I want to throw carelessly into the ether:
I think @mklement0 enumerated the options for deprecation well (but everyone please feel free to add new ideas for communicating deprecation). Two thoughts on that:
So in terms of what it would take to resolve this issue in my mind, I would list the following:
|
|
re: terminology re: signaling deprecation/obsolescence PowerShell Core follows the Modern Lifecycle Policy, which requires upgrades after 6 months (and patch installs after 30 days). The vehicle for these Cmdlet changes is already in place and active. Use it! Windows PowerShell instead falls back onto the OS lifecycle. Want a stable platform for your scripts to run for years unimpeded? Use a long-term servicing branch of Windows and Windows PowerShell. |
Yeah I agree. Just to clarify, when I suggest that obsolescence should be a design time warning, what I'm suggesting is:
What is and isn't deprecated is still dictated by the author, PSSA just delivers the news. |
The issue I see here is that this relies on the PowerShell version analysing the script (which is something I don't like about PSSA in general). Perhaps that's not the end of the world, or even a good thing in this case. But my preference would be for PSSA to have a catalogue of deprecated invocations and us keeping that up to date.
Those are very low level APIs; there's really no mechanism to decorate them for deprecation (at least not one that wouldn't slow some APIs down by orders or magnitude). Conversely, the .NET APIs, which PowerShell is more directly built on, emit compile-time warnings for every static use of an API with an I don't like emitting warnings at runtime for deprecated APIs though. PowerShell's "compile time" is essentially at runtime (or at least, isn't dependable enough a stage to put warnings at). I still don't fully agree with the breaking argument -- warnings are differentiated from errors in that they are ignorable (open the dev console in any webpage and see how many warnings show up), they are also configurable and redirectable, I'm also wary that most users aren't using PSScriptAnalyzer or really all that much in the way of tooling. But I suspect that tooling and documentation is the best answer we have here. To get to that point though, PSSA is going to need some investment, both in terms of making it a better tool (which is planned to be worked on but will take time and could always use help) and also in terms of helping PowerShell users discover, configure and use it. I also suspect that in documentation, we should have a one-stop-shop for enumerating deprecated APIs so that they are really discoverable. Also, nobody's said otherwise, but I feel like I should add that in terms of SDK/development experience, the |
After mentioning this briefly to @joeyaiello, he raised the valid point that a reasonable proportion of users may never even use PowerShell non-interactively. While I think tooling works well for scripts to be deployed and run non-interactively, it doesn't provide a mechanism to let users know that the commands they're using or habits they're building are deprecated. Getting past runtime warnings here is hard. A possible alternative might be in PSReadLine (like if it coloured deprecated commands differently or put a warning in the completion info section), but that's really spitballing. The other problem with not emitting runtime warnings is that statically analysing for deprecated invocations isn't always possible. It's entirely possible to use a deprecated API in a way that a tool analysing the AST (be it PSSA or PSReadLine) wouldn't pick up (but which the engine always catches). |
|
Throwing in some data: PowerShell had a similar predicament with unapproved verbs and made them opt-in by default / added a flag to hide them. So there's emission precedent. |
|
I currently use Having PSSA identify the use of an However we implement deprecation in PowerShell, it would be nice if module authors can also make use of it. |
I'm not sure how we would even mark this type of thing as deprecated. |
|
I'm for never showing a message interactively. Just tried Send-MailMessage in PowerShell 7rc3 and got the news that it is obsolete, but gives me no guidance on what to use instead. This isn't something I ever want consumers on my modules to see interlaced with other output. It's a bad experience and doesn't even offer a solution. Seeing it when I'm writing something is a lot more useful as I see it as the developer and not as a consumer/user. As a developer I can plan/understand that I will eventually have to replace it with something else. |
# Suppress warnings emitted by redirecting the warning stream
Send-MailMessage -From me@outlook.com -To you@outlook.com 3>$null
I believe the obsolescence message is the following:
Unfortunately this is a hard problem to address in a scripting language with no compile step. Many script invocations are not made by developers but in an interactive scenario, in which case PSScriptAnalyzer won't address the need to inform PowerShell users that the command they just used is something they should reconsider. In places like reddit and Twitter, users still ask about PSSnapIns and WMI cmdlets in the context of PowerShell 7 — communicating obsolescence is hard.
|
|
@rjmholt, re @FireInWinter, re:
I can't speak to the technical feasibility (@SeeminglyScience has hinted at a solution), but to me it makes sense to show the warning in direct interactive use (only), e.g., if you submit To turn the warning off, you could use As @rjmholt notes, without a compilation step we cannot really ensure that scripters will see the warning, but being vocal about the recommended tooling (PSSA, directly or implicitly via VSCode) would help. Of course, someone who only ever invokes scripts / module functions / cmdlets would then never get to see the warnings, but I think it's fair to expect scripters (command authors) to take responsibility for deploying updated commands that avoid the deprecated features. This could be complemented with an opt-in mechanism (preference variable) to always show deprecation warnings at runtime, which would address @KevinMarquette's scenario, though not by default; similarly, it addresses the limitations of static analysis that PSSA can provide, as noted by @rjmholt. Additionally, a centralized online deprecation "registry", as mentioned by @rjmholt, sounds like a great idea, especially for the wholesale removals / fundamental breaking changes mentioned by @joeyaiello , such as the removal of workflows and the change in default character encoding. Re terminology: While not really formalized, the term deprecated - not obsolete - has historically been used in PowerShell (e.g., for the
Why it is not recommended anymore, which could be one of several reasons, should be detailed in the specific messages - as I agree with @rjmholt that trying to formalize degrees of deprecation would introduce too much complexity; as @joeyaiello and @rjmholt have noted, some features effectively have gone away, notably in the transition to PS Core, but that's the exception, from what I understand. Use of the |
|
@PowerShell/powershell-committee discussed this and we agree the current approach is inadequate and needs a consistent design. We'll follow-up with individual comments/ideas. |
I do understand that, but that is the same as saying "deployed code should never become obsolete". Eventually features, libraries, platforms can become obsolete. You might deploy a security library today and have it become obsolete in the future. What's more, the obsolescence warning will only appear if PowerShell itself is updated, at which point the deployment should be re-evaluated for compatibility. |
|
It may make sense to introduce a |
|
That implies a whole other stream, though, which is currently not true. I don't think we need to increase the potential confusing factors here. |
|
That was my thought too, @vexx32, but we also proposed that a NoteProperty on the Warning stream might be better. Then they can be squashed with all other warnings, queried on in logs, etc. @BrucePay also raised that a good lens to look through in the future is along the lines of "consumers" (e.g. interactive users and script executors) as opposed to "developers / authors" who are building modules and scripts they need to be well maintained over a period of time. |
|
For the author, we could make a message visible with For the consumer, for the first major version after it's actually gone, throw a specific error to communicate why their script is broken instead of the standard parse error. This also helps authors that run preview versions to discover it early. If all that doesn't get the authors attention, then it's unlikely they would have changed it before it breaks. We can't say we didn't give our best effort. As far as implementation details, having an attribute that can be used on functions, parameters, variables, classes (and their methods and properties) that's available in script modules gives authors the ability to utilize this feature. It would be good if the attribute on the functions,classes, (or modules?) can specify properties and members that have been removed. I prefer the attributes but as an alternate implementation, use something in the module manifest to indicate what's on the way out or already gone so it's handled appropriately and gives other authors a single place to look in a module to see all these settings in one place. This would help |
|
One thing I want to clarify as we think through this is that we should really scope the conversation to notifying the user of deprecation, rather than actually building a path to actually removing things from the engine. I was very convinced by @BrucePay's argument offline that we really can't ever remove anything from PS7 given it's LTS nature and stability long-term. One idea I had today (inspired by some of @JamesWTruher's suggestions) is that we could create a parallel to experimental features called "deprecated features". They'd be on by default to keep breaking changes from occurring, but with Get/Enable/Disable-DeprecatedFeature sets, you'd get:
I'm planning on writing up a specific issue for just this idea, but I wanted to float it here first to move the ball forward on getting it shot down ;) |
|
I think long term, we actually have an unanswerable need to be able to break things that used to work in order to build better things. Otherwise, we simply end up with mountains upon mountains of technical dept, half-written features, semi-supported use cases that never actually worked properly, just "well enough" for some limited usage, etc., etc., etc. If breaking changes/deprecation of features is never going to be OK, PowerShell will eventually just become another bloated tool that does too many things not well enough, and will ultimately be superceded by more effective and streamlined tools. Part of the reason semver even exists is to help developers communicate that across some version jumps, things may break. It's a tool we should be OK with using. Nobody is forcing folks to move to new versions until they're ready to do so; at some level, we need to be willing to remove old code, or the project as a whole will gradually become effectively impossible to maintain. Just my 2 cents. |
|
To my mind we need a process that allows breaking changes and API removal. That's related to deprecation but isn't the same thing. Deprecation is just a warning system, and it might warn about breaking changes or it might warn about other things. I think to break things the right way, we need:
Today we don't really have any of these, other than possibly experimental features for the first, and I think it would require a fair amount of work to get them. If we decided to take it on, we'd need to find the time to invest in it. I should also say that on the edge of development there is always a bias for moving forward, but as a shell, a programming language and a platform, PowerShell needs to be mindful of the full lifecycle of changes it introduces, and should look around to see how other software we compare ourselves to behaves. How often and in what way does bash, zsh, C#, .NET or Python take a breaking change? As @joeyaiello says though, conversation about breaking things should be moved to a new issue. Deprecation is a warning system. |
|
I wonder how many TMA we are talking about? Can we take the SDK and classify all the APIs to understand this? Also, if we are only talking about warnings, it’s enough to set the obsolete attribute and add XML comment. |
|
Moving the conversation about how we make breaking changes to #13129 |
|
.NET has written up an interesting document about obsoletion which might be worth comparing to our discussions here |
|
@PowerShell/powershell-committee discussed this today. Although no conclusion, we did agree on a few things:
We believe this feature requires a RFC to be authored |
|
We could do not complicate and simply continue to use Experimental Features but have "Deprecate" in an experimental feature name. |
Follow-up from #11662 (comment):
Context:
Note: Deprecation in the context of PowerShell is always "soft" deprecation in the sense that, given the commitment to backward compatibility, features are never removed, so that old code continues to function. Instead, deprecation means discouraging use of a given feature, for various reasons: having been superseded by a superior alternative, general obsoleteness, security concerns.
Deprecation can be implemented in the following, potentially complementary ways:
Documentation warnings: Clearly state that a given cmdlet / one of its parameters is deprecated and should not be used in new code.
Design-time warnings: Implement a PSScriptAnalyzer (PSSA) warning that is ideally also surfaced in Visual Studio Code, via the PowerShell extension's integrated PowerShell Editor Services (PSES).
This is currently mostly not implemented (see details below).
Runtime warnings:
Currently, a warning (to stream
3) is emitted when a deprecated feature is called.The latter are currently implemented by a repurposing of the general
System.ObsoleteAttributeattribute, whose purpose is to provide compile-time warnings (and optionally errors) about obsolete features: given that PowerShell code isn't compiled, the PowerShell engine explicitly surfacesObsoleteattributes on cmdlets or their parameters (but seemingly not SDK classes with the attribute) with a PowerShell warning at runtime.Examples of features currently deprecated this way are
Send-MailMessageand the-Rawparameter of theFormat-Hexcmdlet; e.g.:3>$nullor-WarningAction Ignorecan be used to suppress the warning, but old code written pre-deprecation will show the warning.As an aside: The combination of the boilerplate part of the warning (
Parameter 'Raw' is obsolete) with the attribute-specific message (Raw parameter is deprecated) could use some revising.Relevant questions are:
Should deprecation be signaled at runtime at all?
Is it sufficient to warn at design time, knowing that not every user will see those warnings, given that they may use an editor other than Visual Studio Code and not install and routinely run
Invoke-ScriptAnalyzerafter installing thePSScriptAnalyzermodule?As of this writing, PSSA (warning) rules seems to comprise just one relating to one specific deprecated feature,
AvoidUsingDeprecatedManifestFields, whereas several more deprecated features exist - full list of rules is here.Perhaps a more promising approach is to let PSSA directly surface
Obsoleteattributes encountered in code, if technically feasible.The text was updated successfully, but these errors were encountered: