Digging Out: A Tale of Open Source Neglect

Back in 2007 when I first announced my masked input plugin, I had no idea what I was doing. It was early days in jQuery and still very early in my career. I was just trying to give back a little bit for all that I had been given from open source software. I had no idea that over 7 years later I would still be working on this project. At the time I needed this, I built it to suit just what I needed and put it out there. That's how this OSS thing works. right?

I'm here to say it worked, but quite by accident. At the time jQuery had just recently released 1.1 and soon thereafter they created a plugin repository and I put the plugin up there. Then, something happened. jQuery became massively popular and I pretty much rode the success of that project. I built a plugin that some people needed and it worked with the new JavaScript library. It was right there in the plugin repo and people downloaded it. The last time I had any kind of metrics was version 1.2.2 when I was still hosting the project on google code. You can still go there and see the counts, which at the time of this writing is sitting at 2.8 million downloads between the minified and full source files. After that I moved to github and currently the project currently has almost 1200 stars.

Why am I telling you all of this? Is this a humblebrag?
I'm not telling you this to brag at all. I need you to understand the scale of it all - it's a clue to the source of much of my anxiety. I'm still shocked when I see those numbers because I have no idea how it happened. No, I'm telling you this because it's time to tell a story of what happens when something gets bigger than you and how I'm handling it.

Grab a snack, this might take a while.
I didn't quite realize this going in, but it takes a lot to maintain a project long term.  This is only magnified by the number of people actually using it. Those people are using this as part of a product they are building and each has their own unique requirements. Given my role in this, I feel obligated to provide a certain level of quality. Unfortunately, if I were to build in every feature that was requested, then I would end up with a kitchen sink project. I'm sure you've discovered these in the wild. They have a ton of options and go out of their way to accomodate everyone while sacrificing usability/performance/stability for the majority. In the world of web library development, each new feature comes with a large future cost in effort to maintain it.

There are a lot of browsers out there to test on. Each one broken in it's own special way. At present I test on desktop OSs: IE 8-11, Chrome, Firefox, Safari. Then there are the mobile browsers... Oh, the hell that mobile browsers cause. I wrote this before there were even smartphones. Now I'm trying to make sure it works at least on iOS and Android.  It's safe to say I take pause when a new feature request comes through. That's a lot of work to make it work everywhere given that I have to contend with weird stuff like this.

At this point, it's worth sharing with you that I have a really hard time telling people "no". I have such a hard time with it, that for a while I just didn't. Issues suggesting features and pull request came across my inbox. I wasn't 100% happy with them, so I would just ignore them. Deep down I knew that ignoring them was bad, but somehow I was just hoping that it would all go away. Of course you know how these things go - problems won't magically resolve themselves. So when legitimate issues popped up with bugs, I continued to ignore them because of the other open issues before them. Several times I tried to get back into it only to get upset about the state of the code. Everything was just in deadlock.

A lot of events had happened around this time in my life and it was all overwhelming. Really though, there are always going to be distractions from free work. It's funny, since I've started this project I've had two children, sold one house, purchased another, changed jobs 2 times voluntarily, and got dumped on my ass once because an employer didn't play nice with the IRS. This product has been wearing on me for a while and the words you're reading are basically what my close friends and coworkers have been hearing from me for years.

About 4 years ago I put out a bit of an SOS. That was the first upswing in series of akward cycles of disgust, acceptance, small effort, tiny release, finally ending with denial induced procrastination - "I just did a thing, you should be happy with the thing. I'll pick this up in a few months." Each cycle I would get maybe 1/4 of what needed to be done completed and then fall off. This is how I ended up with nearly 90 issues out there. Ninety. Issues. The project only has 430 lines of code, how can it have 90 issues?!

This is open source and it's all out in the, ahem, open. There sat 90 fingers pointing at me for the world to see. This guy is failing. Every time I would open up my issue list, there was more. For one person it felt pretty unsurmountable. At this point I can just hear you saying, "It's open source, you don't have to do it alone. You should have just accepted the pull requests and moved on." The problem lies above where I mentioned that I felt obligated to meet some self perceived level of quality. Very little of the code being sent my way was meeting that imaginary quality bar. In reality, the problem was me. I had failed to provide any feedback. If something wasn't right then I should have responded and tried to set some expectations about what I wanted the final outcome to be. Instead, I just let the valuable feedback and code sit there and rot.

It's silly for me to expect someone else to play an open source game with me when I haven't told them the rules. Heck, I didn't even know the rules. I just knew when the solution didn't feel right or complete. You can't run a project like that. It doesn't work and I think I've done a good job at proving that. I was about rock bottom mentally on this project and the way I saw it, I had 3 options:

  1. Call it quits - Just throw my hands up and walk away. Put a note on the README letting everyone know that this project is dead. I seriously considered this one, but I felt like leaving when the project was in such terrible shape would be a huge disservice to the many existing users.
  2. Hand off the project - There was already a couple of well established forks out there, so this never felt useful. The people I would have handed it off to were already well into building their vision of this widget. When I looked at the forks I was impressed with the stuff they had done, but it didn't feel like something I would want to use.
  3. Suck it up - Go through the long process of wading through the issues and pull requests and try to make sense out of the madness.

It wasn't until I was on the other side of the pull request that how this process goes finally clicked. I wanted to add a small feature to elixir and stumbled on their excellent document on contributions. There it was, everything I needed to know about getting my code accepted. I knew exactly what was expected of me and I followed the rules laid out in that document. Within no time my pull request had been accepted and that issue was closed and gone. Huh, so that's how it should work. That was a smooth process.

With the help of Jared and the encouragement from several friends, I decided to take option #3. It took a while and I stalled a few times, but we went through each issue; triaged it; set up a plan of attack; slowly worked through the bugs, feature requests, and pull requests; and finally released version 1.4. It was hard because I had to say "no" several times. It was hard because I had to respond to several year old issues and say "I'm sorry this has taken so long." It was hard because I pretty much wanted to be doing anything but staring at the mess I had created.

Now What?
Inspired by the contributing document I mentioned above, I've borrowed heavily from it and added my own contributing document to the project. I'm working to make it easier to have an answer when a feature is requested or a a pull request is sent over. Had I established these guidelines early on, most of these issues would have been as effortless as my experience with the elixir project. There are still some outstanding issues and I'm hopeful we can move those forward and resolve them too. Most of all, I'm hopeful that I can get this project to where I can stay on top of the requests without a massive amount of effort. If not, well there's still option #1; at least I'll know I tried everything I could to keep it going.

For those of you putting a project out there for others to use, don't let any of this scare you. You won't have to worry about any of this up front; just build something awesome and share it. If people start using the thing you've built and want to give back, take some time to really think about the direction you want the project to go. Put that into words and make sure everyone knows what to expect. They might not like your rules, but at least they'll know up front if they want to play.

 

Masked Input 1.3.1

A long overdue release of my Masked Input Plugin is available. This is a bugfix release that adresses some of the bigger issues out there:

  • jQuery 1.9 compatibility.
  • Fixed browser lockup when window loses focus.
  • Android issues.
  • No longer preventing event bubbling.
  • Making sure we call completed handler when pasting a value.
  • Fixed bug trying to set caret on hidden elements.
  • Fixed cursor positioning bug related to bounds check.

You can see everything in detail over at the 1.3.1 milestone on github.

I had planned to roll in more bugfixes, but jQuery 1.9 releasing forced my hand a bit. :) If you see any problems, please report them as a github issue. I also managed to get this published in the newly revamped jQuery plugins site. Thank you to everyone who reported issues and to those who submitted patches.

 

Masked Input Plugin 1.3

There is now a new version of my Masked Input Plugin for jQuery.  This is primarily a bugfix release. The biggest deal for me with this release has been the addition of a test suite.  In order to do so, I ended up writing a keystroke simulator which I call keymasher. It's not a perfect way to test, but it's better than nothing.

As a product of adding the tests, I found a few inconsistent issues with the delete and backspace handling which are now resolved. I was also able to take advantage of some event normalization that jQuery provides which didn't exist when I first wrote this plugin.  That plus a switch to UglifyJS has resulted in a smaller compressed file size (3.26KB for v1.3 vs 3.46KB for v1.2.2).

Bugfixes:

  • Fixed completed callback bug.
  • Fixed IE bug requiring charAt() instead of array notation to access char within string.
  • Fixed delete key handling with cursor at literal character.
  • Fixed infinite focus loop bug with multiple masked inputs on a page.
  • Fixed raw value returning mask placeholders when input empty.

Enhancements:

  • Now gracefully handle it when mask() gets called multiple times by calling unmask() on behalf of the user.

As always, if you have encounter any issues, please feel free to report them over on my github project. I'll do my best to  fix them in a timely manner. Also, if this plugin has helped you out, feel free to throw a few bucks my way by clicking the donate button at the top of the page.

State of the Masked Input Plugin

This post should be titled, "Dude, Where the Hell have You Been?" I'm sorry I have ignored this project for so long.

New Home
First, let me get started by saying that I moved the source to github a while back.  It now lives here. If you look at the commit history, I moved it there months ago, fixed a few outstanding bugs, added a feature that someone needed for a specific project and then just left it. I'm picking it back up somewhat, but we'll talk about that in a minute.

Email Bankruptcy
There have been a TON of emails from you guys; so many that I haven't been able to keep up.  Most of the emails have been about two bugs: an off by one goof I made for the completed function and forgetting to use charAt() to access a char in a string. These bugs are fixed in the github repo right now (I think). I'm calling my email situation a total loss.  Sorry to those that have emailed me and not gotten a response.

Birth of a New Project
Part of the reason I stalled on this project was a lack of tests.  For a while I wasn't sure how to even test this thing given that it is purely driven from user input.  Up to this point I had just been opening up a test page in every browser I could think of and running through a few things manually.  A couple of weeks ago I sat down one night and spiked out a rough version of a keystroke simulator which I'm now calling KeyMasher. Please be kind, it's still very rough. Once I get it more polished, I'll put up an official project page on my blog here.  I had found a few other projects which do this and jquery.autotype seemed to be the closest fit. Unfortunately I couldn't get it to work with my specific needs, so I've now written my own with a syntax I feel more comfortable with.  I've already worked out a few tests using this against my masked input plugin.

I'm Just One Guy
After I get a half way acceptable set of tests around it, then I can feel a bit more confident about what I change.  I would like to be able to implement some of the features I've seen come across my email. It will take some time to get everything to a place where it should have already been.  Please be patient, I'll get there. :)

Masked Input Plugin 1.2.2

There is now a new version of my Masked Input Plugin for jQuery.  This is primarily a bugfix release which addresses some edge cases.  Additionaly, I made a few changes that I feel make the plugin behave more natural so that the user experience isn't affected too much by using the plugin.
A few other things worth noting:  This is my first build for this plugin that uses a build script.  No longer am I compressing the javascript by hand.  I'm hoping to extend the script further so that future releases are easier.  Also, I'm now compressing the script with YUI Compressor.
This release has been tested with jQuery 1.3.2 and 1.2.6.
Bugfixes
  • Fixed bug which blocked apple meta key.  This was keeping copy and paste via keyboard shortcut from working on Mac.
  • Fixed bug that caused mask literals to be pushed into the mask placeholder positions when verifying the data.
  • Fixed bug that prevented user input from completing when mask ended in mask literal.

Changes

  • Changed behavior on focus to select all text if focusing on a completed mask.
  • No more masking on readonly inputs.
  • Changed escape behavior to put the input back to the original value instead of just blanking the text.
  • Increased range of accepted characters for input.

Next Page »