WalkerCodeRanger.com

Month: June 2015

June 24, 2015

Advice for Open-Source Projects

Recently, I’ve been comparing JavaScript build tools and their plug-ins. At the same time, I’ve been checking out static site generators like Jekyll, Hexo, Middleman, Metalsmith, DocPad, Champ, Assemble and others. For a more complete list, see StaticGen.com and this survey of .NET static site generators. However, I don’t really want to talk about static site generators today.

What I want to talk about is all the things that go into an open-source project besides the idea and the code. Now of course, one should have a cool idea for an open-source project and someone is going to have to write the code. I absolutely care deeply about quality, clean code. Yet with an open-source project there is a lot of other stuff that goes into it. Often times, it is all that other stuff that makes an open-source project great and gets people to use it. Now I can’t promise you that if you do all these things people will use your project, but it will certainly help.

All Open-Source Projects

There were a couple things I saw that we all need to do better at, regardless of whether one manages a massive project with hundreds of contributors or just wrote some code in a hour and posted it on GitHub.

Clearly Indicate the Status of the Project

The most important thing you can do with any code you post online is clearly say at the top of the read me or other most visible place, the status of your project. This should answer questions about the current state of the code and about the likely future path of development. Here are some ideas for statuses one might use. Though a couple sentences of explanation in addition will always be helpful.

To describe the state of the code:

  • Works for Me or No Warranty: Indicates this is some code you threw together, it solved your problem or met your goal, but there has been no effort spent making sure it is suitable for others to use.
  • Sample, Example or Starting Point: The code demonstrates how to do something, but shouldn’t be taken as production code. It should probably be incorporated into other projects as their own code that they will own and further develop in the future.
  • Pre-release or Beta: The project has gone through multiple rounds of active development with a goal of reaching a stable release version, but is not there yet.
  • Stable/Release: The project has reached a stable point where there should be a minimum of bugs and the necessary features are present. Use this even if there is a previous stable release, but you are working on a new version that is in beta.

To describe the future path of development:

  • Active (with Date): This indicates people are currently actively contributing and addressing issues and questions on a semi-regular basis. The current date should always be included with an active status and updated at least twice a year so that people know it is true and not just a statement that hasn’t been updated correctly.
  • Inactive: The project is not currently being maintained, but there are still people who would notice if someone reported a horrible bug or submitted a pull request. No guarantee is made that they would be addressed though they would at least get a reply.
  • Hiatus: The project is currently inactive, but there is an intention that it will return to an active status in the future. An idea of the time-frame is helpful here.
  • Ignored: This code is not being maintained, issues, questions, bugs and pull requests will not be looked at.
  • Legacy: The project was once active and may have had a reasonable community. There is still some work to fix important bugs and answer some questions, but no major features will be added.
  • Obsolete: The project uses technologies or versions of those technologies or approaches the owner and contributors think are obsolete and not worth further development. The code remains available for existing users or new users who are for some reason stuck on the old technologies. It may be helpful to combine this with a status like inactive, ignored or legacy to give a better indication of what users can expect.

You might imagine that large open-source projects with slick websites are exempt from needing to describe the project status because their website indicates the amount of effort being invested into the project. However, it is not uncommon to come across a beautiful open-source project website that was created four years ago and the site and project have been ignored ever since.

State a Clear Value Proposition

Explain in a short paragraph what your project is and does and why people would want to use it. Don’t assume other technologies you reference are known to the reader. They may have stumbled across your project even though they work in a totally different technology stack. When referencing other technologies, provide a link and a couple word description of what it is. Make sure it is clear what your project does. It is very easy for you, who knows the project inside and out, to not make this clear, because it is so obvious to you. Finally, include why someone would choose your project over other options. Try to avoid purely technical features like performance. Focus on features that are distinctive and not shared by most projects like yours. If you really want others to use your project, think of this as your sales pitch.

Committed Open-Source Projects

The above two things are what I think every bit of open-source code thrown up on the web needs to do. But what if you are actually wanting to start an open-source project that you hope people will use? Or what if you become a core contributor to a project you want to see succeed? The follow is my advice on how to make that project a success.

Recognize the Commitment and Make it

Creating and maintaining a successful open-source project is a lot of work. It is often as big as any project you might tackle at your job. Furthermore, you will be called on to do tasks you probably aren’t responsible for at work, like documentation and website design. There will be important bugs that if not fixed in a timely manner risk alienating your user base. There will be lots of questions and requests for help, many of which will be pretty “stupid” questions. All that will be done in your free time when you could be doing something else (unless you are one of the lucky few who is responsible for an open-source project at work). While you are spending your free time, there will be long stretches with little to no positive feedback. All that will take a lot of commitment. The most important thing you can do before starting an open-source project or becoming an important contributor to one is to carefully consider all the work it will entail. Don’t shy away from it, you need to face it upfront so when difficult days come you will know you anticipated them. If after all that, you still want to do the project. Then make a firm commitment to do the project and stick with it. The most successful open-source projects generally have a core team of contributors who were very committed for a very long time.

Don’t try to be All Things to All People

Given that this is an open-source project and not a product one is profiting from, it is important to limit the scope and focus on the important things. In large part, that means you don’t need to cater to every option and work style your users might have. It is good for your project to be opinionated and focused. If users don’t like those aspects, there are lots of other projects for them to choose from. It is much more important for the project to be excellent for a small dedicated user base. Trying to include as many users as possible often leads to a system that is incomplete and buggy. For example, the Pretzel project is trying to create a .NET port of the popular Jekyll static site generator. However, at the same time they are throwing in support for the razor template engine. Now their effort and focus is divided and they have twice as many things to test and document. I think this is leading to reduced quality. For example, when I last downloaded the project, the sample razor template site didn’t even compile. Once a project has successfully completed its core and has a growing user base, then it can expand to other functionality if it makes sense.

Docs Are as Important as Features

I see this all the time, a project is described in glowing terms and lists really cool features, so I go to use it only to discover there is no documentation except for one or two blog posts that barely count as an introduction. What the majority of potential users do at that point is look for other options. That is why your documentation is just as important as your features. Commit yourself to writing the documentation for a feature as soon as it is in a state where it is ready to be used. Also, don’t forget to update them as features are changed. That way the docs will always be up to date. The documentation needs to be a website or wiki or read me available on the web. If users have to download something to read the docs, a certain percentage of them won’t bother. Also, a bunch of blog posts don’t count as documentation. Blog posts are great for introducing new projects and features and for promoting a project, but they aren’t documentation. They can’t be updated to match the current version. It is incredibly frustrating to be referred to a blog post as documentation only to find that the API and features have changed significantly since then. Often there is no post describing the changes. Even if there is, it now takes twice as much work to read both posts and synthesize an understanding of the project.

Learn the Lessons of Your Predecessors

Typically there have been projects like yours before in your technology stack or in others. They have tried what you are doing and discovered the pitfalls and best practices. Look carefully at other projects doing what your project does. Make sure you understand how they work and why they work that way. Read through the issues they had. You might discover your exact approach has been tried but found wanting. You will likely save yourself a lot of time in the less core areas of the project which you haven’t been carefully thinking about.

A Slick Website Looks Professional

As developers we don’t value beautiful marketing material as much as we should. Even though your project isn’t a product you are selling for money, you are basically selling the use of it to your users. Having a slick website to sell the project gives people a good feeling about your project and makes it look very professional. If you can invest the time for a great website, then you must have the time to invest in a great project. The site doesn’t have to be large. Even a couple pages of description, features and contact information with links to documentation, code and downloads will go a long way.

Make Sure the Project Runs out of the box

If you have a sample project or example of using your library, be absolutely certain that it works immediately out of the box after users download and install it. If a user’s first experience with your project is that you can’t get the sample to work, they will be left with a very bad taste in their mouth. With each new version, test the sample. If possible make unit tests to cover as much of it as possible. Another way to fail in this area is to have the download instructions or link not updated so that they point at an old or broken version.

Practising What I Preach

Looking at all these open-source projects and realizing the important things I listed above, I realized some of my projects are failing in one or more of these areas. My next step is to go through each of the projects I have posted online and address these.

In summary, here again is my advice. The first two are for all open-source code posted online. The rest are for serious open-source projects:

  1. Clearly Indicate the Status of the Project
  2. State a Clear Value Proposition
  3. Recognize the Commitment and Make it
  4. Don’t try to be All Things to All People
  5. Docs Are as Important as Features
  6. Learn the Lessons of Your Predecessors
  7. A Slick Website Looks Professional
  8. Make Sure the Project Runs out of the box
June 17, 2015

The State of JS Build Tools 2015

I’ve recently been looking at JavaScript build tools because I am starting a project in AngularJS. So of course, I will need a build tool to compile, bundle and minify my scripts and style sheets. Another reason to look into these now is that Visual Studio 2015 will add support for task runners like Grunt and Gulp.

My starting point in this process was the “Gruntfile.js” created for me by the useful scaffolding tool Yeoman. Out of the box it came configured to use Sass with SCSS, while I prefer Less. So, I looked into what it would take to switch. The Grunt configuration, for what I considered a fairly typical build, was hefty, weighing in at 450+ lines. Digging in further, I discovered a number of things I didn’t like. That led me to evaluate other options. Moving on from Grunt, I looked at the next most popular option, Gulp. Then, I investigated the less common Broccoli.js and Brunch. I briefly considered the obscure Mimosa and Jake, but didn’t see anything compelling enough on their websites to make me think they needed to be considered over and above the others. This posts details my findings.

Criteria

When considering these JavaScript build tools, I bring a certain perspective and set of expectations. I’m a .NET web developer who has been using Visual Studio for a long time. In the past I have worked in Java and C++ and used a number of IDEs. I have some experience with a wide range of build tools including make, msbuild, rake, nant, and psake. That experience has led me to prefer a polished tool with lots of convention over configuration. To me, a build tool exists so I can work on my project and should stay out of my way. I should have to spend a minimal amount of time configuring and debugging the build. Most IDEs are pretty good in that regard. You add a file and the IDE just does the right thing based on the file extension (one only occasionally needs to set a few options).

Being a professional web developer, I also expect that a web focused build tool will have lots of support for the complex asset pipelines of modern web apps. This is an area where Visual Studio definitely falls down. This is also an interesting and unique requirement for build tools. Traditional tools like “make” rarely had to deal with situations like this. Compiling a C++ app is mostly about finding all the source code, compiling and linking. There was rarely much more to it than that. For web applications, there are many different kinds of files that must each pass through their own long pipeline of transformations and combinations. Additionally, the result isn’t a single executable, but a large assemblage of files. That makes the need for good file clean-up much more important. Finally, developer expectations have risen since the original days of make tools. Then we were happy to get an executable when we ran the make command. Now, we expect file watching with incremental rebuilds and live browser reloads.

Here is a brief list of the steps/actions I expect will be present in most JavaScript web app builds. Of course, they will vary from project to project and by developer taste.

  • Transcompiling JavaScript: CoffeeScript, Dart, Babel, Traceur etc.
  • JavaScript Transforms: wrapping in modules or ng-annotate etc.
  • Bundling/Concatenation: combining of scripts and styles into multiple files
  • Minification: scripts, styles and html
  • Source Maps: for both scripts and styles
  • CSS Preprocessor: Less, Sass, Stylus etc.
  • Style Transforms: Autoprefixer, PostCSS etc.
  • Cache Busting: renaming files with a hash to prevent incorrect caching
  • Image Optimization
  • Compiling Templates: Mustache or HandlebarsJS, Underscore, EJS, Jade etc.
  • Copying Assets: html, fav icon etc.
  • Watching for Changes
  • Incremental Rebuild
  • Clean Build: deleting all files at start or preferably cleaning up files as needed
  • Injecting References: generating script and style tags that reference the bundled files
  • Build Configurations: separate Dev, Test and Prod configuration, for example to not minify html in dev build
  • Serve: running a development web server
  • Running Unit Tests
  • Running JavaScript and CSS Linters: jshint, csslint etc.
  • Dependencies: handle dependencies on npm and Bower packages, Browserfy etc.

Findings

So what did I find? There were some common failings in all the build tools. One was the presumption that npm and Bower package restore were outside the scope of build. Unlike with an IDE such as Visual Studio that automatically runs Nuget package restore as needed, none of the JS build tools attempts to. This was often impossible to work around due to the fact that the build tool required it be one of the npm dependencies of your project and would not run until it was restored. Several of the tools did have plug-ins supporting Bower package restore. However, almost all examples I saw assumed one would first run npm install then bower install before building. They didn’t even setup bower install as an action triggered by npm install. Another shortcoming of all the tools is the handling of dependencies. This isn’t entirely their fault since the JavaScript ecosystem is still trying to figure out the issue. Apparently, npm packages work fine in Node.js (though the reason for the deep paths and duplicate packages created in node_modules are beyond me). But front end packages are not nearly so well thought through. Unlike a code library, a front-end package has lots of different assets which may be in many different languages, each of which has its own way of referencing one another. Furthermore, depending on the project toolset, the build might need dependencies either before or after compilation. For example, some projects needs the less files of bootstrap, while others need the compiled css. So how is one to express and control how those assets are to be brought into the project? Bower isn’t the answer if you ask me (at least not yet). Referencing a front-end package needs to become as simple as referencing an assembly or Nuget package is in .NET. Until it is, setting up builds involving dependencies will always be a pain. With that understanding of the shared issues with JavaScript build tools, let’s now consider each tool in turn.

Grunt

Grunt seems to be the starting point for many people when it comes to JS Build Tools. It certainly appears to be the most widely used one. However, the GruntJS.com website proudly proclaims Grunt as “The JavaScript Task Runner” and I think that is a good description. Grunt almost doesn’t qualify as a build tool. It really is a system for running a sequence of commands/tasks. Each Grunt task is a plug-in that represents a tool one might run from the command line. Each plug-in defines what its configuration in the Gruntfile is named and what the configuration options are. The fixed section names mean one can’t organize or name the configuration in a way that makes sense to them. Though there are many commonalities in plug-in configuration, in practice you are forced to read the docs of each plug-in to have any idea how to configure it. That means the otherwise acceptable docs are marred by the often poor and cryptic documentation of individual plug-ins. Since each plug-in is essentially a command line tool, they each take a set of input files and a set of output files. To chain together the long asset pipelines commonly needed requires one to manage an ever expanding set of intermediate temp files. File watching and live reload are manually configured by specifying tasks to run when files matching file name patterns are modified. The server configuration options are still totally opaque to me. Incremental builds must be manually setup using grunt-newer. In practice, that means having a deep understanding of how each task makes use of its various input files and which of the cascading temp files will need to be rebuilt. All of this manual configuration leads to enormous, inscrutable configuration files. If you need to run a couple tasks, grunt will be fine for you. If you need a real web development build tool. I strongly suggest you look elsewhere.

Gulp

Gulp is the hot new tool. It is rapidly gaining in popularity and there are some justifiable reasons for this. It recognizes and attempts to handle the primary unique requirement of web build tools, namely the long pipelines. In Gulp all plug-ins work on streams (well as of today there are still a number that don’t, which is very annoying). This streaming model allows Gulp tasks to be chained together in streams without worrying about or even producing intermediate temporary files. Gulp touts its high performance due to both streams and parallel task running. Yet, many users have complained that the parallel task execution is confusing and there are plans to extend the configuration to give more control over this. Personally, I found the parallel task execution very intuitive and feel the proposed cure is worse than the problem.

While streams are a compelling base for a build tool, I ultimately found Gulp lacking. Since the core configuration options are minimal, the attitude seems to be that little documentation is needed, but I found the documentation inadequate. Like Grunt, it was necessary to rely on the usually poor documentation of the plug-ins. This was somewhat mitigated by the greater consistency in plug-in usage versus Grunt. The Gulp authors believe in opinionated software and have some strong opinions when it comes to Gulp plug-ins. Normally, I appreciate opinionated software, but in this case I have to disagree with the authors’ opinions. They believe that a tool that already supports streaming should not be wrapped in a plug-in, but used directly. This runs counter to making builds as easy as possible to setup because it requires one to learn and adapt the tools’ unique APIs to the Gulp system. For a peek into the mess this causes, check out the GitHub issue over the blacklisting of the gulp-browserify plug-in. In it, dozens of users attempt to figure out the correct way of using Browserify. I don’t think any quite hit upon the difficult to find official recipe. But note, even that fails to support the important case of streaming files into Browserify. The whole issue is complicated enough that it takes an excellent but lengthy blog post to fully explain. Furthermore, the streaming model becomes confusing for things that don’t fit into it, such as cleaning. Source maps in particular are odd. I attempted to combine external source maps with cache busting with a manifest file and cleaning up old revisions and while the final config was quite short, it took me several hours and additional plug-ins to get working. Watching still requires manual configuration and incremental building is quite confusing to setup. Figuring out how to do anything is made worse by the plethora of plug-ins for doing the same task. Finally, while I don’t understand the issue myself, I found many discussions of needing to do extra configuration to get reasonable error messages out of Gulp.

Broccoli

Broccoli.js is a newer build tool that has been in the works for a while. It doesn’t seem to have much traction yet, but hopefully its inclusion in the ember command line tool will change that. I really like that the developer has thought through most of the issues facing a JavaScript build tool. Support for watch and incremental build is built in so that Broccoli “will figure out by itself which files to watch, and only rebuild those that need rebuilding”. It achieves that through a sophisticated caching system built on top of its core abstraction of file trees. Trees are Broccoli’s answer to Gulp streams and are easily chained. To simplify the system, they don’t run tasks in parallel like Gulp, but claim that it isn’t necessary because performance is still good. I highly recommend you check out the author’s Comparison With Other Build Tools (scroll down to section 5).

Despite all these excellent attributes, Broccoli is still very immature and in beta. It is lacking in the area of documentation. The read me boldly states windows support is spotty. The fact that there is little to no source map support was a big issue for me. It seemed to me that Broccoli wasn’t quite ready for prime time, but I’d really like to see it mature and supplant Grunt and Gulp.

Brunch

Brunch has apparently been around for quite a while and yet flown under the radar. Brunch relies heavily on convention over configuration and assumes your build pipeline will include most of the steps I laid out above. When you include a plug-in, it is assumed to apply at the logical point in the pipeline and requires little or even no configuration. This means that a config that might be 600+ lines in Grunt or 150+ lines in Gulp could end up being 20 lines in Brunch. Brunch is written in CoffeeScript and all the examples are in CoffeeScript, but it isn’t too hard to translate to JavaScript. It natively supports watching, and incremental compilation with no extra config whatsoever. There is direct support for Development vs Production builds with different configurations (other build configurations are easily setup). It even wraps JavaScript modules for you automatically (configurable).

With all that, you might think we had found our answer to JavaScript builds. However, there are always problems. Despite the lengthy Guide and the Docs, I still found myself wishing for clearer explanations and more examples. I was surprised to find no support for clean builds baked in. Hopefully, this will be addressed due to the issue I filed. I’m still trying to figure out the right way to run unit tests with Brunch. The real issue for me was node and Bower packages. Since Brunch attempts to manage your code more and even wraps JavaScript in modules, it doesn’t seem to play well with packages. They claim support for Bower, but this wasn’t well enough documented for me and still didn’t seem to quite be right. It appears these issues are because Brunch pre-dates the widespread use of npm and Bower and they are looking at how they might solve these issues. In the mean time, be prepared for some awkwardness around that and around Browserify. Lastly, as is common with systems focusing on convention over configuration, if you stray off the established path you might have some issues. For example, I am still trying to figure out exactly how I can get cache busting to apply to assets like images if I am compiling the templates that reference those images.

Conclusions

I am disappointed by the state of JavaScript build tools in 2015. They are very immature and don’t seem to be well informed by the long history of build tools for other environments and platforms. Additionally, their authors don’t seem to be sitting down with the goal of really working through all the issues that come up in a build tool and making sure they are handled and the tool is easy to use. Only two, namely Brunch and Mimosa seem to have set out with the goal of making most builds very simple to setup and not requiring lots of boilerplate configuration.

So which tool would I recommend? Personally, I am still working with Brunch to see if it will work for my project. It would be at the top of my list right now (if you like the convention over configuration approach of Brunch, you could also check out Mimosa). However, if you don’t mind the large awkward configurations then you might want to select Gulp or even Grunt just for the size of the community around them and the selection of plug-ins (though I haven’t had a problem finding the Brunch plug-ins I need). I’m really interested in watching the development of Broccoli and hope it becomes a viable option in the future. The ultimate answer is likely to be a tool that doesn’t even exist today. Once package dependencies are ironed out correctly, I think we are likely to find all the existing tools don’t quite make sense anymore. Of course, there is no telling how long it will take to get to such a state.