When should we consider a proposed library "good enough"?

An observation, inspired by but not limited to the better-files discussion: we should be careful about “making the best the enemy of the good” thinking. Specifically, we’ve got a couple of proposed integrations that are being argued against because they (possibly) aren’t the ideal we would like. That’s a valid line of argument, but IMO is a lot more valid when it’s paired with, “and here’s a better alternative”. (Or even a concrete plan for somebody to build a better alternative.)

I can definitely sympathize with the desire not to put the imprimatur of “standard” on something when we can see the conceptual flaws. That said, the Platform is not going to be perfect, and we have to be somewhat pragmatic about what the definition of “good enough” is. If something could be significantly better, but that “better” doesn’t yet exist and the bird in hand is a considerable improvement over the existing APIs, is that “good enough”?

I’d strongly recommend we think about this explicitly. What should be the rough guidelines for “good enough”? If we have a proposal for adding a library, and we can see the outlines of the library we would prefer, but nobody’s volunteered to write it yet, what should we do? This is really a strategic decision, and a moderately important one. Is it better for the Platform to include the library (which we anticipate is likely to eventually be superceded by something more appropriate, but makes life better in the meantime), or leave it out (leaving the community pain point, and maybe inspiring somebody to actually write and contribute something better)?

I should note that I’m raising this as a discussion topic precisely because I don’t even know my own feelings here – I can argue both sides of this one. But it seems that we’ll have more productive discussions going forward if we have can come to at least a rough consensus on our philosophy here…

Thanks for bringing up this interesting discussion @jducoeur.

I cannot express how much I agree with this statement.

I think we should consider all the libraries that exist in the present and meet the most important criteria of a concrete problem domain. As I mentioned before, we need to turn our hopes/requirements into actions that help us improve what we have right now. There’s always room for improvement. My rule of thumb is to convert any suggestion into an actionable item that I (or others) can work on. This is what moves us forward.

Modules are not meant to be the same for years. They are meant to evolve over time, as the users’ requirements and use cases change. If a module is accepted into the Scala Platform, it means that for 10 Committee members that solution is the best among the current ones. But our job is not done yet, it’s just started! If we’re at home and think that a library could be improved, and we have some time, why don’t we do so? If we don’t have time, why don’t we open an issue and propose an improvement or draft a plan?

With this, I mean that the future of the modules is in the maintainers/contributors hands. Disagreements are settled with PRs and discussions. If there is a problem with a module or a feature is highly requested, someone can get it done. It’s not as in the case of the standard library, where things will not be fixed because of strict compatibility guarantees, unoptimized contributing experience and slow release periods. The case that you mention “likely to eventually be superceded” will not apply, we don’t need to supersede a library, we just need to evolve it.

To me, a proposed SP modules is eligible when it fits the official Scala Platform module definition:

A Platform module is a library of the Scala Platform. Platform modules should stress stability and usability alike, and enjoy widespread use in the Scala community. Modules should be of a nature that aids the goal of the Scala Platform and should have compatible licenses with the rest of the Platform modules.

As you see, stability, usability and widespread use are major features of SP modules. How do we measure them, though? The idea is to get data that backs up statements like “this library is stable”, “this library is widely used”. The better-files proposal is a good example of a proposal because it gives us objective data to assess the current status of the library. This methodology is not full-proof, but hopefully help us to be more productive and focused at the discussions.

This bit I disagree with – I don’t think that consistently matches reality. The thing is, we’re trying to solve problems here – the specific libraries aren’t really the end-goal, providing robust solutions to common problems is. And in some cases, evolving an existing library just doesn’t work – providing a better solution requires starting again from scratch.

This isn’t unusual: for instance, in the better-files discussion, folks have talked about how the ideal library might have a significantly different viewpoint, being monadic from the get-go and like that. We might be able to evolve the existing library in that direction, but I certainly wouldn’t count on it. (Among other things, evolving is often much, much harder than rewriting from scratch.)

So we should take this into account in our decision-making process. If somebody does eventually come up with another library that solves the same problem domain in what we think is a better way, what do we do? Do we add the second library into the Platform, risking bulk and confusion over time? Do we deprecate and remove the older library? Do we ignore the new library, on the grounds that we’ve got too many people invested in the old one?

I actually think this is a tricky long-term problem, and one that we should think about fairly carefully. Maintenance is always the hard bit…

This statement is debatable. If this eventually happens and another library with better features than the one in the Platform is stable and production-ready, then we can study to supersede it. But this is not our priority. In fact, we already prevent this from happening because we require a stable and widely used library to be added to the library in the first place.

So, if I understand you correctly, you’re saying that we should take into account how something could be in the future to decide the current acceptance of a library? Things could be wonderful. But there are always tradeoffs. We should be pragmatic, take the best library among the current alternatives and improve it. This strategy pays off in the end and it’s the only one that helps us move forward.

On an important note, the Scala Platform focus is on stability. If a new library with more features and equally stable and battle-tested is to be added to the Platform --and the community, as well with the SP Committee, agrees–, then the old library will be deprecated. Hopefully, we’ll be able to provide a migration tool that helps converting the old library into the new one.

Creating a plan for this situation is essential, thanks again for bringing it up. But your thesis that we should take into account the future state of a library for modules’ acceptance seems wrong to me. Especially because that future library could not even be realizable.

Mind, I don’t really have a thesis here. I’m just musing (largely off the top of my head) on a problem we face, and your emphasis on stability kind of underscores my point: the more we care about stability of the platform, the harder it’s going to be to change in the face of new-and-better techniques arising.

I would bet good money that we will sometimes wind up in local minima, where evolving to the desired endpoint that everybody wants proves to simply be harder than it’s worth, and nobody is willing to go to the pain of spending multiple years slowly mutating an API into something fundamentally different while still providing a compatibility path. So instead, somebody writes something new and better, and we wind up torn – as we currently often are – between something Old and Weak in the “standard” and something better outside of it.

That’s the heart of my point. I think you’re being a little too blithe about assuming that the libraries in the Platform will evolve to something better; that often eventually fails, especially when the changes are deeply structural. So what do we do when the audience is leaving our “official” solution, and using something else instead? I suspect we’re going to have to replace the “official” solution, and we ought to be prepared for that to happen from time to time.

And getting back to the point: I think we have to be prepared to replace core recommended libraries from time to time if we’re going to have an answer to the “is it good enough?” question. If we are saying “this is The One True Library For File Processing, forever”, then it’s logical for us to argue endlessly about whether it’s good enough for us to commit to it like that – the stakes become much higher. I suspect that we ought to be thinking now about our procedures for gradually replacing recommended libraries in the face of something better coming along, in order to lower those stakes to something manageable, so that our timeframe can be “the next couple of years” instead of “forever”…

2 Likes

There is another way besides evolving i.e. building on top. In this case, instead of “evolving” better-files, keep it as is and acknowledge that its just a nice way to use Java NIO. Basically abstract away “pain to deal with Java” into better-files and have a TBD library deal with providing pure monadic interfaces built on top of better-files?

A real example:

  1. We want an Akka based file watcher service
  2. But first we need to get a prototype working sanely. Java NIO’s WatchService is insanely complicated and full of pitfalls
  3. So we use better-files to abstract away Java’s idiocracy and wrap it in an intuitive API
  4. Once we have an sane wrapper around Java NIO, we can build the Akka one.
2 Likes

I suspect sometimes people arguing against have not proposed an alternative because they are the author of an alternative, and they wanted to avoid turning a discussion of a particular facet into an unfocused contest between the two libraries.

I think another takeaway is, instead of having proposals like “add library X to SPP” (usually by its author), we should have a proposal “add functionality X to SPP, but we have to choose between libraries A, B, and C.” The choice of libraries shouldn’t be subject to which author steps forward first, but should be based on a robust discussion about the pros and cons of the different libraries.

Of course there are counterarguments to that, but I’ll let those who disagree make them. :wink:

1 Like

instead of evolving “better-files”, keep it as is and pretend its just a nice way to use Java NIO and use a library on top of better-files that is pure and monadic and does not have to deal with interacting with Java IO headaches

Yes. The correct way to expose a foreign API is to first provide a low-level mapping (ideally 1:1) that does nothing more than speak in terms of Scala types, then use this as the basis for further abstraction. Abstracting too early leads to APIs that lack the expressive power of what they’re wrapping, which leads to functional gaps that are hard to fix.

So I think the better-files approach as a NIO layer and nothing more is exactly right.

Yep, and sometimes that works fine – that’s why I’m specifically trying not to focus on better-files here. In some ways, this is more motivated by the ongoing evolution of the Scala.js world, where there are several wildly incompatible frameworks duking it out. Six months ago, I might have said that scala-js-react was the clear winner there (itself only a year or two after scala-js-jquery was the only way to go), but I have a suspicion that @ochron’s Suzaku library is going to make a serious splash. It’s an example of the way that “the best answer” can change very seriously, and sometimes fairly rapidly.

Essentially, the Platform project is about picking winners. I’m enough of an armchair economist to be intrinsically a little suspicious of that, and the library world evolves fast enough that I suspect we are going to need some flexibility to adjust to the times.

(Possibly I’m incorrect, and that would be fine. But there’s enough precedent to concern me.)

1 Like

I’d argue that if a library is “good enough”, it’s “good enough” regardless of if overlaps functionally with another. This leaves a door open for future, better implementations, and a nice long deprecation cycle for libraries that folks are unwilling to continue to maintain.

Given the stability goals of the platform, I’d argue that “good enough” should generally mean “people are using it in the wild and happy with it so we are ready to stabilize the API” rather than "let’s incubate it and consider reworking it heavily while we do so.

This is in fact how it works. For the Committee to review a proposal, first someone has to propose a library to solve functionality X with a library Y. This proposal is the one that takes precedence. Then the Committee takes this proposal and identifies the problem at hand, analyze whether it’s one the Platform should provide a solution for, find other alternatives and assess them all based on the aforementioned module criteria.

However, this process requires someone to show interest into adding X into the Platform – otherwise the Community would not actively propose the inclusion of libraries but rather wait on the Committee to decide which use cases are worth submitting a proposal for. And I think this would be the wrong approach.

I also agree this is the way to go :tada:.

The flexibility is provided by the compatibility guarantees. They allow us to remove a library and replace it in every major release of the Scala Platform, which was set to 18 months but we discussed that it would be reasonable to use instead 12.

Things are dynamic and change over time. Software is not an exception. Stability is a great concern but, at the same time, I get your point that we should react to the development of other better alternatives developed later to the acceptance of a module.

However, we should encourage that changes are done in the Platform modules (i.e. Platform modules are evolved) rather than outsourcing its development into external repositories whose changes are not to be merged back. This is possible when changes are not over intrusive – which happens most of the time in stable libraries, to be honest. I would argue that comparing scala-js-react with an IO library is not fair, the first one requires a research component that is not inherent to the IO domain. IO libraries have been for a long time in our hard drives, but react libraries have not.

I think we should be prepared for the situation that @jducoeur depicts to happen. In that case, I suggest that we carry out the deprecation cycle that we’ve been talking about (+ potential automatic migration). But it’s important to note that most of changes can be worked out by all the Scala developers in the same module. And it’s important that modules are actively maintained because both our personal code and companies’ code will depend on it. We should join forces if possible.

Scenario

Library Y is objectively better than library X, which has been in the Scala Platform for two years.

Solution

  1. Someone opens a Discourse thread to supersede library X with Y. This thread provides verifiable information that shows Y is superior than X, enjoys stability and a remarkable userbase. Feedback from current users has to be good.
  2. Members from the Community voice their opinions (ideally people that depend on X). Committee members meet and discuss. We find tradeoffs and agree about which bits should be supported in Y and which shouldn’t (remember that library Y may decide to add/drop support for features that were not/were in X). All these decisions should be explained.
  3. When we achieve consensus and agree that X should be replaced, we come up with a plan. The SPP Process Lead helps to create it. This plan includes:
    3.1. The deprecation of module X.
    3.2. The details of the replacement of module X by module Y in the next major Platform release (complying with the current compatibility guarantees).
    3.3. The creation of docs/explanations to help the migration.
    3.4. A potential automatic migration tool that helps users to convert bits of library X into Y (when possible).

This is what I propose to happen. I’m sure this can be improved! Any suggestion?

Sorry, let me rephrase, my point is not about the proposal process. I mean perhaps before an actual proposal, there should be a discussion that starts with all potential “contestants” (libraries) on equal footing. IOW the outcome shouldn’t be biased by who initiated the discussion. The “entry point” should be a discussion that considers all the viable candidates.

Oh, thanks for rephrasing that, I clearly missed your point. The problem with this approach is that discussion could derail and end up discussing libraries that will never be proponents to the Scala Platform. For instance, you could say that library X is better than Y, but if someone doesn’t submit a proposal and get some maintainers for its inclusion, the process cannot go on.

I wouldn’t say that we “bias” the discussion. The Scala Platform process encourages other library authors to submit their libraries even if there are other proposals in discussion. But to do it efficiently, it’s good to have first the confirmation of the Commmittee than a solution is required for need N and then ask library authors to submit more proposals for N. The proposal that ranks the higher in the Committee’s evaluation of the libraries will be picked. Details here.

Not quite sure how to interpret “remarkable userbase”, but in general this seems like an appropriate outline…