Modularizing The JavaScript You Already Have

Over time, without structure, things fall apart. So give them structure, a few conventions, and start to make them more maintainable. Let me say more, at least in the context of JavaScript in your applications (or did you think I was talking about gardens or buildings or such? lol.)

As our applications grow larger and larger, it starts to become obvious that some parts of them grow messier than others.

This may vary from team to team but I think one of the most common parts which quickly grow out of hand is the front-end: JavaScript, HTML and CSS.

For Rails applications in particular, the framework provides some structure for your back-end code, but it doesn’t really offer any structure to your front-end code.

Modern versions of Rails already integrate with the webpack and the Node.js worlds, but even then you are free to choose any framework you want, or to use no framework at all.

This is on purpose and it’s great to have this freedom — although sometimes freedom can be the final nail in the coffin.

In the spirit of Rails is omakase: Sometimes it’s better to let the chef decide, or in this case, just use a battle-tested framework.

But there’s a problem. We don’t always need a framework. If all we need to do in our application is tie together a few Select2 drop-downs, a slider, some modals and maybe a date picker or two, then surely a framework is overkill.

That’s one of the reasons the Basecamp team (the Rails source code was basically extracted from this company’s app) came up with Stimulus: a modest JavaScript framework for the HTML you already have. What Stimulus does is beyond the scope of this post, but for new Rails applications which aren’t heavy on the front-end, this approach is highly recommended over going framework-less on the JavaScript side. But even then, maybe you don’t really need Stimulus, or you don’t feel like learning another framework, or just don’t think a framework is necessary for your little app.

Sometimes jQuery is enough, and for most Rails applications this approach “just works”. The problem is that as the application grows over time, because there is no well-defined structure to the front-end code, it deteriorates more and more until it feels like it’s all tied up with paperclips, bubblegum and wishful thinking. Make one small change in one file and everything breaks, I’m sure we’ve all seen this.

In this post I’ll talk about this case. I’ll suggest what to do to give at least some structure to your JavaScript code, give you a few “buckets” to throw your code into and end up with a decently maintainable structure, organized only by conventions. We will not use any library except the almighty jQuery.

Our tools

I’ll use CoffeeScript for doing this, but that’s just because I’m a fan of CoffeeScript’s succinct syntax and I use it any chance I get. If you do implement this, you should use whatever flavor of JavaScript your application is already using. For new applications, I’d say just use ES6/Babel.

I’ll use SASS for the CSS and HAML for the HTML. Again, this might just as easily be SCSS and HTML, or whatever you are already using.

A place for everything and everything in its place

On the JavaScript side, we will have the following folders:

Controllers follow Garber-Irish, which is basically using convention over configuration to automatically call a JavaScript file on page load.

It works like this. You add a few data-attributes on your application layout:

So when the HTML looks like this:

Then it will run App.Controllers.Comments.Index#initialize:

The implementation itself might vary. It normally takes less than 50 lines of code to tie everything up. You can find an implementation here.

Once you have Garber-Irish working, you have a place to put the JavaScript of each of your controller actions. This solves the problem of “which file is being executed when”.

We also want to avoid situations where changing one script causes others to break. This sort of problem can commonly happen as a result of a) poor isolation (an object knows too much about the external world), or b) violation of the Single Responsibility Principle, which is when an object does too many things.

For example:

Not only is the show method doing too much in this example – managing the modal state on the screen and initializing other components – but if the views which use my-element ever change the class of the element (or remove the element altogether), that’s gonna break. And if it fails silently, that’s not easy to see. You can spend quite some time hunting down this kind of bug. Eventually, maintaining the application feels like walking on eggshells.

One way to fix that is by introducing the concept of Components in your JavaScript. A component is simply a wrapper around an element – or a set of elements – which isolates the logic of initializing those elements, getting values from them, and just generally interacting with them. In order to change or know anything about those elements, you have to go through your wrapper object.

With this isolation, and by upholding the Single Responsibility Principle, it is possible to change one file and not break others.

A component-oriented approach would look like this:

Notice that communication is still required between Modal and Comments#Index, but we invert the responsibilities of the objects by using events.

Modal just lets consumers know that something related to the modal is going on. Consumers might listen to this event and do their work if they want. Modal doesn’t need to know about this, and will never need to change if the markup changes.

Now if we change the app/views/comments/index.html.haml view, we know that we might break app/assets/javascripts/controllers/comments/index.coffee.

It might seem like a small thing, but having this 1-to-1 relation is great. We don’t have to hunt things down, and we know that the only parts which are dependent on the markup are the controllers and – to some extent – the components.

Communication between components

Most likely you will need your components to talk to each other. You can easily achieve this by using PubSub.

PubSub is just an object which exposes on, off and trigger methods. The names may differ, but basically it allows you to bind callbacks to events, unbind them, and trigger events. There are many implementations, but my favorite is this tiny one. It basically delegates everything to jQuery, which you probably are using anyway. If you don’t use jQuery, you can search for alternatives or write your own PubSub implementation. It’s pretty easy.

If you do use jQuery, your components might look like this:

What about lib?

The app/assets/javascripts/lib folder is used for jQuery extensions, monkey-patches, and whatever utility objects you use across components and controllers.

Feel free to create more folders and categories as you see fit.

CSS and HTML

This is where BEM comes to the rescue. It doesn’t have to be BEM, though. Some people dislike it because, let’s admit it, it’s ugly. But it’s modular, it’s organized, it’s easy, and it works. We really want all of those things in legacy applications.

BEM stands for “Block/Element/Modifier”. A block is a container: a box in the page with some functionality, like a button, a navigation bar, a modal. A block can have many elements.

An element only makes sense inside a block. For example, if you want to display a button inside a modal, we know that modal is a block. And while the button is inside the modal, the button is actually its own block.

So in this case, because button makes sense outside of a modal, it gets to be its own component. On the other hand, the button to close the modal makes sense as an element, but it doesn’t really make sense without a modal.

Finally, modifier is simply state. For example, the modifiers of a button could be: active, disabled, primary, danger, hidden.

A BEM CSS rule starts with a selector that might look like this: .my-block__some-element--active.

That selector tells you that the style declared in the rule is applied to the active modifier of the some-element element inside the my-block block. It’s not pretty, but it works surprisingly well.

With this in mind, we make each of our JavaScript components a block. And we get the free advantage that all the CSS rules will be scoped only to the appropriate component.

As for the HTML, Rails’ standard solution of partials + helpers is a nice approach. Our final component looks like this:

We end up with one component in JavaScript, one component in CSS and one component in HTML.

Isn’t that cool?

Conclusion

This approach, while not ideal for front-end heavy applications, is in my opinion a pretty maintainable solution which requires no libraries and just a few conventions.

Something funny I noticed is that if you modularize JavaScript code using Object Oriented Design, and refactor over and over again, you might end up with something that looks like Backbone.js.

While Backbone is not perfect, it’s interesting the way design patterns applied to different codebases end up with similar-looking code. Refactoring is the way to clean code.

Hope you had a nice read. See you next time! 🙂

— Fede

Leave a Reply