Backbone is a lightweight framework that has been around since 2010. One of the best things about it is that it hasn’t changed much over the years - it’s established, used in a lot of very large websites, and most of the updates during our development were very minor. There is very little magic in it and the source is readable in a few hours. But because it’s so simple and lightweight, it doesn’t force you into a certain pattern of how to use it, which can be good or bad depending on the developers that are working with it. In our case, we had three incredible developers lay down the initial architecture and patterns, and we were then able to scale that code during the project. I know this is not always the case with development, and sometimes more guidance from your framework is desired, but I enjoyed the freedom of architecting our own solution that really fit their requirements.
I have to admit that over the course of the project, the number of issues we had with changes in third party libraries breaking our build was probably less than 10. Given the number of dependencies we had and builds we did, I would have expected more. After we got a burned a few times, we learned to stick with exact versions of libraries using specific version numbers instead of using the conventional tilda in the package.json and bower.json to grab any patch release updates. This way there weren’t any surprises during simple patch fixes because we got those surprises; it’s likely you will as well.
When we first started the project, we didn’t do a whole lot of code reviews as we were rapidly trying to build out the skeleton of this new application. But after seeing a coworker describe their GitHub pull request workflow, it seemed like something very beneficial and didn’t seem to add a lot of overhead. At first, we were worried at the amount of time it would take to do the reviews and what it would do to velocity, but after we were able to catch some potential issues during a review, we knew this was something very beneficial to the project.
The basic idea is to follow the Gitflow style pattern where a developer works in a feature branch of the code repository and completes their task there. When they completed that feature, they’d create a pull request from that branch to the main
develop branch. Then someone would have a chance to review it and make sure it was working and met the requirements of the ticket.
At first, we did this on just some features that we wanted an additional set of eyes on to help test, but it quickly turned into something we did all the time. Especially as we grew as a team, we wanted to ensure that the quality of code stayed high. This also cross trained developers into different sections of the app that they might not have been as familiar with. Some developers knew certain sections of the app better than others, by assigning them the pull request, they were able to share some additional edge cases in testing that maybe you didn’t think of. Pretty soon, anytime you committed something directly to develop, it immediately felt wrong and you’d have to wear the cowboy hat.
One thing worth mentioning is that it’s best to keep your pull requests small and focused. There was a saying that if the pull request had less than 10 lines of code changed, that we’d be more likely to reject it, but if it was fairly large touching a bunch of files, it would likely be approved. It’s not that we didn’t test it, but it’s much harder to be comprehensive on a review of a large piece of work. Time crunches happen, and if it works, it sometimes gets in.
Embracing New Paradigms
While we were working on this project, React came out and we paid close attention to what the community was doing with it. We even had one of our developers go to React Week, immersing himself in it there. After dabbling around in it, we really wanted to bring it into our project, but by that time, we already had a solid architecture in place using Backbone and even had developed a shared library for our service layer that additional projects were depending on. Introducing a new dependency on something that was still changing fairly rapidly was not something we decided on doing.
While we never got to bring in React, I think we ended up with something pretty special that gave us the best of both worlds. Developers got familiar with the concepts and enjoyed working that way. If they ever move to a new project that’s using React, I’m sure they’ll be quick to pick up the rest. This is just an example of how you could potentially bring in new programming paradigms into your own code without the explicit dependency on it. When working in technology that is changing so often, you should be watching the current trends and be open to bringing in some things into your project.
Code organization is one of those things that you can do several different ways. I simply wanted to mention this as one of the things we initially did one way, and then later in the project realized that maybe we shouldn’t have done it that way. You won’t always get things right the first time, so be prepared to recover, rethink, and redo.
When we first started the project, we had a ‘src’ directory, and that had subdirectories for ‘less’, ‘js’, and ‘html’ holding the corresponding file types. Initially, our ‘js’ folder stayed somewhat organized into folders for each of the different sections of the app, but the ‘html’ templates folder and ‘less’ folder ended up become flat with a bunch of files in them without organization. The lack of organization is one thing, but what was extremely painful later on was finding what files went together for particular sections of the app. Especially for new developers that joined the project, it wasn’t always clear what went with what.
CSS in a large project can become very problematic if you don’t architect it well initially. Without guidance, it’s easy to have styles trickle into certain selectors that you may have had no idea about. Then it becomes difficult to see why some things aren’t styled as you intended.
Initially, the developers would try to scope their css within certain sections of the app and then they had a special file for anything that was truly a global style. This became problematic as we built more and more sections; we really needed better guidance on how to keep this under control.
Later we adopted a new pattern in correlation with the code structure changes we made where your styles would be scoped to the views we were working on. Each view would have a class where it would scope everything under that. For example, in the following HTML, we have an OrderDetail view, and here is how we might style that.
This ensures that the things you styled for OrderDetailView would only be scoped to that view. If you needed to make something common, you’d move up to the highest level of the scope that included the pieces you wanted to make common and then define the style using that selector. For example. if you wanted to make
header common to all the potential detail views, you would do something like:
If you had to override that style for the ‘OrderDetailView’ for example, you could do it within the scope of that view. While in this example it seems straightforward, when you are building a larger app, maintaining this hierarchy is not often as easy as it seems. Setting the rules at the beginning will help keep this consistent as your app grows.
It’s worth noting that we knew that performance of the browser takes a hit as you increase the number of selectors in your style, but we felt that the security it provided to the style outweighed the performance impact of it.
Another thing related to CSS and styling that I wanted to share is that you should make sure that you never hard code any colors in your Less/Sass. You should work with your designers to decide on a color palette for the app and ensure that you define variables for those colors and stick to them. Ideally, you’d have some kind of style guide, but not all projects have the time/budget for one, so sometimes your color variables become the only guide to keep new designers from bringing in other variations of those colors into your app. It seems minor but it can become a huge headache trying to figure out which shade of that color is the right one. We had an issue where we had way too many versions of a particular color and during the refactor of fixing that, we wrote a script to color match the questionable color against the known set of colors to determine its closest match. Using that script we were able to reel in all the rogue colors.
There seems to be a lot of room for improvement in how CSS is used in large apps. CSS Modules look promising, and provide answers to some of the issues we have with CSS, but only time will tell where this goes. If you are interested in CSS Modules, we just posted an article all about them here.
During the course of the project, we scaled in team size from around three to twelve developers. One thing that all developers commented on when joining the project was how readable and understandable the code was that they were working with. We focused on clarity of code over cleverness. One of our developers always said, ‘know that the next developer to look at this code will probably be yourself so be polite and focus on clarity’.
When working on features, we tried not to over-engineer them, focusing only on the most immediate requirements. We were working in an agile environment and tried to keep things reasonable per sprint. Sometimes we ended up butting heads with the same pieces of code that were becoming problematic. By the third time we hit something, we’d regroup as a team and see if there was a better solution. The majority of the time it really revolved around creating a more generic solution, which makes you think ‘well why didn’t you make it more generic the first time?’ Making a generic solution doesn’t always make sense, it adds extra development time, additional cost, and might not be as performant as something targeting the exact features. What we found is that over time it sometimes took those intermediate versions to really figure out all the features that were really intended for it.
When you work on something for three years, there will always be things you did right the first time and others that you didn’t. Being able to look back and see things that you can improve will help you grow and help on the next project you work on. That process will likely repeat throughout your career.