Skip to content
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

Merged
merged 12 commits into from
Jul 21, 2021
Merged

breaking changes policies #18541

merged 12 commits into from
Jul 21, 2021

Conversation

Araq
Copy link
Member

@Araq Araq commented Jul 20, 2021

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...

arnetheduck and others added 4 commits July 12, 2021 18:00
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.
doc/contributing.rst Outdated Show resolved Hide resolved

Examples of run-time breaking changes:

- Raising exceptions of a new type, compared to what's currently being raised.
Copy link
Member

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

Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

@timotheecour timotheecour Jul 20, 2021

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)

Copy link
Member Author

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.

doc/contributing.rst Outdated Show resolved Hide resolved
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,
Copy link
Member

@timotheecour timotheecour Jul 20, 2021

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.

Copy link
Member Author

@Araq Araq Jul 20, 2021

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.

Copy link
Member

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.

Copy link
Member Author

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.

doc/contributing.rst Outdated Show resolved Hide resolved
Araq and others added 2 commits July 20, 2021 10:47
Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
doc/contributing.rst Outdated Show resolved Hide resolved
doc/contributing.rst Outdated Show resolved Hide resolved
doc/contributing.rst Outdated Show resolved Hide resolved
Araq and others added 2 commits July 20, 2021 11:08
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
doc/contributing.rst Outdated Show resolved Hide resolved
Araq and others added 3 commits July 20, 2021 11:11
Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
Copy link
Contributor

@dom96 dom96 left a 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 :)

doc/contributing.rst Outdated Show resolved Hide resolved
Co-authored-by: Dominik Picheta <dominikpicheta@googlemail.com>
@timotheecour
Copy link
Member

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.

@Araq
Copy link
Member Author

Araq commented Jul 20, 2021

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...

@dom96
Copy link
Contributor

dom96 commented Jul 20, 2021

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.
Copy link
Member

@timotheecour timotheecour Jul 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 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.

Copy link
Member Author

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.

Copy link
Member

@timotheecour timotheecour Jul 20, 2021

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)

Copy link
Member Author

@Araq Araq Jul 21, 2021

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.

@timotheecour
Copy link
Member

timotheecour commented Jul 20, 2021

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...

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

@Araq
Copy link
Member Author

Araq commented Jul 20, 2021

breaking changes are bad but sometimes this is the lesser evil

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.

use important_packages (and grow it) as proxy to assess damage, along with difficulty of patching broken code

Difficulty of patching production code that relies on runtime edge cases: High.

How do I know? I still patch old code that uses s[i] without a s.len check as that used to be valid Nim code.

Claiming "we finally nailed it now" just ignores history...

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.

@Araq
Copy link
Member Author

Araq commented Jul 21, 2021

I'm merging this but follow-up PRs can further refine the rules.

@Araq Araq merged commit f86a530 into devel Jul 21, 2021
@Araq Araq deleted the araq-atd-policy branch July 21, 2021 08:22
PMunch pushed a commit to PMunch/Nim that referenced this pull request Mar 28, 2022
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants