How to fight front-end code spaghetti?
- Processes, standards and quality
The current state of front-end software development is great, and considering how active the open source community is, the future looks very promising. In this article we will show how to transfer back-end practices to front-end development and list the benefits of doing so.
When we think about back-end side of modern web application we often imagine this:
It’s Clean Code written by Uncle Bob that explains all kinds of good practices when it comes to software development.
There is a lot of good software development practices around. We all know (or at least heard of) SOLID, TDD, Design Patterns, DDD etc. On the back-end side, we have interfaces, types, brilliant IDEs, and many, many more. All those practices and rules help us write maintainable code and work effectively.
On the other side, there is front-end software development. It is often associated with something like this:
Lack of types and interfaces, hard time with testing, string driven development (HTML), no enterprise class security; it is easy to stray from good practices like SRP or SOLID.All of those often result in thousands of lines of spaghetti code, which is hard to maintain, even if you have skilled back-end developers in your team.
Nowadays, a lot of good old practices from the back-end leak to front-end code and help keep it clean and simple. The question, we’ll try to answer here is – how to do it? We’ll try to explain how the front-end frameworks and libraries have evolved to make the code simpler, more manageable and easier to reason about, and what tools we now have at our disposal when developing front-end applications.
When we look back, we can see that people generally have been struggling with writing good front-end code for years. Every year (or even more often) we get new, fancy JS framework that is trying to solve this problem.
However, the thing is that we don’t even need those frameworks. It’s said that framework solves the problems of its creator, not necessarily yours. When you try to use it, it may even get in your way and prevent from solving your problems properly. We can’t count all of the situations when we were struggling with a framework to do “simple” tasks.
What’s the alternative? Well, here’s an opinionated recipe for well designed, readable, manageable front-end application:
- divide responsibilities to components (use modules)
- use small packages/libraries
- introduce simple data flow
- provide easy to understand state management
On the back-end side, we always try to have a lot of smaller classes with their own responsibilities instead of one big piece of structural code. We build layers of abstraction and delegate work to other classes.
In front-end, it’s a little bit more complex because we deal directly with UI. It’s hard to organize HTML and JS in the same way as we organize things on the server side. For some time, there was a trend to use some kind of MV* framework and create separate layers for HTML, JS and CSS. But web applications can be very complicated and we can end up with big HTML views, even bigger controllers on JS side and it will be the same mess it was before. What to do?
„Component” enters the scene. Component is a small container that deals with its own data, logic and view. There is no point separating those because data is tightly coupled with view and its logic. This way we can use the same patterns we use on the back-end side; we can delegate things to smaller components that will follow SRP and use composition to create something bigger.
Front-end community is a big help to achieve this goal. One of the nicest examples is React.js – an open source library that is written with components in mind. We really encourage taking a look at it.
Sometimes we need our components to do more complicated tasks. When we don’t use a framework we can install additional libraries as we go. This is good because we don’t have useless functionality and dead library code. It’s possible to use small libraries that do one thing that we need and connect it with the rest of the application.
With smaller libraries we have less files to look through and less unwanted dependencies on 3rd party code. This is very beneficial for front-end development because it reduces the amount of data you need to send to the browser.
Developing and using libraries is easy with some kind of module system. Currently, the most popular choice is CommonJS because it’s simple and widely used. It’s supported by browser side bundlers like Webpack or Browserify and makes it possible to use wide range of libraries from NPM.
Thanks to Webpack we can even treat css files as modules and import them on demand to our components.
Data flow and state
UI is always stateful. Unfortunately, in most front-end applications, the most common solution of managing state is not to manage it at all. This leads to state being modified literally everywhere and effectively it is controlled nowhere. Nobody knows anything about the state of the application. We should use as much stateless components as possible because otherwise we don’t know what is going on in our application. Managing state by passing data down and events up the component tree makes reasoning about your application easier. It is more readable, it makes it simpler to extend the application, and it improves the efficiency in working with the code.
The root component of your application is often the best place to keep its state. If the child component needs access to the state, it should get it from the root component. It is also a good idea not to modify the state outside the root component. Child components should tell the owner of the state what needs to be changed. This way we always know where the state is changed, so there is no unexpected behaviour or side effects across the application. We end up with known state in the root component.
Renaissance of functional programming shows us all kinds of benefits of immutability that we are free to apply to front-end applications. We can use this to our benefit and make the application state immutable, as well as separated. For this we can use libraries like Immutable.js or seamless-immutable that are designed to keep all data immutable. Most of the libraries are small, have minimal clean interfaces and can be used in applications easily. The benefits of immutability are best understood in the context of functional programming and therefore are out of the scope of this article.
Current possibilities of front-end development show us that well-written applications are at your fingertips. Furthermore, we can finally use well known practices from back-end side, e.g. SRP, modularity, interfaces, immutable data structures. All of that prevents us from writing spaghetti code and makes it easier to reason about front-end application. There is no justification for writing thousand lines spaghetti monsters that are so common in legacy web applications. Modularity and keeping things small, simple and immutable is the new trend in web development that will, hopefully, be around for a while. More importantly, this workflow seems to work (ask them) so don’t hesitate to use it. Just do it.