-
Notifications
You must be signed in to change notification settings - Fork 762
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
DO NOT MERGE! experimental split of UI-framework-specific parts out of main packages #2034
base: main
Are you sure you want to change the base?
DO NOT MERGE! experimental split of UI-framework-specific parts out of main packages #2034
Conversation
@idg10 this looks great, thanks for starting this! I have to say I am a bit concerned with the decision to create a new package The current package ecosystem is already very confusing, having suffered several breaking changes in the past including package renaming, package splitting, package re-unifying, changing strong name signatures, etc. I think this was ultimately damaging to the Rx.NET community. I agree we really need to clean-up, but somehow introducing yet another new package into the mix just feels like it will exacerbate the problem even further. There is an existing I would usually be the first to defend full backwards-compatibility, but unfortunately the current state of Rx.NET packages is completely untenable. Continuing to maintain a meta-package As proposed in #1847 we would then introduce new platform-specific packages, e.g. I understand the concerns with backwards compatibility, but legacy apps can always lock their package versions to an earlier major release of the package. If the app is not legacy, then they can usually update in a few minutes since the API surface area is exactly the same except for a few edge cases. Indeed making it break by stripping |
I would very much prefer not to introduce a new package. If I can possibly find an acceptable way not to do it, then I will avoid it. So I agree with your concern. However... Actually before I start discussing this, I've realised that I left out one quite important file from this draft PR, because it was on a different branch. I've just pushed f49ebda, so if you look now at this: you'll see the various factors I'm aware of that make this challenging. That doc is a bit of a mess right now because it has changed a few times as I've tried various experiments, but it does at least collect together a lot of the issues that mean this isn't straightforward. I'm still working on it and am hoping to get it better resolved soon. So... with this "let's unbreak Avalonia" project, my initial goal was to retain The fundamental problem occurs here if a single application has two dependencies (which might be indirect, non-obvious dependencies) of this form:
LibA will require LibB will require Now think about this proposed future in which Rx 7 has a But what does that mean for LibA? It will expect So can If you can think of some way that The only way to make it proper clean break is to declare that anyone in the LibA+LibB scenario described is out of luck. But a problem with that is that it gives library authors a strong incentive not to upgrade to Rx 7 just in case they end up being the root cause of that kind of problem. So that also doesn't really feel like a clean break—we're still dealing with the legacy of the problem. This was the appeal of introducing a whole new component: that really can be a clean break. So at this point I am pretty sure that I did consider resurrecting That's a lot of downloads. Apparently people still have systems that were written in an era where it definitely mattered that these particular components work the way they do. There are a lot of people with legacy systems frozen in time for one reason or another. (In the last few months I've helped a customer with a project where they just migrated off VB6!) So we do need to bear in mind what might happen to someone who is still depending on a thing that was done in Rx v4 if they end up having an upgrade to Rx v7 forced on them by some dependency they can't control. There's one particular aspect of If That's a radical change for a component which for the last 7 years has been a backcompat package in which the version numbers have been a) fixed, and b) weird in specific ways to solve particular problems. My worry is that by changing what this component is, legacy systems that are still today relying on how this weird version numbering fixes a problem for certain plug-in architectures will find that they are in trouble if something forces Rx onto v7. My real worry here is that I have absolutely no idea what the consequences of reinstating
As it currently stands, we will break UWP backcompat. But nothing else. I'm a lot more comfortable with breaking UWP backcompat than I am with breaking .NET FX backcompat. (Also, the gains from ditching UWP are huge: its presence causes us an unbelievable amount of grief.) The number of downloads of the UWP-specific Rx packages are 1,000x lower than the numbers for the backcompat shims in Rx v4. So I don't currently agree with the idea that because there's a breaking change, we can break much more. The numbers suggest that UWP is used so much less that the impact of a breaking change there is acceptable.
I wholeheartedly agree with this. Your proposal—breaking backwards compatibility for the main Rx package—is radical. So I think you agree that radical change is required. This is why I'm prepared to contemplate yet another change to "Will the real Rx package please stand up?" much as I don't want to do it. Unfortunately, while I massively prefer an end state in which However, I would LOVE to be convinced otherwise. I really don't want to do what I currently think I'll have to do. |
To some extent this boils down to this debate
A potential option 3 is that someone proposes a way of achieving 1 without it being gnarly. |
@idg10 thank you for your thoughtful review of the situation as always. I am well aware of these pains. My most important Rx project is stuck in v2.2.5 ever since we got the short end of the stick when Rx changed strong name signature when moving to .NET foundation. All our dependencies structure got permanently broken and no amount of manifest tweaks or redirects can deal with a strong name key change. However, we have recently found a way to move on that may be applicable here. Essentially we decided to target v2.2.5 for all projects targeting net472 and v4 for projects targeting net5. This was a good solution that allowed us to overlay incompatible dependency graphs in a way that both preserves all our legacy ecosystem and allows people to move forward while being compatible. I was hoping it would not be too late to do the same for Rx.NET. Essentially the best chance to do a clean break is .NET 6/core. I agree with your assessment that NET FX projects are the ones most stuck with legacy code that cannot be updated. This is where I would draw the line of back compatibility, since .NET core ecosystem is much more dynamic and used to dealing with changes. So I propose the following: ditch UWP as you propose and keep NET FX UI schedulers on System.Reactive only for net472 targets. For net6 forward, remove the schedulers and ask people to move on and include the new platform-specific packages. Re. The backcompat facades like Core, etc, I would simply stop updating them to encourage people who can to move on. The goal of these packages should be to resolve back compatibility issues and not for apps actively being maintained. Microsoft .NET with all its gigantic ecosystem was able to make the decision for a clean break for the sake of supporting sustainable evolution of their ecosystem, my proposal is that we take that opportunity and go along with the ride. |
To further generalize the above proposal, if you happen to consider it too late to make .NET 6 the cutoff point, the exact same idea could be applied to the upcoming .NET 8, or future releases. My argument for why this is always appropriate is twofold:
I would still recommend picking an LTS release like .NET 6 or .NET 8 to do the clean break, since those are usually much more likely to be adopted by reusable library maintainers. |
I believe it would be interesting to receive feedback regarding this change from the opensource developers who are directly working with Rx. If this gets merged, it will affect RxUI, ReactiveProperty etc. |
This will break .NET 6.0. .NET 7.0, and .NET 8.0 apps that find themselves in the situation I outlined in my earlier post. I'll copy the relevant part here:
A brand new .NET 8.0 application written today could find itself in this situation shortly after Rx 7.0 shipped. Since Rx 7.0 doesn't exist yet, any version of The problem here is not confined to .NET FX. In fact it doesn't really affect .NET FX at all. The problem here is very much specific to modern .NET. I don't yet understand how the "clean break" opportunity you're seeing could work. The basic problem here is that versions get unified: the build system is going to pick exactly one version of If we are truly to have a clean break here, there has to be a change of package identity, with |
Since I'd like the discussion on this to have higher visibility, I've created this: #2038 |
@idg10 thanks for creating the discussion, I will continue there, but just to close I would like to add one clarification:
If we make the cutoff point .NET 8.0 for example, then apps made in .NET 6.0 and .NET 7.0 would not be affected, unless I am fundamentally misunderstanding something about NuGet packaging (which is a genuine possibility).
This actually to me seems like an example of what we would want. Any "brand new" application written today should not be using legacy packages. If they need to for whatever reason, and those packages aren't upgraded, then they should not target .NET 8.0 and should pick an earlier compatible target. |
I have created a response with my thoughts in 2038, thanks Ian. |
This is an experiment showing one way Rx could solve the problems described in AvaloniaUI/Avalonia#9549
With these changes, programs can take a dependency on a newly-introduced
System.Reactive.Base
component. (Not currently available on NuGet—only in this experimental branch.) This is effectively exactly the same API surface area as you got withSystem.Reactive
. The difference is that it won't bring in references to WPF or Windows Forms just because you happen to target anet6.0-windows.10.0.19041
(or later) TFM.System.Reactive
still exists (and still does the same thing to you) but it's now just a backcompat facade. This essentially moves all ofSystem.Reactive
into the newSystem.Reactive.Base
, except for the bits that were causing problems.Note that there is one breaking change: if you target UWP,
System.Reactive
now gives you the sameThreadPoolScheduler
as all the other platforms get. If you were relying on the UWP-specific features, you need to switch to the newWindowsRuntimeThreadPoolScheduler
, which provides the exact same functionality that used to be available on the oldThreadPoolScheduler
. This was the only way we could fully push all of the UI-specific functionality out of the core of Rx and into UI-framework-specific components. (In all other cases we could just relocate whole types. This was the one case where a type available in thenetstandard2.0
andnet6.0
targets also existed in another target but with a different API.) Since use of UWP has been discouraged for some time, and because Rx 5.0 and 6.0 continue to work just fine, our view is that this particular breaking change is tolerable, particularly given the problems it solves for everyone else.