📜 ⬆️ ⬇️

Why did a person from the Java world become an ardent supporter of Node.js and JavaScript?

David Harron, the author of the material we are translating today, asked the following question: “Should a person who worked for more than 10 years at Sun Microsystems, in the Java SE team, think only about Java bytecode and create instances of abstract interfaces until the last breath? ". He asked this question in relation to himself, and for him the Node.js platform, after Java, turned out to be like a breath of fresh air. David says that when he was fired from Sun in January 2009 (right before the takeover of this Oracle company), he learned about Node.js. This technology has hooked him. What does “hooked” mean? Since 2010, he has written a lot about programming for Node.js. Namely, he wrote several books, including Node.js Web Development, the fourth edition of which was published this year. He prepared a lot of small materials about Node.js published on the Internet. In fact, he devoted a lot of time and effort talking about the Node.js platform and the possibilities of JavaScript. Why was it that the person who used to do Java exclusively was so fascinated with Node.js and JavaScript?

image

About Java


Working at Sun, I believed in Java technology. I gave reports on JavaONE, participated in the development of the java.awt.Robot class, organized the event Mustang Regressions Contest (this was a contest aimed at finding errors in Java 1.6), helped launch the project “Distributions License for Java”, which served as the answer to the question about Linux distributions of the JDK before the appearance of OpenJDK. Later, I played some role in the launch of the OpenJDK project. Along the way, for about 6 years, I have been posting blog materials on java.net (now this site is closed). These were 1-2 articles per week devoted to significant events in the Java ecosystem. A significant role in my work was played by protecting Java from those who predicted this technology would have a bleak future.


This award, the Duke Award, was given to particularly distinguished Sun employees. I got it after I organized the Mustang Regressions Contest
')
What happened to the man who did so much about everything related to Java? As a matter of fact, here I want to tell you about how I turned from a Java follower into a hot supporter of Node.js and JavaScript.

I must say that what happened to me cannot be called a complete rejection of Java. I, over the past 3 years, have written quite a lot of Java code, have used Spring and Hibernate. Although I really like what I am doing in this area (I work in the solar industry, I do what I like to do, for example - I write requests for working with data from the energy sector), Java programming is now in my eyes lost its former glory.

Two years of development using Spring allowed me to clearly understand one important thing: trying to hide complex mechanisms does not lead to simplicity, it only leads to the appearance of even more complex structures.

Here, in brief, are the main ideas that I will touch on in this material:


About Java Development Issues


Some tools or objects are the result of many years of efforts by engineers to improve them. Programmers experience different ideas, remove unnecessary attributes, and as a result they get entities that have only what is needed to solve a certain task. Often these technologies have something inherent in a very attractive simplicity that hides powerful capabilities. This is not applicable to Java.

Spring is a popular framework for developing Java-based web applications.

The main goal of Spring, and, in particular, Spring Boot, is to provide an opportunity to use a pre-configured Java EE stack. The programmer who uses Spring should not, in order to create a complete system, take care of servlets, permanent data storage systems, application servers, and it is not yet known about anything. All these concerns are shifted to Spring’s shoulders, and the programmer is writing code that implements the logic of the application. For example, the JPARepository mechanisms are responsible for generating database queries for methods that look like findUserByFirstName . The programmer does not have to write the code of such methods. It is enough to pass the description of the method to the system, and Spring will do the rest.

All this sounds very good, it is pleasant to work in such a style, but - until some surprise happens.

I mean a situation where, for example, a Hibernate PersistentObjectException exception comes in with the message detached entity passed to persist . What does it mean? It took several days to figure it out. As it turned out, to describe everything in a very simplified way, this means that the JSON data received at the REST endpoint has ID fields with some values. Hibernate, again, without going into details, seeks to control ID values, and, as a result, throws out the above-described obscure exception. There are thousands of such error messages that are confusing and difficult to read. Considering the fact that there are whole cascades of subsystems based on each other in Spring, the Spring stack looks like a sworn enemy programmer who watches him and waits for the programmer to make the slightest mistake, and when this happens, he throws exceptions that are not compatible with normal operation of the application.

Next, you can immediately recall the longest stack traces. They represent several screens full of all sorts of abstract methods. Spring obviously creates the configuration needed to implement what is expressed in the code. Such a level of abstraction undoubtedly requires a considerable amount of auxiliary logic, which is aimed at finding everything necessary for the operation of the code, for example, in order to execute queries. And long stack traces are not necessarily bad. Such things are, rather, a symptom, leading to the question of how much auxiliary mechanisms create a load on the system.

How does the findUserByFirstName method findUserByFirstName , given that the programmer did not write the code for such a method? The framework needs to parse the name of the method, understand the programmer’s intention, create something like an abstract syntax tree, generate some SQL code, and so on. How does all this load the system? And all this exists only so that the programmer does not need to write code?

After you have to go through a few dozen times through something like a search for the meaning of the error described above, spending weeks trying to solve secrets, which, by and large, you shouldn’t guess, you can come to the same conclusion that I came to . Its meaning is that the attempt to hide complex mechanisms does not lead to simplicity, it only leads to the emergence of even more complex structures. The Node.js platform is much simpler.

The “Compatibility Matters” slogan concealed a great idea, according to which the most important feature of the Java platform was backward compatibility. We took it seriously, putting on t-shirts images similar to the one you can see below.


Backward compatibility is very important.

Of course, this level of attention to backward compatibility can be a source of constant anxiety, and from time to time it is useful to move away from old mechanisms that no longer benefit.

Java and Node.js


Spring and Java EE are overly complex. The Node.js platform on their background is perceived as a breath of fresh air. The first thing you notice about getting acquainted with Node.js is Ryan Dahl’s approach to developing the platform’s core. His experience told him that platforms using threads are needed to create complex, heavyweight systems. He was looking for something else, and spent a couple of years improving the set of basic mechanisms embodied in Node.js. The result is a lightweight system that characterizes a single thread of execution, an inventive use of anonymous JavaScript functions as asynchronous callbacks, and a runtime library that originally implements asynchronous mechanisms. The initial message when creating such a system was to provide high-performance event handling with the delivery of these events to the callback function.

Further, an important feature of Node.js is the use of JavaScript. There is a feeling that those who write to JS have a tendency to get rid of the template code, which makes it possible to clearly describe the intentions of the programmer.

As an example of the differences between Java and JavaScript, consider the implementation of listener functions (observers). In Java, to work with listeners, you need to create a specific instance of an abstract interface. This entails the use of cumbersome language constructs that hide the essence of what is happening. How to discern the intention of the programmer, hidden under the covers of the template code?

JavaScript uses simple anonymous functions instead. When implementing a listener, you do not need to look for a suitable abstract interface. It is enough, without the need to use a variety of auxiliary texts, to write the necessary code.

So, here is one important idea that can be derived from the analysis of the mechanisms described above: most programming languages ​​hide the programmer's intentions, which leads to the fact that the code is difficult to understand.

The solution for using callback functions offered by Node.js looks very attractive. But it is not without problems.

Problem solving and problem solving


In JavaScript, there have long been two problems associated with asynchronous programming. The first is what is called callback hell in Node.js. This problem lies in the fact that, in the course of development, it is easy to fall into a trap, built of deeply nested callback functions, where each level of nesting complicates the program, as well as processing the results of the code and errors. There was another problem related to this, the essence of which is that the JavaScript language mechanisms did not help the programmer to properly express the ideas of asynchronous code execution.

To simplify asynchronous development in JS, several libraries have emerged. But this is another example of an attempt to hide complex mechanisms, which only leads to the appearance of even more complex structures.

Consider an example:

 const async = require('async'); const fs = require('fs'); const cat = function(filez, fini) { async.eachSeries(filez, function(filenm, next) {   fs.readFile(filenm, 'utf8', function(err, data) {     if (err) return next(err);     process.stdout.write(data, 'utf8', function(err) {       if (err) next(err);       else next();     });   }); }, function(err) {   if (err) fini(err);   else fini(); }); }; cat(process.argv.slice(2), function(err) { if (err) console.error(err.stack); }); 

This is a nondescript imitation of the Unix cat . The async library is excellent in simplifying asynchronous call sequences. However, its use requires a large amount of template code that hides the programmer’s intent.

In essence, this code contains a loop. It is not written as a normal cycle, it does not use natural constructions describing cycles. Further, the results of executing the code and the errors it generates do not go where it would be right for them. They are locked in callbacks, and this is inconvenient. But, before the introduction of ES2015 / 2016 standards in Node.js, nothing better could be done.

If we rewrite this code taking into account new features, which, in particular, are available in Node.js 10.x, we get the following:

 const fs = require('fs').promises; async function cat(filenmz) { for (var filenm of filenmz) {   let data = await fs.readFile(filenm, 'utf8');   await new Promise((resolve, reject) => {     process.stdout.write(data, 'utf8', (err) => {       if (err) reject(err);       else resolve();     });   }); } } cat(process.argv.slice(2)).catch(err => {   console.error(err.stack); }); 

In this example, we used the async/await construct. Here the same asynchronous mechanisms are presented as in the previous example, but here the usual structures used in the organization of cycles are used. Working with the results and errors looks quite normal. Such code is easier to read and write. This approach makes it easy to understand the intent of the programmer.

The only drawback is that process.stdout.write does not have a Promise interface; as a result, this mechanism cannot be used in async functions without wrapping it in promise.

Now we can conclude that the problem of hell callbacks in JavaScript was solved in a way that differs from trying to hide complex mechanisms. Instead, changes were made to the language, which solved the problem itself and saved us from the inconvenience caused by the need to use large amounts of sample code in the temporary solution. In addition, using the async / await mechanism, the code simply became more beautiful.

We started this section with a discussion of the lack of Node.js, but an excellent solution to the problem of callback hell led to the fact that talking about shortcomings turned into a conversation about the strengths of Node.js and JavaScript.

Strong typing, interfaces, and imaginary code clarity


In those days, when I was engaged in protecting Java from all sorts of attacks, I stressed that strong typing allows you to write huge applications. At that time, the development of monolithic systems was in progress (there were no microservices, there was no Docker, and so on). Since Java is a strongly typed language, the Java compiler helps the programmer avoid many problems by preventing him from compiling the wrong code.

JavaScript, unlike Java, is not distinguished by strong typing. From this we can make an obvious conclusion that the programmer does not know exactly which objects he has to work with. How can a programmer know what to do, for example, with some object obtained from somewhere?

The reverse side of strong Java typing is the need to constantly perform template actions. The programmer constantly performs type casting or checks that everything is exactly as expected. The developer spends time writing code, does it with exceptional accuracy, uses considerable amounts of patterned constructions, and hopes that all this will help him save time by detecting and correcting early errors.

The problem of programming in a language with strict typing is so great that a programmer, with virtually no options, has to use a large, complex IDE. A simple code editor is not enough here. The only way to keep a Java programmer in adequate condition (with the exception of pizza) is to constantly show him drop-down lists containing the available fields of objects or descriptions of method parameters. This and other supporting mechanisms of IDEs such as Eclipse, NetBeans, or IntelliJ help in creating classes, facilitates refactoring and solving other problems.

And ... I will not talk about Maven. This is just a dreadful tool.

In JavaScript, variable types are not specified when they are declared, type casting is usually not used, and so on. As a result, the code is easier to read, but this state of affairs also means the risk of programming errors that are difficult to detect.

Whether the above applies to Java pluses or minuses depends on the point of view.

Ten years ago, I thought that all these difficulties justify themselves by giving the programmer more confidence in the code he writes. Today, I believe that strict typing increases the amount of work a programmer has and projects are much easier to develop in the same way as in JavaScript.

Fighting bugs with small modules that are easy to test


Node.js pushes the programmer to split his projects into small fragments, into so-called modules. Perhaps this fact will seem insignificant to you, but it partially solves the problem we just mentioned.

Here are the main characteristics of the module:


All this makes Node.js modules entities with clearly defined boundaries whose code is easy to write, read and test.

However, anxiety when working with JavaScript is caused by the fact that the lack of strong typing can easily lead to the fact that the code will do something wrong. In a small module aimed at solving some kind of narrow problem with clear boundaries, “something is not right” can affect only the code of the module itself. This leads to the fact that problems that can be caused by the lack of strong typing turn out to be locked in the module boundaries.

Another solution to the problem of dynamic typing in JavaScript is to thoroughly test the code.

The developer has to take a serious approach to testing, which takes away from him some of the benefits that come from the simplicity of the JS development process. Testing systems created by a JS programmer should find those errors that, developed by something like Java, could automatically find a compiler. You are writing tests for your JS applications?

For those who need a static typing system in JavaScript, it may be helpful to have a look at TypeScript. I do not use this language, but I have heard many good things about it. It is compatible with JavaScript and extends the language with a type control system and other useful features.

As a result, we can say that the use of a modular approach to development is the strength of Node.js and JavaScript.

Package management


I feel bad at the thought of Maven, so I can’t even write about it normally. And, as I understand it, Maven, without compromise, either love or hate.

The problem here is that in the Java environment there is no complete package management system. Maven packages exist, you can work with them normally, they are supported by Gradle. But how work with them is organized is not close to what the package management system for Node.js gives the developer.

In the world of Node.js, there are two excellent package managers that work closely with each other. At first, the only such tool was the npm repository and the command line tool of the same name.

Thanks to npm, we have a great scheme for describing package dependencies. Dependencies can be strict (for example, it is stated that version 1.2.3 of a certain package is needed only), or specified with several degrees of freedom - up to * , which means using the most recent version of a certain package.

The Node.js community has published hundreds of thousands of packages in the npm repository. In this case, using packages that are not in npm is as easy as packages from npm.

The npm system turned out so successful that not only Node.js server product developers use it, but also front-end programmers. Previously, there were tools like Bower used to manage packages. Bower was deprecated, and now you can find that all JS libraries for frontend development exist in the form of npm packages. Many support tools for client development, such as the Vue.js CLI and Webpack, are written as Node.js applications.

Another package management system for Node.js, yarn, loads packages from the npm repository and uses the same configuration files. The main advantage of yarn over the npm package manager is its higher speed.

The npm repository, regardless of whether you are working with it using the npm package manager or using the yarn package manager, is a powerful basis for making development for Node.js so simple and enjoyable.


Once, after I helped in the development of java.awt.Robot, I was inspired to create this thing. While the official Duke image consists of curves, RoboDuke is built from straight lines. Only the elbow joints of this robot are round.

Performance


Java, JavaScript . -, , . , , - .

Java, JavaScript . Java Node.js, . JavaScript . -.

JDK Sun/Oracle HotSpot — , -. , , , , , . HotSpot — , .

JavaScript, , JS-, , , - . , JavaScript . , , . , , Google Docs, . JS .

Node.js , V8 Google Chrome.

, Google, V8, . , V8 Crankshaft Turbofan.

— , , R Python. , , . JavaScript, , , JavaScript.

JavaScript , TensorFlow.js. API API TensorFlow Python, . , , , .

IBM, Node.js, , , Docker/Kubernetes. , Node.js Spring Boot. -, . , Node.js , , , V8.

, Node.js . . - , Node.js , . , «Node.js Web Development», , :


JavaScript , Node.js. — Node.js-. Node.js- node-gyp , . , Rust- Node.js.

WebAssembly , , JavaScript, . WebAssembly , JavaScript-. WebAssembly Node.js.

-


- (Rich Internet Applications, RIA) . , , ( ) JS-, .

, 20 . Sun Netscape Java- Netscape Navigator. JavaScript , , Java-. , Java-, — Java-. , . .

JavaScript , . RIA, , - Java -.

, RIA . Node.js , , , . JavaScript.

Here are some examples:


Java, , - -, JavaScript. , , - Sun Microsystems. Sun , . . Java- , Java Java Web Start. Java- Webstart-.

Java, , , IDE NetBeans Eclipse . Java , , Java.

JavaFX.

JavaFX, 10 , Sun iPhone. , Java, , , . Flash iOS. . JavaFX , , . - React, Vue.js .

JavaScript Node Java.

Java, - JavaONE. Java . , , , .


Java




Results


. «P-» (Perl, PHP, Python) Java, Node.js, Ruby, Haskell, Go, Rust, . .

, , , Java, Node.js, , , Node.js-. Java , Node.js . , , , Java, .

. , , Node.js - , - . . , XBRL-. XBRL Python, , , Python. , , , .

Dear readers! , , JavaScript - , - Node.js, .

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


All Articles