Stop talking, build something, then start to talk again
In 2013, while I was coding some background jobs, I realized that I had performed the same particular task of setting up Delayed Job or Resque on almost all the Ruby/Rails apps our team had worked on. Moreover, each time we'd had to migrate from a queuing library to another our background jobs needed a partial rewrite to make them compliant with the underlying implementation.
I decided to tackle that problem by writing a small gem, Jobz, the aim of which was to provide a uniform API to enqueue asynchronous jobs. The basic idea was to allow one to mark any class or module as deferrable, making all of its methods potentially asynchronous. I've made
ActiveRecord::Base deferrable by default, so that one may easily turn any method call on a model in an asynchronous one:
# Regular call user.deliver_confirmation_instructions! # Async call user.async(:high).deliver_confirmation_instructions!
I've also made the
async method chainable so that a schedule can be specified easily:
user.async(:normal).in(8.hours).wake_up! # or user.async(:critical).at('2016-01-01'.to_datetime). say(:happy_new_year)
I didn't want to have to serialize the model identifiers and method call arguments manually on the app side, and I didn't considered efficient to have to write code to lookup for models at run time. So I wrote a simple metadata handler for ActiveRecord to make the whole thing transparent. This way the users of the library would only have to care about the actual implementation of the methods they'd like to call asynchronously.
I created a tiny "API" for adapters and implemented a concrete one for Resque (and Resque Scheduler). As it fitted our immediate needs, I placed the implementations for DelayedJob and Sidekiq in a todo list - and instantly forgot about them. To make programming in the development environment easier and unit testing of jobs a seamless experience, I wrote an alternative inline adapter which basically shortcuts the queuing and performs the method calls synchronously.
Unless for a few fixes and an additional handler for Mongoid, I've left the things as is. I've never implemented the adapters for the other queuing gems, nor did I take the few minutes required to add basic usage instructions into the README. As we've used the gem from a git repository using bundler, I haven't even bothered to push the gem to Rubygems.
Almost a year later, David Heinemeier Hansson wrote a blog post to announce the beta version of Rails 4.2.0. As usual, there were a lot of great improvements and cool new features. The headline particulary drew my attention:
Active Job is an adapter layer on top of queuing systems like Resque, Delayed Job, Sidekiq, and more. You can write your jobs to Active Job, and they'll run on all these queues with no changes.
With an always-configured queue in place (though the default is just an inline runner)
The cherry on top is our new GlobalID library. It makes it easy to pass Active Record objects to jobs by serializing them in a generic form. This means you no longer have to manually pack and unpack your Active Records by passing ids
Jobz pales in comparison to ActiveJob. The latter is well integrated with Rails and provides a lot more features. It is way more versatile and secure, has a cleaner and better codebase, and it will benefit of the incredible support of the Rails core team and community. I've already moved some of our apps to ActiveJob and I've planned to migrate others in the short term. All of that is to say I don't think Jobz could be a viable alternative to ActiveJob. But still, Jobz had existed for a year before ActiveJob kicked in, it solved, at least partially, a problem people were seemingly experiencing. Both the libraries have been built on the same core value proposition, they're actually different solutions to the same problem.
When I read the announcement, a question struck me: why the hell didn't I let other people know Jobz existed in the first place?
A lot of people out there put emphasis on the fact that doing things is much better than to talk about them, that ideas worth nothing while products magically carry some value. I think that products only carry potential value which becomes actual value through direct or indirect conversation. When you're publishing something - be it a blog post, a documentation, an improvement of a README file, even a tweet - you're actually placing a touch point, an opportunity for other people to hear about the value you're trying to deliver. Maybe they'll ignore the message. But they also may give it a go and create a relationship with you through the product. Then they may provide some feedback, request new features, contribute to it, or even create their own alternative; the stuff you created may begin to deliver actual value instead of progressively erode its own potential.
Shyness, self-doubt, lack of time, there are a lot of reasons to avoid talking about what you've created. They're all bad ones. I had done the hard part: I had created something and had made it available. But somehow I "decided" not to start that conversation, I guess because I thought the gem was too small and imperfect to find buyers. That was plainly stupid.
I stopped talking and built something useful. Then I should have started to talk again.