Live data from Hacker News

Hyrum’s Law in Golang

abenezer.org

101–110 of 190 posts

Re: Hyrum’s Law in Golang

#101
Perhaps some package authors are more accepting of this than others. I stumbled upon this comment in the `json` package the other day:

// isValidNumber reports whether s is a valid JSON number literal. // // isValidNumber should be an internal detail, // but widely used packages access it using linkname. // Notable members of the hall of shame include: // - github.com/bytedance/sonic

Re: Hyrum’s Law in Golang

#102

Earlier quoted context omitted.

> You will be forced to lug this broken behavior with you forever Yep, welcome to my life.

Wouldn't a nil ECDSA key be a security risk?

If a private key is available, the public key can be derived from the private key using scalar multiplication. This is how ecdsa.GenerateKey works by itself - it first generates a private key from the provided random byte stream and then derives a public key from that private key.

I don't see how this can be a security risk, but allowing a public key that has a curve but a nil value is definitely a messy API.

Re: Hyrum’s Law in Golang

#103
post #74

Hyrum's Law is one of those observations that's certainly useful, but be careful not to fixate on it and draw the wrong conclusions. Consider that even the total runtime of a function is an observable property, which means that optimizing a function to make it faster is a breaking change (what if suddenly one of your queues clears too fast and triggers a deadlock??), despite the fact that 99.99999999% of your users w…

I think everything you said is totally correct for open source library owners. But let me offer a different perspective: Hyrum’s law is neither a technical contract nor a social contract. It’s an emergent technical property in a sufficiently used system. How you respond to that emergent property depends on the social context. If you are a FOSS maintainer, and an optimization speeds up 99.99% of users and requires 0.0…

> It’s an emergent ... property in a sufficiently used system

This is also a sufficient description of "social contract" for this context.

Re: Hyrum’s Law in Golang

#104
Another related effect of this is Protocol Ossification [0] which happens when implementers of a public API/Protocol surface area take implicit dependencies on common but not standardized behaviors of the API/Protocol implementation.

That being said, you can take proactive steps to defeat this. For example, the default Hash for strings in .NET is randomly seeded each time a process starts[1] in order to strongly dissuade folks from taking an implicit dependency on the underlying algorithm which is not guaranteed to be stable

[0] : https://en.wikipedia.org/wiki/Protocol_ossification

[1] : https://andrewlock.net/why-is-string-gethashcode-different-e...

Re: Hyrum’s Law in Golang

#105

Earlier quoted context omitted.

Semantic versioning does nothing to help here. If you don't realize that people are depending on such a behavior, you won't increment the major version number.

And if you realize it (as in this case) you probably don't want to increase the major version number either, but leave it as-is (unless you follow the CADT model of maintainership).

I think a very nice middle ground is when you decide to remove something, to mark it as deprecated in the next major version, and remove it in the one after. Not always possible, but IIRC React does this; so I'd frequently upgrade, then start seeing deprecation warning messages (in dev); I'd then have a clear signal before upgrading to the next version. It helped that major versions did not arrive often so making this kind of change was only occasionally necessary.

A bit trickier in this case no doubt; and trade offs. Ive not minded the React updates over the years, but busting out the Go code I wrote many years ago and having it still run flawlessly is amazing too.

Re: Hyrum’s Law in Golang

#106

Earlier quoted context omitted.

Go has typed errors, it just didn't use it in this case.

In principle. In practice, most Go code, and even significant parts of the Go standard library, return arbitrary error strings. And error returning functions never return anything more specific than `error` (you could count the exceptions in the top 20 Go codebases on your fingers, most likely). Returning non-specific exceptions is virtually encouraged by the standard library (if you return an error struct, you run i…

You do not have to do more work to use errors.Is or errors.As. They work out of the box in most cases just fine. For example:

    package example

    var ErrValue = errors.New("stringly")

    type ErrType struct {
        Code    int
        Message string
    }
    func (e ErrType) Error() string {
        return fmt.Sprintf("%s (%d)", e.Message, e.Code)
    }
You can now use errors.Is with a target of ErrValue and errors.As with a target of *ErrType. No extra methods are needed.

However, you can't compare ErrValue to another errors.New("stringly") by design (under the hood, errors.New returns a pointer, and errors.Is uses simple equality). If you want pure value semantics, use your own type instead.

There are Is and As interfaces that you can implement, but you rarely need to implement them. You can use the type system (subtyping, value vs. pointer method receivers) to control comparability in most cases instead. The only time to break out custom implementations of Is or As is when you want semantic equality to differ from ==, such as making two ErrType values match if just their Code fields match.

The one special case that the average developer should be aware of is unwrapping the cause of custom errors. If you do your own error wrapping (which is itself rarely necessary, thanks to the %w specifier on fmt.Errorf), then you need to provide an Unwrap method (returning either an error or a slice of errors).

Re: Hyrum’s Law in Golang

#107

Earlier quoted context omitted.

The map iteration order change helps to avoid breaking changes in future, by preventing reliance on any specific ordering, but when the change was made it was breaking for anything that was relying on the previous ordering behaviour. IMO this is a worthwhile tradeoff. I use Go a lot and love the strong backwards compatibility, but I would happily accept a (slightly) higher rate of breaking changes if it meant greater…

Data point of one, but I've been using Go since 2012 and would drop it instantly if any of the backwards compatibility guarantees were relaxed. Having bugs imposed on you from outside your project is a waste of time to deal with and there are dozens of other languages you can pick from if you enjoy that time sink. Most of them give you greater capabilities as the balance. Go's stability is a core feature and compensa…

Respectfully, I don’t think you would just pack up and leave. The cost of switching to an entirely different language—which might have even worse backwards compatibility issues—is significantly higher than fixing bugs you inadvertently introduced due to prior invalid assumptions.

I’d call your bluff.

Re: Hyrum’s Law in Golang

#108

Sure... but this is why we have sem versioning and release notes. It's always nice to try and support all users but sometimes you just need to ship breaking changes...

Hyrum's law is specifically about how every change is a breaking change if you have enough users. So it's always a bit subjective. No sane person considers changing an error message a breaking change in context of semver. It's just go going above and beyond to take care of backward compatibility.

Re: Hyrum’s Law in Golang

#109
post #8

This is a good example of "stringly typed" software. Golang designers did not want exceptions (still have them with panic/recover), but untyped errors are evil. On the other hand, how would one process typed errors without pattern matching? Because "catch" in most languages is a [rudimentary] pattern matching. https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...

Go has typed errors, it just didn't use it in this case.

Go errors cannot be string-typed, since they need to implement the error interface. The reason testing error types sometimes won't work is that the error types themselves may be private to the package where they are defined or that the error is just a generic error created by errors.New().

In this case the Error has an easy-to-check public type (*MaxBytesError) and the documentation clearly indicates that. But that has not always been the case. The original sin is that the API returned a generic error and the only way to test that error was to use a string comparison.

This is an important context to have when you need to make balanced decisions about Hyrum's law. As some commentators already mentioned, you should be wary of taking the extreme version of the law, which suggest that every single observable behavior of the API becomes part of the API itself and needs to be preserved. If you follow this extreme version, every error or exception message in every language must be left be left unchanged forever. But most client code doesn't just go around happily comparing exception messages to strings if there is another method to detect the exception.

Re: Hyrum’s Law in Golang

#110
At one job, I found a misspelling in an error message and fixing it only to discover that the web of dependencies on that misspelled text was so deep that it was impractical to fix and had to return to the misspelled text. It still bugs me.
Post reply on HN