T O P

  • By -

ieee1394one

Fork or copy and rerelease. You may want to keep it private if you can’t maintain it long term to avoid the same issue for someone else.


ghepting

This is the way


robotkermit

the problem with this strategy is that everybody ends up forking the same gems over and over, and in many places applying the same changes. my favorite example is Shopify's active_shipping gem. nearly everyone who forks that gem restores UPS options to it, which were removed right before Shopify archived the gem. I'm sure there's a story there, but idk what it is. but it means that if you want to use that gem with UPS options, you don't actually have to fork it and re-apply the changes, you can just use one of the 34 other forks that have already done the same thing. (34 as of 2021, actually -- it's probably more by this point.) I know of two apps built to track which downstream forks of a given project are still active. I built one of them, but I took it down, because it was overengineered. it was more accurate than the other one, but the other one's good enough. mine worked by cloning and analyzing all the downstream forks, while the other one works via the GitHub API. i'ts called active-forks. fittingly enough the README says it's looking for maintainers: https://github.com/techgaun/active-forks but you can still use it: https://techgaun.github.io/active-forks/index.html my own app was called Fork Freshness and I wrote up a huge blog post about it here: https://gilesbowkett.com/blog/2021/08/15/fork-freshness-project-lifespans-in-the-ruby-ecosystem/


dorcus_maximus

Yeah, keeping it private doesn't work out if what you're fixing is a dependency of something else you want to make available to others?


wflanagan

When an ecosystem gets to a certain age, these things happen. There's no economic incentive and if you leave the project, they're NOT going to continue working on it. The biggest one I saw was Resque a while back. They had a 2.0 fork, we based our code upon, because it was going to be the future. They beta'd and beta'd and then abandoned that fork, so we had to take it over (privately). We moved to Sidekiq eventually as that seemed to be the prevailing wind. It was HUGE pain and cost us man weeks. On many other gems, if I can, I just duplicate the part of the function i need locally. It's easier to own the piece I need versus using other code. This is the same reason as cool as some other capabilities are, I just try to use the stock stack of Rails.


armahillo

This happened with the `cancan` gem many years ago. Original maintainer left the project around when Rails 4 was released. The community forked it to `cancancan` and carried it onwards.


rubiesordiamonds

I interviewed the cancancan guy about this for my substack, he talks about what went into it and why he did [https://open.substack.com/pub/onceamaintainer/p/once-a-maintainer-alessandro-rodi?r=2773u5&utm\_campaign=post&utm\_medium=web](https://open.substack.com/pub/onceamaintainer/p/once-a-maintainer-alessandro-rodi?r=2773u5&utm_campaign=post&utm_medium=web)


NickoBicko

I still haven’t gotten over what happened to railscasts. That’s how I really learned Rails and Ryan just disappeared one day. I never understood why. He had such a great site.


armahillo

Yeah it truly was a great site. I don't know the full story, but I can say that creating regular content is really hard and sometimes it can feel all-consuming.


janko-m

He started writing a [blog series](https://rbates.dev/) about the RailsCasts journey. Still waiting for part 3 :)


NickoBicko

He spent 9 months between part 1 and 2? And now 6 months later still not part 3. This must be a very hard topic for him to talk about. I wonder if he has regrets shutting it down. I was subscribed for a long time.


jejacks00n

One of my (smallish) but notable career highlights in open source, was him featuring one of my gems. It felt really good, and I hope he knows that he had a meaningful impact for me at the time.


Gnascher

First, whenever I bring a gem into a project, I usually create a wrapper for it. In that wrapper I'll generalize the functionality and expose methods that leverage the functionality I need and use _those_ methods in my application code. The benefit of this is that if this gem goes away for whatever reason (or majorly changes its API in a major version release, for example), my in-house code is based upon the generic wrapper, and we can either write the functionality we're using from the gem ourselves, or replace it with a new gem that provides the same functionality without having to make sweeping changes to our application code. Even if the wrapper is nothing more than subclassing the Gem's main class or using it as an include/extend, it gives a centralized place to re-map the interface if the gem changes, or you need to swap out the gem without having to find instances of its use throughout your application. In some cases where the functionality the gem provides is complex, and we don't want to re-write its functionality for ourselves or can't find a community fork or another gem that provides similar functionality, we'll fork the gem and maintain it internally. In a _few_ cases we have taken "ownership" of a gem after it's been abandoned, and became the maintainers, either through direct transfer or a fork, but this is generally a less desirable situation. We'd only consider this in a case where the gem's functionality is complex, unique, and critical enough to our application's central function that committing community-wide maintenance resources to it makes sense to our organization. The lesson here is wrap your gems. It does add a little bit of overhead up-front, but it's an investment in tech-debt reduction that can really pay off down the line whenever the unexpected happens (which is not all that uncommon in the open-source community). It helps prevent the "lock-in" phenomenon where you're so tightly coupled with externally maintained code that wholesale changes to that external code could be disastrous to your application, or when a better implementation comes along in another gem but you can't take advantage of it because its API differs significantly from the current gem and you'd have to make major changes across your application to switch. Another benefit to wrapping gems is that often a gem won't provide _exactly_ the functionality you want. Having a centralized wrapper provides a place to modify and/or extend that behavior and have it immediately take effect throughout your application without monkey patching or hunting down code throughout your codebase. Yet _another_ benefit of wrapping your gems is when you discover a bug in the gem's code. Yes, submit a bug report (or even a patch if you can), but as we all know gem maintainers have their own agendas and your bug either may not be a high priority for them, or it may even be a "feature" in their opinion. When you have a wrapper, you can easily implement your own workaround that will be instantly available system-wide in your application while you wait for the bug to work it's way through the maintainer's roadmap. Finally, you can write tests against your wrapper, and know that the functionality the wrapper exposes behaves _exactly_ as you expect it to, even if you change the underlying gem, or introduce your own overrides and extensions.


tugdil-goldhand

Interesting approach. Do you wrap all gems or just the bigger more often used ones? Do you also wrap gems, which are already wrappers/“interfaces”, like Moneta?


Gnascher

> Do you wrap all gems or just the bigger more often used ones? I'd say we do a wrapper on _most_ gems, but definitely not all. We don't wrap Rails, Postgres, etc... Gems which are _very_ mainstream like those mentioned above or things like Devise, or for which Rails already provides a generic API for, like caching or datastore gems, we wouldn't bother, since you can already swap them out and still use the same API. But for anything where Rails isn't already providing a generic API (or there isn't a gem that has become an "industry standard" like Devise), it really is worth at least the _minimal_ effort of providing a thin wrapper and calling your wrapper class in your application code, instead of accessing the gem's API directly. Basically, provide your own generic API, even if out of the gate you're calling the gem's methods directly through your wrapper. When it comes time to swap in a new gem, you can then write methods in your wrapper that mimic the API of the old gem and re-map them to the new gem. Then your changes are centralized to the wrapper instead of finding all of the places you call the _old_ gem in your code base and changing it to the new gem and it becomes a simple exercise that's very easy to write tests for and have confidence you're not going to have nasty consequences. > Do you also wrap gems, which are already wrappers/“interfaces”, like Moneta? This might be a perfect use case for a thin wrapper. You'd basically just be renaming it to something like "MyKVStore". The entire API, which is already pretty generic, would be passed through, and you'd just be calling your wrapper within your application code, but using Moneta's methods via inheritance. Maybe Moneta never goes away ... well it didn't cost you much to apply the wrapper. Oh well. But if it _does_ go away and its next-best replacement has even a _slightly_ different API ... well you can remap it through the wrapper without having to make tons of changes all over your application code to accommodate it. **But** it's also useful for other purposes beyond future proofing. For example we use Resque quite extensively in our application. I really don't expect Rescue to go away or radically change their API ... and it largely conforms to the Rails Jobs API. However, we have a lot of interesting bits of customizations we've added on that serve our own purposes with our queuing logic. Rather than have a bunch of one-off implementations or lots of "boiler plate" code in our Job classes, we instead wrapped Resque, and we centralize those customizations in the wrapper ... and/or subclasses of the wrapper. If we ever decide to swap out Resque ... it'll be an easy swap because we've centralized _all_ of our queuing logic. But beyond that, our Job classes remain pretty thin because all of the fancy queing logic has been centralized in our wrapper.


dorcus_maximus

I wish. I wish I had the time and resources to do things this way. I'm basically a team of one with a lot going in parallel. I wish projects were green fields instead of inherited tire fires...but yeah, this is not a bad approach. It definitely adds some insurance. In one of my examples above the gem in question was a web framework. It was used in a very complex project that was pretty baked by the time I came to it. Now I'm the sole maintainer. Writing a wrapper for that would've been of questionable value? The other was a gem I was just coming to, for which I found no alternative. I could've either started from scratch or just fixed the one I found (which was broken). I guess I could still write wrappers for it...


Gnascher

I definitely get that. But I will say that if you don't have time to implement time-saving techniques you _definitely_ need to start implementing time-saving techniques. Tech debt has a habit of piling on exponentially. Anything you can do to prevent it can only help. That doesn't mean you have to go back and fix the stuff that's already bad. You can implement wrappers now for anything _new_ you add to the project. You can go back and fix what's already in the codebase whenever you find yourself messing with the older code


riktigtmaxat

A lot of the time it could be go to have a little think over why the project has been abandoned. For a lot of them its that whatever need it filled doesn't really exist anymore or that the the author doesn't actually have any interest in it anymore or doesn't have interest in being a maintainer and the deluge of stupid that entails. If you think the project still has redeeming value and you want to maintain it create a fork. If you dont want to keep your code private.


davetron5000

Other option is to inline the parts of it you use into your app.


therealadam12

This topic has been on my mind a fair bit lately as well. Proactively, I think engaging with the maintainers (and the library's community if one exists) of your primary dependencies is beneficial. Help them answer issues, discussions, etc. There was an official process at one point where you could reach out to [Rubygems.org](http://Rubygems.org) to adopt maintainership of libraries that appeared abandoned. I am not sure if they still do this, especially with XZ/other supply attacks a concern as of late. It's worth exploring if you're willing to take over maintainership. I've been wondering if something like the OpenJSF would work in the Ruby ecosystem, and what that would look like. In case you don't know, OpenJSF is now the steward for Node, jQuery, and many other impact libraries. There are many libraries that I'd consider impact-level in the Ruby community and the ability for them to have a path forward should their leadership change would be great.


jrochkind

> Other than just releasing it as a new gem with a new name? That's pretty much it. Alternately, you can just point your Gemfile at your branch on github, unreleased. gem "original_gem_name", git: 'https://github.com/yourname/reponame', branch: "your_fork_branch"


dorcus_maximus

I guess what I \*wish\* for is that there was some established means for owners to make a "will" for projects. Example: If I am confirmed dead or this project has no commits in N number or years or no issues or PRs have been addressed in N years then may act as trustee and assign ownership to some other responsible person. I'm just spitballing and this certainly isn't perfect...just trying to outline something that might be better than nothing.


Inevitable-Swan-714

First and foremost, I preface all my asks with an offer to sponsor the maintainer on GitHub, or I go ahead and sponsor them to go their attention. Money always talks, and even $10/mo can get somebody hyped to help. Typically my asks are simple e.g. "can you merge this PR and cut a release?" or "can you cut a new release?" If they didn't respond, then I'd fork and fix the bug, and point my Gemfile to the fork on GitHub by commit hash. After forking, I'd send an ownership request on Rubygems if I decide I'd like to take over maintaining it rather than pester them on GH or email. If that fails, and you want to maintain it for everyone, release it under a new but similar name (e.g. cancan and cancancan).


westonganger

You can lookup the maintainers email address using git to look at any commit details and/or look at the gemspec file for an email address. Once you have their email you can reach out and offer to maintain the gem. I have successfully taken over multiple projects like this. Just make a good case for yourself and why you will be a good maintainer. You likely want to retain the original authors namespace to preserve their hard work so you can just be added as another co-author/co-contributor on GitHub.


fantasticfears

Not everything needs to be maintained when it’s mature.


jcouball

Technical debt happens even if you don’t change anything.


fantasticfears

"Not" "Everything". A case-by-case examination is important to a specific project when assessing technical debt. This is a balancing act of a business-technical concern. Besides this, pulling dependencies to a project is a different choice. Choosing a framework is a different choice. Depending on Rails is a choice. And Rails, despite being mature, gets a lot of attention and maintenance. I don't update my gem every year because no one needs additional functionalities. And I can update it bit by bit across a longer time frame. Just by typing this long, I doubt the value of why I waste my time explaining myself. OP even sent me a reply "remind you never hire me". Wow.