My biggest open source project is Git-plus, which started out as a tiny side project for adding some handy git features to Github’s atom editor when it was still in early beta and invite only. It’s since turned into one of the top favorited and downloaded packages for the editor.
In the beginning, the code was decent and tiny. As more features were added and more people contributed, the quality and consistency decreased. Perhaps the decline in quality could have been avoided if I defined contribution guidelines and enforced TDD. None of that happened and there was a time when I got tired of dealing with issues and patching up a duct-tape covered tool I had created, so I just quit using atom and stopped putting effort into improving it. (I also thought atom was really buggy and frustrating but that’s a separate topic). Right around when atom starting preparing to leave the beta stage with the 1.0.0 release, it was becoming an editor I could use daily and the API was evolving to make development easier. In August, I made it a goal to improve the code quality of my package to make it something I’m proud of. The goal was comprised of 3 parts:
- Write tests
- Add a CI component
- Use promises
This post is a reflection on how I achieved those goals for the Git-plus 5.5.0 release.
Testing and CI
Atom’s testing api is old because it still uses pre-jasmine 2 as the test runner. The real difficulty was in mocking out
the main atom components like
atom.project. I still haven’t figured it out so some of the
tests throw errors as side-effects. Before I started, there were 3 whole tests in the package. Woot woot! right?
No because the majority of the package was made up of 33 features that had no tests and the 3 existing tests were for
3 internal methods that simply found a repository, its directory, and submodules. All other functionality was
thrown together, manually tested, and published to the public. Yes it was painstaking and part of why I lost interest
in working on the package in the first place. I created an issue in the repo
to track my progress. 35 tasks; features that needed testing and then an uncounted amount for what makes the whole thing work
which is this file that
used to be 199 lines of code and is now about 142 thanks to refactoring. As of the time of writing this, there are 107 tests
in the package and 119 assertions. I’d say that’s pretty good for previously only having three tests. I will admit that not
every single piece of this package is tested but there is enough to catch errors up front and reduce the risk of creating new
bugs in old code when making new changes. I set this repo up in Travis CI so I can catch errors with existing code from pull requests as well.
Promises are great and I think they are semantically straightforward. I appreciate the method chaining they allow and they have a haskell-y feel to them when composing multiple promises. The first place I refactored to use a promise was at the root of the entire package.
This method, gitCmd, is at the heart of just about every feature. It spawns a child process to execute our typical git commands and results are acted on through callbacks. It was used like this:
By turning gitCmd into a method returning a promise, the method itself is more concise and the usage is so much cleaner.
Because Promises are composable, features like Git Add All and Push can go from this:
You might have noticed that in the old version of Add all and push,
GitCommit is instantiated with
new. That was terrible
had no state. The old class is too big to show here because it was about 176 lines but the new functional and ‘promising’ (get it?)
version is a whopping 93 lines. The only classes left in this package are either the Atom views like InputView, SelectView, .etc or something that extends an atom view.
If you’re interested in checking out the code back when it was more hideous than it may be today, you can find it here.