Back to the news
Dev Gravity Gravity

How wonderful it is to work with a JavaScript backend

24.03.23
JavaScript

JavaScript gets a lot of (well-deserved) hate for being an awful language. Java is still often considered as not being fit for backend development. As a result, TypeScript often receives the same complaints.

Although this is not entirely false, using a single language for backend and frontend eases a lot the workflow of the team:

  • Better testing,
  • Less code redundancy
  • Easier workflow for the developers

In this post, I will address the benefits of having a single language for our frontend and backend (TypeScript in our case). The motivation behind this post is that I often find posts and discussions highlighting the negative effects of having a backend written in JavaScript.

To be honest, most of the points against a JavaScript backend are pretty valid. But as with any choice of tooling/solution, the reality is neither all black nor white, just grayish.

Is JavaScript bad?

Yes.

I will not argue that JavaScript can be pretty weird (the classic Wat lightning talk from Gary Bernhardt covers with a lot of fun those “funny” moments with JavaScript), nor will I argue that it can be sometimes a real pain to debug when it comes to unhandled promises or “callback hell”.

JavaScript isn’t perfect (although TypeScript improves it), but I don’t believe any other language is.

I developed web apps using Ruby on Rails at my previous company, and earlier, I used Python with Django or Zope for a few websites. I have really enjoyed, working with those two languages, even if they also had their own issues.

Despite JavaScript’s flaws, here are reasons why I wouldn’t switch easily to another language for backend.

CTA Gravity demo AI

The benefits of sharing a single language between backend and frontend

Sharing types

Well, obviously that one requires using TypeScript and not plain JavaScript. I’ve lost count of the defaults caused by incorrect data shared between frontend and backend in my experiences. There were mainly two reasons for this kind of issue:

  • The backend endpoint had been updated, but we had forgotten to apply the changes to the frontend. If the endpoint is not tested with the full stack, this problem will not be caught by the CI. Worse yet, if frontend tests use mocks for the backend, we can get incorrect positive results indicating everything is fine.
  • Misunderstanding between frontend and backend teams on exchanged data.

Alternative solutions include contract testing, using protobuf or JSON schema to format the data, and others.

But I find it easier to deal with this issue without needing an external library to handle formatting the data.

Sharing utilities

Although the frontend and backend generally do not have the same scope of responsibilities, there is often some code that needs to be shared between the two.

The first in mind would be (de)serializing data, but some treatments on those data could also be shared. In Gravity, for example, the language used to query sessions must be available in both stacks: in the frontend to define the scope of sessions and in the backend to query the sessions.

Those utilities are generally simple, but you still have to write them twice when you have a different language for frontend and backend. And when changes are made to those treatments, they must be applied to both stacks.

Ease the team’s workflow 

The Gravity team is composed of 5 developers, and although everyone has their own preferences (for example, I prefer spending time in the backend than in the frontend), any developer in the team can work at any level of the application, without having to learn two languages. So frontend bugs can be addressed by any developer, not just those dedicated to frontend.

Another advantage of being able to write code in both frontend and backend is that, when a developer (or two when we are pairing) starts a new feature, the same person(s) will develop the feature from the beginning to its delivery.

As a developer, I find this more motivating to work on the whole feature instead of focusing on just a subset of it.

To be honest, those examples are more tied to the team organization than to the fact we use the same language for backend and frontend. In my previous company, we started with the same kind of organization, although the backend was written in Ruby. In the future, when the team will grow, we will certainly have to change our processes. We might have dedicated teams for frontend and backend, or we might have features teams that work on both stacks, or we might end up with another solution.

Single language development

That being said, I am still convinced that having a single language to work with, eases a lot, having this kind of team organization. It should also prove to be pretty useful when we integrate new junior developers or interns, as they should also face one learning curve and not two.

And one last advantage for the developers is no more context switching (well, in an ideal world… Having a single language does not magically protect you from meetings, bugs, and production incidents). When you are developing in parallel in two languages, you are often facing those little moments trying to recall the correct syntax: “is `map` taking a callback argument or using a block ? Ah yes, it’s Ruby, it’s a block !”.
Those are obviously not real blockers, but being able to focus on what you want to do, and not how to do it, eases the development workflow

Sharing tests

One of the main reasons (added to all the previously mentioned) I enjoy working with a single language is that we can simply write acceptance tests that can be executed at multiple levels of the application:

  • against the backend core functions
  • against the HTTP server
  • against the components
  • Against the full application.

To achieve this, we write the acceptance scenario in Gherkin (the Given/When/Then language behind Cucumber). During the execution, the glue code will hit the system under test at different levels depending on the configuration.

This enables us to write our code incrementally:

  1. We first focus on the business rules.
  2. Then we expose it through HTTP.
  3. We write components that will communicate with the HTTP endpoints.
  4. Finally, we integrate those components into the application pages.

This can be achieved too when having a different language for the backend, but it is more tedious (some tools that were written for Cucumber-js have not been ported to the other Cucumber implementation). The glue code also has to be written twice: once for the backend, and once for the frontend. It also doubles the maintenance of the said glue code (any change in one implementation will have to be ported to the other one).

A few words of conclusion

Like with any choice of tool, the context can be more important than the tool itself. To do a simple analogy with woodworking, a hammer is neither better nor worse than a screwdriver. TypeScript and JavaScript each serve the same goal (fastening pieces of wood together). But they have different approaches (with nails or screws).

The same goes for languages. They all serve the same goal: telling the computer what to do. The business field served by the application might be a deciding factor in the choice of the backend language. For example, Python is often praised for Machine Learning, and sometimes a Java connector is necessary for certain tools. The team and its organization are also a determining factor. Forcing a Ruby on Rails team to switch to TypeScript backend could be a waste.

Is a JavaScriptTypeScript backend the perfect solution for any web application? Certainly not. Is it a great solution for our team today? Yes, for sure. Will it still be the case in a few years? I honestly have no clue.

If you want to learn more about the features offered by our tool Gravity, book a demo with our team here

I hope you enjoyed the reading, I would be happy to know your opinion about JavaScript in the comments section. See you soon!

 

Stay tuned!

The Pesticide Paradox

Shifting-right: beating the Pesticide Paradox in Automated Testing

AI Gravity Testing Gravity

More tests does not necessarily lead to higher quality Before delving into the Pesticide Paradox, let’s provide a brief overview…

Quality Intelligence

What is Quality Intelligence?

Gravity Testing Gravity

The challenge of assessing the effectiveness of testing Before diving into Quality intelligence, let’s talk about the testing effectiveness challenge.…

TestOPs

TestOps in Software Testing: What is its Role?

Gravity Testing Gravity

Introduction Before diving into the TestOps approach, let’s introduce the DevOps first. With the widespread adoption of agile methodologies and…