NuGet / Home

Repo for NuGet Client issues
Other
1.49k stars 250 forks source link

Feature suggestion: include upgrade refactorings #9544

Open n8allan opened 4 years ago

n8allan commented 4 years ago

It would be nice if renaming and other minor refactorings could be included in nuget packages so that libraries don't get locked into poorly named public symbols in the name of maintaining backwards compatibility.

Essentially, when a library is upgraded, the code changes necessary to support the changes in the library would automatically be applied. At the least, it could handle renames, but perhaps other changes that don't change semantics could be considered too, such as invoking a different method, and such.

donnie-msft commented 4 years ago

This may already be possible with T4 templates and Build Actions.

Managing undo on downgrade would be one concern.

zivkan commented 4 years ago

Keep in mind, if your package doesn't keep the old APIs, then it will break ABI (application binary interface). If you have multiple assemblies that use your package's assembly, as soon as the any one of the assemblies upgrades to the new version of the package with breaking changes, all the other dlls compiled against your old package will throw method not found errors at the app's runtime. Unless the app adds significant complexity to support multiple versions of the same assembly.

Keeping the API and marking it with the ObsoleteAttribute allows you to tell your customers to use the new API, without breaking binary compatability.

nkolev92 commented 4 years ago

Is the ask here for a NuGet capability or something that the package author would need to write themselves?

@n8allan

n8allan commented 4 years ago

I was thinking it would be something that nuget facilitates, but would have to be created by the package author. The thinking is that as a library creator, if you wanted to rename a class, method, or whatever; or move a method elsewhere, you could do so freely without breaking the users. This way the library API doesn't become cemented and the possibilities for innovation and evolution of libraries isn't thwarted. This came up when a library was broken for me and I had to dig through the release notes and source code to figure out how I was to adapt my code.

I realize this would be a rather challenging feature to design properly, and there are probably a million ways for it to go wrong if not properly executed, but if it could be pulled off, it would definitely make Nuget an innovator in package management. What better way to document breaking changes than in terms of refactorings to fix dependencies?

nkolev92 commented 4 years ago

This way the library API doesn't become cemented and the possibilities for innovation and evolution of libraries isn't thwarted. This came up when a library was broken for me and I had to dig through the release notes and source code to figure out how I was to adapt my code.

Note the caveats raised by @zivkan in that case. The .NET ecosystem is such where NuGet flattens, specifically if many different dependency paths request different versions of a package, NuGet will try to resolve the best one.

Keep in mind, if your package doesn't keep the old APIs, then it will break ABI (application binary interface). If you have multiple assemblies that use your package's assembly, as soon as the any one of the assemblies upgrades to the new version of the package with breaking changes, all the other dlls compiled against your old package will throw method not found errors at the app's runtime. Unless the app adds significant complexity to support multiple versions of the same assembly.

For some thoughts on this topic, see https://codeblog.jonskeet.uk/2019/10/25/options-for-nets-versioning-issues/ for example. It does a great job at summarizing the problem, even if it doesn't actively advocate for a specific solution.

I'm not sure that compile time changes would do enough to improve the situation. Specifically in this proposal, we are assuming that all dependencies are compile time.

n8allan commented 4 years ago

I don't disagree with the validity of any of the associated problems y'all raise. One option is to only allow the refactoring changes I mention when transitioning between major versions, but that still has the down-side of not allowing for mutually shared dependencies.

I've been working on a pet language and runtime on the side for years, which involves the ability to explicitly describe the isomorphism between structures. I've been planning to exploit this in the library versioning system so that a library can essentially exist in multiple versions at once. Maybe this idea could be borrowed in .NET in a way not dissimilar to the way a database view works. Essentially, a given assembly could be structurally described in terms of an expression against its predecessor. I realize this kind of thing is far beyond the scope of this project, but I thought I would raise it in case it spurs any ideas, or someone's passion.

nkolev92 commented 4 years ago

Re-reading the comments, I realize that realize that some might sound dismissive, that wasn't our intention :)

My goal is mainly to understand if this conversation is maybe best had in the runtime or any other repository.

Some of things that we've changed in PackageReference is that we do not have an explicit install/update gesture.

Specifcally you can update your package through:

All of them boil down to NuGet just caring for a new state.

Back to the original idea. Maybe an attribute that the analyzers can somehow understand offer refactoring options would be a good idea (all that based on a contract).