-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
breaking changes policies #18541
breaking changes policies #18541
Conversation
In wanting to improve the standard library, it's helpful to have a set of principles and guidelines to lean back on, that show how to introduce such improvements while at the same time considering existing users of the language. A key idea behind documentation of this sort is that it should highlight a path forwards in making changes rather than trying to prevent them, and although the current snippet does include some language for what one shouldn't do, it also shows a few options for what one can do. This is a riff on #18468 mainly based on what helps enable to the use of Nim productively in environments and teams where the priority and focus is not always on the tool (Nim in this case) but rather on the codebase itself, and its use by end users. We use similar guidance documentation to help coordinate the code of the teams using Nim in https://status-im.github.io/nim-style-guide/ where it is applied not as law, but rather as recommendations for the default approach that should be considered first.
|
||
Examples of run-time breaking changes: | ||
|
||
- Raising exceptions of a new type, compared to what's currently being raised. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raising a Defect should be considered an acceptable breaking change when the defect is called for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defect is an annoying things about Nim.
The last thing I need is some library author thinking they know better than I do in regards to what is recoverable. Which is rarely if ever possible for the author to know as per the end to end argument in system design, the consumer of the library almost always has more options at their disposal and an uncatchable exception really stymes that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. So what do you propose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raising a Defect should be considered an acceptable breaking change when the defect is called for
Hardly, as it's a subtle runtime change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raising an exception of a new type is a lesser breaking change than raising a defect, the latter should be scrutinized further.
Anyhow, as it's written is better than what @timotheecour is proposing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just an example, but see what dotnet allows/disallows: https://docs.microsoft.com/en-us/dotnet/core/compatibility/ agrees
✔️ ALLOWED: Throwing an exception that is considered unrecoverable
✔️ ALLOWED: Throwing a new exception in a new code path
The exception must apply only to a new code-path that's executed with new parameter values or state and that can't be executed by existing code that targets the previous version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"in a new code path" is not clear enough and is better left out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the meaning is: if the new exception can only be raised when new optional params are explicitly provided, it's an acceptable breaking change:
# before:
proc fn*(a: int) = discard
# after v1:
proc fn*(a: int, dir = "") =
assert dir.len == 0 or dir.isAbsolute # with a Defect
existing code that uses fn will not break
# after v2:
proc fn*(a: int, dir = "") =
if dir != "" and not dir.isAbsolute: raise newException(ValueError, dir) # with a ValueError
existing code that uses fn will not raise, at worse you get a CT error if user has raises: []
on a caller of fn
(falls under category of CT breaking change, not RT breaking change)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable, but this rule can added later.
reusable Nim code in libraries: In the Nim distribution, critical security and bugfixes, | ||
language changes and community improvements are bundled in a single distribution - it is | ||
difficult to make partial upgrades with only benign changes. When one library depends on | ||
a legacy behavior, it can no longer be used together with another library that does not, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that's the whole point of #18496; it would allow legacy behaviors localized to a scope (eg nimble package); as noted in PR+RFC, this can be generalized to other behaviors:
- override a whole module (that's what's implemented in the PR)
- override a symbol (eg so the old behavior is used within a scope; eg for
system.delete
) - override whether a symbol can raise a new exception (very useful in case original implementation forgot to handle an edge case)
These strategies can work, and be less painful than alternatives.
Bad initial design can also create long term problems, which is why we should consider approaches based on tooling to ease transitions (solutions based on std/os2
have serious drawbacks that can be worse than the breaking change in consideration).
#18513 also helps transition, by allowing to show which lines of code need to be migrated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#18496 is completely unproven at this point. I don't like the idea but even if I liked it, it's impossible to tell the consequences. For example, maybe #18496 triggers dozens of compiler bugs that even though they always already existed, make the feature unusable in practice for the next months and years.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given how unattractive the other options are, we should at least explore this before saying it might trigger compiler bugs. A feature can be useful even if it has limitations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"first opt-in, then opt-out, then removed" is not that unattractive and doesn't need yet another compiler feature. Complexity breeds complexity.
Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well written and makes sense. Looks good :)
Co-authored-by: Dominik Picheta <dominikpicheta@googlemail.com>
A fair test for whether this is a realistic policy would be to assess what percentage of PRs since 1.0 would be acceptable under these guidelines. |
I don't see how that would be the case given that we already know plenty of changes were done not following these policies. Which is not surprising since the policies are new... |
I think we should try these, we can always discuss changse later if we run into problems. |
Examples of changes that are considered non-breaking (or acceptable breaking changes) include: | ||
|
||
* Creating a new module. | ||
* Adding an overload to an already overloaded proc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Adding an overload to an already overloaded proc. | |
* Adding an overload to an already overloaded proc. | |
* Fixing a bug (implementation is wrong according to spec or doc comment) |
stdlib is full of them, cementing bugs for sake of stability at the stage of maturity where nim is a bad trade-off. The alternative is introducing things like std/os2
whose drawbacks have been extensively discussed in previous attempts at defining a policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"according to spec or doc comment" is a real bug, "doesn't work like in Bash/Python/D" is not a bug, it's just something that annoys you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, i wrote wrote according to spec or doc comment
. If a module implements a json API (eg std/json), producing invalid json is a bug according to an external spec, so #18026 should remain an acceptable breaking change
path handling is another typical case. if C:foo\bar
is currently incorrectly treated as absolute; fixing this should be considered an acceptable breaking change; ditto with C:\
which is currently normalized as C:
instead of C:\
which is incorrect (becomes relative)
(refs https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't accept your proposed wording: Code out there is not written against the spec, it's written against the existing implementation. The bugfixes you describe are all about changing the runtime behavior in subtle ways. These fixes must be opt-in first, then opt-out and then we can eventually remove the code that keeps opt-out from working. It's more work on our part but it helps our users and doesn't imply an os2.nim module.
and yet we've managed to exercise common sense each time a breaking change was knowingly introduced, relying on practical matters (how much impact on important_packages sample, how difficult is it to make code fwd/backward compatible) over other considerations. nim in devel is considerably better than 1.4, and likewise for each prior release, thanks to, not in spite of, the breaking changes/bug fixes introduced. Claiming "we finally nailed it now" just ignores history and the same promises made in 1.0 (no breaking change apart from security related + experimental parts of language) will have to be broken again. All I'm arguing for is exercising common sense:
Lots of people also want bugfixes even when it implies breaking changes, see reactions to #18468 (comment) or to #18480 |
Subtle changes in run-time behavior are not the "lesser evil". Even os2 (which I didn't bring up this time btw) would be the lesser evil than that as we could design a solid API for it first.
Difficulty of patching production code that relies on runtime edge cases: High. How do I know? I still patch old code that uses
I don't claim that. I claim that these policies will work better than what we use today, yes. How will we know for sure? By trying it. |
I'm merging this but follow-up PRs can further refine the rules. |
* document some std library evolution policies In wanting to improve the standard library, it's helpful to have a set of principles and guidelines to lean back on, that show how to introduce such improvements while at the same time considering existing users of the language. A key idea behind documentation of this sort is that it should highlight a path forwards in making changes rather than trying to prevent them, and although the current snippet does include some language for what one shouldn't do, it also shows a few options for what one can do. This is a riff on nim-lang#18468 mainly based on what helps enable to the use of Nim productively in environments and teams where the priority and focus is not always on the tool (Nim in this case) but rather on the codebase itself, and its use by end users. We use similar guidance documentation to help coordinate the code of the teams using Nim in https://status-im.github.io/nim-style-guide/ where it is applied not as law, but rather as recommendations for the default approach that should be considered first. * document the new policies * typo * Update doc/contributing.rst Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update doc/contributing.rst * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/contributing.rst * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * clarify some things * Update doc/contributing.rst Co-authored-by: Dominik Picheta <dominikpicheta@googlemail.com> Co-authored-by: Jacek Sieka <arnetheduck@gmail.com> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Dominik Picheta <dominikpicheta@googlemail.com>
Note: Constructive critique is most welcome but if there continues to be no agreement, I might use my BDFL powers and push for a solution...