Hi, my name is Igor (
iamo0 ), I am a senior front-end developer in Isletka. I deal with our main product: site
Ostrovok.ru . With the help of our website, thousands of people book hotels daily, so it is very important for us that the quality of our product is at its best. And for this you need not be distracted by all sorts of little things and be able to effectively solve the tasks.
I'll tell you how we organized the process of front-end development in such a way that it would be possible to solve the set tasks without thinking about the means of solving them, focusing on the task itself.
I do not pretend that my story will tear the covers off or become a real revelation. I want to share with you the experience with large applications gained by the developers of the Island.
Teamwork
Bus number
The team involved in the front end of Ostrovka employs nine people. As a rule, even two developers find it hard to come to a consensus on how to write code correctly (
hello, Extranet ;-), to say nothing of the nine. On the other hand, you need to think about the architecture of the project to be clear and precise, the code is easy to read and maintain. It is important that a newcomer who has recently joined a team or a person who has to switch to another project can quickly understand the code.
')
Managers and developers have the term “bus number”. This is the number of developers who have to knock down a bus so that the project can not be supported. In the worst case, the autbus number is one. Our task is to maximize the bus number, ideally, it should be equal to the number of developers.
A high bus number is easily achieved if all developers write code in the same style and know what is happening in those parts of the project that they do not do.
Guidelines
For developers to write code in the same style, there are guidelines. On one of our internal resources, any developer can find the most detailed JavaScript, HTML and CSS gaydlans, which include not only code formatting rules, but also describe the features of our project's ecosystem: where and how we store files, how we organize the project assembly and Some rules for writing code.
One of the most important rules used in our project is the mandatory documentation of the code using jsDoc (
http://code.google.com/p/jsdoc-toolkit/ ,
https://developers.google.com/closure/compiler/docs / js-for-compiler ). Using jsDoc provides great benefits. First, the readability of the code increases. Secondly, in many IDEs it’s incredibly convenient to navigate a project that uses jsDoc. Those who
like to shoot at their feet use simple text editors, like vim, can compile the documentation into HTML files using the console utility that goes to jsDoc-toolkit. Well, finally, in order to write jsDoc correctly, the developer has to sort it all out in his head, and that never hurts.
Code Review
The second condition for increasing the bus number - developers' knowledge of the details of the code of other parts of the project - is achieved using a code-review system, which in Islet is called WTF (the first reaction of any developer who does not look at his code).
Since in our team everything is built on trust and there are no cruel bosses with rods, we don’t have any one person who would follow the code of all developers, on the contrary, we all know about everyone. Before you put the code, the developer himself asks someone from his comrades to check his code. Firstly, it has a good effect on the responsibility of both the verifier and the verifier, and secondly, each of the developers knows what and how his neighbor does, because he repeatedly saw his code and gave permission to publish himself.
Design features
The approach to the development of each company is built from the features of the project and the team working on the project. In the Island, a strong enough emphasis is placed on the front-end. Such an approach has developed historically, besides, this is favored by a large number of developers and a well-established system.
With the help of the front-end, we solve a large number of seemingly atypical tasks, which allows us to implement a powerful user interface. For example, our search results work in such a way that almost all the work with information on it takes place on the client side. The back-end is engaged in providing a list of available hotels in the form of JSON, while the front-end is already engaged in displaying a list of hotels, drawing hotels on a map, sorting and filtering.
Try changing the filters on our search results, for example, the number of stars or the cost of rooms - the list of hotels will be updated instantly, because all calculations are done on the client. Despite the fact that we work with large amounts of data - for example, in London we have more than two thousand hotels - the site responds to user requests very quickly.
Javascript
Such a strong focus on the client side requires that the code be carefully organized and structured. We chose a modular approach to development based on OOP. This means that we actively use classes, inheritance, weak binding, and other OOP charm provided by JavaScript.
For each page we have a separate JavaScript class that contains some nested modules, which, in turn, can also be split into infinity.
We have created a base module class. In addition to working with events, he knows almost nothing, but ultimately all other modules are inherited, be it a page, a search form controller, or, for example, a calendar. It turns out that on every page we have an input point at which an instance of the class of this page is simply created and initialized.
To illustrate the approach, I’ll give a rather crude code example. Imagine that we have a page with a form to enter an email for a subscription.
We organize this code like this:
Now, to make it work, it is enough for me to put the following code in the HTML pages, of course, first connecting all the necessary files:
<script> new ota.Page; </script>
In this example, I purposely did not use any libraries to show the essence of our approach and not to reduce the conversation to the tools used.
Of course, to solve such a small problem, the code turned out to be quite a lot, but if we imagine that there is a search form on this page; list of hotels with endless scrolling; map, with marked hotels, and markers on the map are highlighted when you hover the cursor on the hotel in the list; several filters, the state change of which should cause an update of both the list and the map — such an approach becomes justified and very convenient.
Plus, with this approach to development, the project code becomes flexible and easily adaptable to non-trivial situations. For example, if the developer is tasked with customizing the issuance page for users who came to our site for a search query, and not from our main page, the task is limited to creating a child class and redefining several methods of the parent class.
Customized and regular search results for Moscow (highres).
Layout and styles
The careful organization of the code, of course, concerns not only JavaScript, but also the layout: server and client templates and CSS. The layout requirements are the same as for the JavaScript application: modularity, flexibility, customizability, painless inheritance. We are lucky that there is a well-established and proven system of code organization that fits these requirements. This is BEM (
http://ru.bem.info/method/ ,
http://clubs.ya.ru/bem/ ).
For markup and styles use the BEM-methodology, but with some features. Firstly, BEM is not very well suited to our system for working with JavaScript, so js files are stored separately and organized in their own order, and secondly, for styles, we use SCSS (
http://sass-lang.com/ ) instead of simple CSS.
Client Templates
Due to the fact that most of the logic is implemented in JavaScript, we actively use client templates. At first, we used the template engine built into the underscore.js library (
http://underscorejs.org/ ), but we quickly realized that it was inconvenient. Firstly, I wanted to store the templates in separate files, and secondly, I wanted the templates to be not string variables in JavaScript, in which to write markup in text editors like vim like a penalty.
In addition, we thought that it would be nice to make these templates precompiled so that the user's machine resources are not wasted on complex string conversions.
Our infrastructure department came to our rescue by making a template engine for us. We called it JST - JavaScript Templates. The main syntax was left from underscore.js, but made so that the templates are stored in external files with the jst extension and automatically compiled into a JavaScript function even at the stage of building the project. Compiled templates are connected to the project as usual js-files.
<ul> <% elements.forEach(element, function(el, index) { %> <li><%= element.name %></li> <% }) %> </ul>
For templates, a separate namespace is entered, and the template identifier is its path within the project.
Working with client templates has become incredibly convenient for both those who are engaged in layout, and for those who write JavaScript.
Dependencies and build project
Since for each JavaScript class, there is its own dependency system, it is almost impossible to connect all the necessary files by hand, besides, somehow you need to take care of code minification. For such a complex system that requires the creation of a large number of files, you need an effective build system. Again, our infrastructure department helped us to create this system.
The service that builds the project is called mediagenerator, it was written by one of our developers. This service collects dependencies within the project based on the use of special tags.
Let's go back to the example. To connect mainly for the page to the page.js file, the base.js and searchform.js files, you need to register the dependencies right inside page.js. They are written in jsDoc, in the tag @require. It turns out that in order to include the necessary files, it’s enough to add two tags in the page.js file, anywhere, for example, in the @fileoverview block:
Everything. Now in debug mode, two <script> tags will be added to the page, which will include the necessary files. And in production mode, the media generator will glue all these three files into one and pass through the minifiers. Of course, he knows how to keep track of complex dependencies and never connect one file several times.
In addition, it is the media generator that compiles jst-templates and SCSS, caches pictures and does all the hard work of assembling a project. Therefore, front-end developers can only focus on the problem being solved.
Where are we going
There is an old programmer joke: any rather complex C or Fortran program contains a rewritten, unspecified, buggy and slow implementation of half of Common Lisp.
For front-end developers, this joke can be rephrased like this: any fairly complex project written on jQuery, Backbone or Knockout.js (
http://jquery.com/ ,
http://backbonejs.org/ or
http: // knockoutjs. com / ), contains a newly written, buggy and slow implementation of half of Google Closure Tools (
https://developers.google.com/closure/ ).
Indeed, all that I’ve talked about here: modularity, OOP-approach, the presence of a basic set of modules from which to inherit, work with styles, precompiled templates are in the Google Closure ecosystem. In addition to the Google Closure Library JavaScript library, this system also includes the Google Closure Compiler JS minifiers; Soy template engine that compiles both into server and client templates; and a style language integrated with the Google Closure Compiler and therefore allows minimizing even the names of css-classes (the blue dream of any developer using BEM).
Closure Tools has been developed by Google for their own projects since 2005. At first, the library was written for the needs of the GMail mailer, and then with its use all the other Google projects were rewritten. The code base of the JavaScript library alone, the Closure Library, weighs about 8 MB and contains everything you need to develop complex projects.
This library has a rather complicated infrastructure and a high entry threshold. It will be quite difficult for a programmer who is used to using the solutions currently on the market to change to Closure, but using this library can significantly improve the quality of the project.
Everything in this world has already been written by some Pushkin: most of the problems many developers of medium / large projects face when developing them from scratch have already been solved by Google engineers in the process of writing their infinite number of services.
This fall, a new version of the Island is being prepared for release, which is currently being developed with the use of Google Closure. The main feature of the new Island is that it is a single-page application. This means that all the logic for switching between pages, working with the address bar, loading data and rendering pages will move to the client part. The complexity of the project development is more than offset by the speed of the site and a more convenient user interface.
Of course, with the transition to Google Closure, we had to change something in our development approach, for example, we had to abandon BEM due to some of the shortcomings of this methodology and its weak compatibility with our infrastructure for the Google Closure project, but our views on building project we have remained the same.