📜 ⬆️ ⬇️

Getting out of the rabbit burrow SPA with modern Rails

TL; DR: The SPA trail is dark and full of horrors. You can fearlessly fight them ... or choose another path that will lead you to the right place: modern Rails.



I remember thinking Rails was focusing on the wrong goal when DHH announced Turbolinks in 2012. Then I was convinced that the instant response time during user interaction is the key to the excellent UX. Due to network delays, such interactivity is possible only if you minimize network dependency and, instead of network access, you maintain most of the state on the client.
')
I thought it was necessary for the applications I was working on. And with such an opinion, I tried many approaches and frameworks for implementing the same template: Single-page applications (SPA). I believed that SPA is the future. A few years later, I'm not sure what the future is, but I definitely want to find an alternative.

Rabbit Hole SPA


A one-page application is a JavaScript application that, once loaded, gets full control without having to reload the page: rendering, receiving data from the server, processing user interaction, updating the screen ...

Such applications look more native than traditional web pages, where the application depends on the server. For example, if you use Trello, you may notice how quickly cards are created.

Of course, with great force comes great responsibility. In traditional web applications, your server application includes a domain model and business rules, data access technology for working with a database, and a controller layer to control how HTML pages are collected in response to HTTP requests.

C SPA is a bit more complicated. You still need a server application that includes your domain model and rules, a web server, a database, and some kind of data access technology ... and a bunch of extra pieces on top:

For server:


For a new javascript client:


Summing up, with SPA you will have another application for support. And a new set of problems to solve. And note, you cannot replace one application with another. You still need a server application (it will now render JSON instead of HTML).

If you have never worked with a SPA, you may underestimate the difficulties you will face. I know this because I made the same mistakes in the past. Render JSON? I can handle it. Rich domain object model in javascript? It sounds funny. And, you know, this framework will solve all these problems. Great cake!

Wrong.

API and data exchange.


Data exchange between your new application and the server is a difficult problem.

There are two opposing forces:


In addition, you need to think about what to download, and when to do it for each of their screens. I mean, you need a balance between boot, what you need right away, and what can be loaded lazily, and develop an API that allows you to do this.

Some standards may help here. JSON API to standardize JSON format; or GraphQL, to select only the necessary data, as complex as needed, in one query. But none of them will save you from:

And both of these aspects represent a sufficient amount of additional work.

Boot time


People are used to associating SPA with speed, but the truth is that getting them loaded quickly is not so easy. There are many reasons for this:


This does not mean that it is impossible to force the SPA to load quickly. I'm just saying that it is difficult and you should take care of it, because it will not come along with the SPA architecture.

For example, Discourse, a SPA based on Ember, has fantastic load times, but among other things, they preload a large amount of JSON data as part of the initial HTML in order not to make additional requests. And I note that the Discourse team is crazy about speed in a good way and their skills are well above average. Think about it before you can easily reproduce the same in your SPA.

The ambitious solution to this problem is isomorphic JavaScript: render your homepage on the server and quickly give it to the client while in the background the SPA is loading and gaining full control when ready.

This approach requires the execution of JavaScript runtime on the server, and it is also not without technical problems. For example, developers should consider SPA loading events, as the loading process changes.

I like the possibility of reusing the code in this idea, but I did not see the implementation that would allow me to go in the opposite direction. In addition, this page rendering process is very funny to me:

- Server: request to the server API
- Server: database query
- Server: generate JSON
- Server: convert JSON to HTML
- Client: display initial HTML
- Client: download SPA
- Client: parse initial HTML and subscribe to DOM events

Could you just request data from the database, generate HTML and start working?

This is not entirely fair, because you will not be a SPA and because most of this magic is hidden behind the framework, but it still seems wrong to me.

Architecture


Developing applications with a rich user interface is difficult. This is all because it is one of the problems that inspired the emergence of an object-oriented approach and many design patterns.

Managing the state on the client is difficult. Traditional websites usually focus on single-responsibility screens that lose their fortune when they reboot. In the SPA, the application is responsible for ensuring that all state and screen updates during use are consistent and run smoothly.

In practice, if you started writing JavaScript in small portions in order to improve the interaction, then in SPA you will have to write tons of additional JavaScript code. Here you should make sure that you are doing everything right.

There are as many different architectures as SPA frameworks:


Of all the frameworks I've seen, Ember is my favorite. I adore his consistency and the fact that he is rather stubborn. I also like his latest programming model, which combines traditional MVC, components and routing.

On the other hand, I am strongly against the Flux / Redux camp. I have seen so many smart people applying them that I made every effort to study and understand it and not once. I can't help shaking my head in frustration when I see the code. I do not see myself happy while writing such code.

Finally, I can't accept the mix of HTML and CSS in components that are full of JavaScript logic. I understand what problem this solves, but the problems this approach introduces does not make it worth it.

Leave personal preferences, the bottom line is that if you choose the SPA path, then you will have a very difficult problem: to create the architecture of your new application correctly. And the industry is quite far from reaching an agreement on how to do it. Every year new frameworks, templates and versions of frameworks appear, which slightly changes the programming model. You will need to write and maintain a ton of code based on your architectural choice, so think about it as it should.

Code duplication


When working with SPA you will probably encounter code duplication.

For your SPA logic, you will want to create a rich model of objects representing your domain domain and business logic. And you still need the same thing for server logic. And it is a matter of time when you start copying the code.

For example, imagine that you work with invoices. You probably have an Invoice class in JavaScript that contains a total method that summarizes the price of all the elements so that you can render the cost. On the server, you will also need the Invoice class with the total method to calculate this cost in order to send it by e-mail. See? Client and server class Invoice implement the same logic. Duplication code.

As mentioned above, isomorphic javascript could even out this problem, making it easier to reuse the code. And I say level, because the correspondence between the client and the server is not always 1-to-1. You will want to be sure that some code never leaves the server. A large amount of code makes sense only for the client. Also, some aspects are simply different (for example, the server element can save data to the database, and the client can use the remote API). Reusing a code, even if possible, is a complex problem.

You can bet that you don’t need a rich model in your SPA and that you will instead work with JSON / JavaScript objects directly, distributing the logic across the UI components. Now you have the same code duplication mixed with your UI code, good luck with that.

And the same thing happens if you want templates for HTML rendering between the server and the client. For example, for SEO, how about generating pages on a server for search engines? You will need to re-write your templates on the server and make sure that they are synchronized with the client. Again duplicate code.

The need to reproduce the logic of patterns on the server and client, in my experience, is the source of the growing misfortunes of programmers. Doing it once is normal. When you do it for the 20th time, you will clutch your head. Having done this for the 50th time, you will wonder if you need all these SPA pieces.

Fragility


In my experience, developing a good SPA is a much more difficult task than writing web applications with generation on the server.

Firstly, no matter how careful you are, no matter how many tests you write. The more code you write, the more bugs you will have. And the SPA represents (sorry if I press hard) a huge pile of code for writing and support.

Secondly, as mentioned above, developing a rich GUI is difficult and results in complex systems consisting of many elements that interact with each other. The more complex the system you create, the more bugs you have. And compared to traditional MVC-based web applications, the complexity of the SPA is simply insane.

For example, to maintain consistency on the server, you can use restrictions in the database, model validation, and transactions. If something goes wrong, you reply with an error message. In the client, everything is slightly more complicated. A lot can go wrong because too much is happening. It may be that some record is saved successfully, and some other record is not. You may have gone offline in the middle of some operation. You must ensure that the UI remains consistent and that the application recovers from an error. All this is possible, of course, only much more complicated.

Organizational challenges


It sounds silly, but to develop a SPA, you need developers who know what to do with it. At the same time, you should not underestimate the complexity of the SPA, you should not think that any experienced web developer with the right motivation and understanding can write an excellent SPA from scratch. You need the right skills and experience, or expect important mistakes to be made. I know this because this is exactly my case.

This is perhaps a more important challenge for your company than you think. The SPA approach encourages narrow specialization teams instead of teams from general specialists:


The chances are that you will find yourself with people who cannot work with the SPA, and who cannot work on the server side, simply because they do not know how.

This specialization may be ideal for Facebook or Google and their teams consisting of several layers of engineering troops. But will it be good for your team of 6 people?

Modern Rails


There are 3 things in modern Rails that can change your mind about developing modern web applications:

It is difficult to understand what kind of approach it is, without playing with it. Therefore, I will make a few references to Basecamp in the following sections. I have nothing to do with Basecamp, except as a happy user. As for this article, this is just a good living example of modern Rails, which you can try for free.

Turbolinks


The idea behind Turbolinks is simple: speed up your application by completely replacing page reloading with AJAX requests that replace the `` element. The inner witchcraft that does this work is hidden. As a developer, you can focus on traditional server programming.

Turbolinks is inspired by pjax and has gone through several revisions.

I used to worry about its performance. I was wrong. Acceleration is huge. What convinced me was how I used it in the project, but you can just try the trial version of Basecamp and play around with it. Try creating a project with some elements, and then navigate through them by clicking on sections. This will give you a good idea of ​​how Turbolinks looks.

I do not think that Turbolinks is simply amazing with its novelty (pjax - 8 years). Or its technical sophistication. It amazes me how a simple idea can increase your productivity by an order of magnitude compared to an alternative to a SPA.

Let me highlight some of the problems that it fixes:


MVC on the server, in the version used by Rails and many other frameworks, is much simpler than any of the templates used for the rich GUI architecture: get a request, work with the database to satisfy it, and display the HTML page as an answer.

Finally, the restriction that is always replaced has a remarkable effect: you can focus on the initial rendering of the pages instead of updating certain sections (or updating certain states in the SPA world). In general, he just does everything.


Notice, I’m not talking about identifying problems, but about fixing them. For example, GraphQL or SPA rehydration is the ultimate solution for very complex problems. But what if instead of trying to find a solution, you put yourself in a situation where these problems do not exist? This is a change in approach to the problem. And it took me years to fully appreciate the ability of this approach to solve problems.

Of course, Turbolinks is not a problem-free silver bullet. The biggest problem is that it can break existing JavaScript code:


AJAX rendering and SJR responses


Remember when rendering HTML via Ajax was in trend 15 years ago? Guess what? This is still a great tool in your arsenal:

You can see how this approach is felt in Basecamp by opening your profile menu by pressing the top right button:



It opens instantly. From the development side, you don’t need to worry about JSON serialization and the client side. You can simply display this snippet on the server using all the features of Rails.

A similar tool that Rails has been incorporating over the years is server-side JavaScript (SJR) responses. They allow you to respond to Ajax requests (usually form views) with JavaScript, which is executed by the client. It has the same advantages as AJAX rendering HTML fragments: it executes very quickly, you can reuse the code on the server side, and you can directly access the database to create a response.

You can see how this happens if you go to Basecamp and try to create a new todo. After you click Add todo, the server saves todo and responds with a javascript fragment that adds the new todo to the DOM.

I think that many developers today are looking at AJAX rendering and SJR responses with contempt. I remember that too. They are a tool and, as such, may be abused. But when used properly, this is an amazing solution. Let us offer great UX and interactivity at a very low price. Unfortunately, like Turbolinks, they are difficult to assess if you have not fought a SPA.

Stimulus


Stimulus is a JavaScript framework published a few months ago. It does not care about rendering or JavaScript-based state management. Instead, it's just a good, modern way of organizing the javascript you use to add HTML:


If you adopt the Rails path, your JavaScript will focus on modifying the HTML code created on the server side and improving the interaction (with a small amount of JavaScript). Stimulus is designed to organize such a code. This is not a SPA system and does not claim to be one.

I have used Stimulus in several projects, and I like it a lot. It eliminates a heap of sample code, it is built on the latest web standards and reads very nicely. And something that I love especially: now this is the standard way to do something that until now had to be solved in each application.

Compromise game


Turbolinks is usually sold as "Get all the benefits of a SPA without any inconvenience." I do not think this is completely true:


Now development is a game of compromise. And in this game:

I believe that with Rails you can get 90% of what a SPA offers with 10% effort. As for performance, Rails kills SPA. As for the UX, I think that many developers are making the same mistake as me, assuming that the SPA UX is unsurpassed. This is not true. In fact, as discussed above, you'd better know what you are doing when creating your SPA, or the UX will actually be worse.

Conclusion


I watch how companies massively adopt SPA frameworks, and see countless articles on how to do fancy things in the style of SPA. I think there are many “uses of the wrong tool for the job,” as I firmly believe that the types of applications that justify the use of the SPA are limited.

And I say that they are justified because SPAs are complex. Anyway, I hope I have convinced you of this. I'm not saying that it’s impossible to create great SPA applications, or that modern Rails applications are great by definition, it's just one approach that is very complex and the other is much simpler.

While preparing this article, I stumbled upon this tweet:



It made me laugh, because I would choose the first options if the alternative was not justified. He is also a representative of a kind of thinking of developers who loves complexity and thrives on it, even to the extent that he considers other people crazy with different criteria.

After many years, I realized that complexity is often a choice. But in the world of programming it is surprisingly difficult to choose simplicity. We value complexity so much that accepting simplicity often makes you think differently, which by definition is difficult.

Remember that you can get rid of trouble. If you choose the SPA path, make sure that it is justified and you understand the problems. If you are not sure, experiment with different approaches and see for yourself. Facebook or Google, on their scale, may not have the luxury of making such decisions, but you probably can.

And if you're a Rails developer who left Rails many years ago, I recommend you go back to him. I think you will be delighted.

Source: https://habr.com/ru/post/417875/


All Articles