Hacker News new | ask | show | jobs
by rtpg 1121 days ago
So with libraries that are consumed by third parties, it's rarely about the number of lines of code. Since you don't really have a definitive list of all usages of your code, things like the changes to `_money_from_dict` making things nullable mean you have to consider all ways in which that might blow up.

And like "the public API is the public API, the private API everything goes" is easy to say, but it's so easy to just break other projects with these kinds of changes. This makes it very hard to move forward with certain kinds of "bug fixes" in projects.

That being said, it's not that that PR is "hard", but it's hard to say "merging this is A-OK" instantly. Hours? I dunno, but I would definitely add some tests and try to figure out how to write code that breaks with those changes.

If I were managing that project at the time, and had motivation, I'd definitely do a lot of cherrypicking, get all the "obviously won't break anything" changes merged in, to leave the problematic bits in for a focused review. Again, this all might be under an hour, but sometimes you look at a thing and are like "I don't really want to deal with this, I have my own life to deal with."

At a higher level, the biggest problem with these kinds of libraries is having the single person who can merge things in, who can hit the "release" button. There's a lot of projects that interact with Django that have heavy usage, and survive mainly thanks to random people making forks and adding patches that properly implement support for newer Django. At $OLD_JOB we used a fork of django-money (including a lot of patches to fix stuff like "USD could be ordered with JPY", pure bug generators). It was very easy to add patches because, well, we had our usage and our test suite and no external users. It's great, but it's also important to try and get patches upstreamed when possible (and we did for a lot of projects).

2 comments

Echoing your experience, I too worked on a project that was stuck on angular.js 1.3 when 1.6 was about to be released. We took a look at some migration guides and didn't see that many changes we had to make, so we tried updating it.

Everything broke.

Investigating, I realised that one of my long departed predecessors forked angular-bootstrap and made a few small changes to it. The problem was that the that library was tied to angular.js 1.3. To update angular.js, we had to update the library. To update the library, we had to remove all the changes in which would break large parts of our UI. The project was already in maintenance mode by that time and we decided to just leave it as is. I spent the next month converting it from coffeescript to es6.

I had exactly the same thing last year with a previous long departed staff member having forked django-flex-fields into their personal GitHub and having made substantial changes. Porting to Django 3.2 then became a huge and costly project in itself as a result.
>So with libraries that are consumed by third parties, it's rarely about the number of lines of code. Since you don't really have a definitive list of all usages of your code, things like the changes to `_money_from_dict` making things nullable mean you have to consider all ways in which that might blow up. And like "the public API is the public API, the private API everything goes" is easy to say, but it's so easy to just break other projects with these kinds of changes.

Strongly typed languages, a well-designed API, and senantic versioning can make this problem largely disappear.

I like typing and am a big fan.

If you fix a bug, this can break existing code. This is a fact of life. Changing performance characteristics can generate downstream problems! You have to consider this stuff seriously.

Here the change introduced nullability. In another universe the function would already be nullable but the conditions in which a value becomes None changes. A spec can be changed, for the better, and still be bug generating if people just upgrade. That is not captured by most type systems, and there aren’t that many great production web apps running on Idris.

But ultimately the reality is that people have a lot of flexibility with Python projects in general. It’s great, and libraries that are aware of this, well… they write it in release notes. They also have open repositories to enable actual code diffs. It’s non-zero amounts of work but it’s there.

There is a theoretical universe in which a static language with well-designed libs provide good aesthetics to make developing certain software easy. Meanwhile even as a big functional programming lover I still reach for Python because I can get work done because the libraries are in fact well designed, and the code is easy to work with, and I can fix issues quickly. As a user it’s great, as a library maintainer I gotta apply some more care. Could be better but it's alright

Largely, but not quite all. E.g. there's only so much information about runtime behavior you can capture in types, and then almost no one captures even half of it in practice. Doing this right requires a lot of experience, plus good knowledge of the problem domain, and it tends to both bloat and ossify the code.

It's easier if you're doing version 2 of a well-tested, scope-limited library, and thus can afford to do some holistic design. But the trend in our industry is to develop iteratively, small releases done often, everything kept at 0.x.y perpetual beta, and on the off chance your project grows old enough to warrant 1.x.y version, it has so much evolutionary baggage that you'd have to rewrite it from scratch to get proper typing in (which, of course, is against the zeitgeist).

How so? The API still breaks if it's strongly typed, the breakage is just easier to spot by downstream consumers.

And note that saying "release as a new major version" is not making the problem disappear, it is simply choosing not to solve the problem, but with a warning on top.

That has not been my experience. Nor Hyrum Wright's (of Hyrum's Law fame [1]), nor Randal Munroe's [2].

Note how things like performance characteristics leak through strong-typing, well designed APIs, and semantic versioning, in spite of non-guarantees around such characteristics.

1. https://medium.com/se-101-software-engineering/what-is-the-h.... 2. https://xkcd.com/1172/

One of the stories shared with me while working on OpenJ9 was of a customer that used stack overflows as a scheduling mechanism, and at some point they were compelled to update their JVM to a version that incorporated tail-call optimization, which caused their Rube Goldberg scheduler to stop working (by spinning indefinitely).

I would however argue that the existence of a user relying on behaviour that has explicitly been reserved as subject to change must not preclude development from rendering improvements to products. At some point the consumer needs to be on the hook for relying on a positive externality that they do not have a right to.