Common issues of frontend applications

Build process of a frontend application

I consulted several frontend projects in last few weeks and found out that, despite high availability and high quality of tools provided by open source community, most of them suffered from various problems that I’ll describe below. The most important part that frontend developers, who worked initially on that projects, does not seem to count those issues as problems that have to be solved. The result of such approach was my (and other developers) bad experience working with such applications.

I would like to point most common frontend problems I’ve noticed and provide ideas how to prevent them. I hope this writing will be useful for frontend engineers and architects, as well as team leads who have frontend application under their control. While applying ideas provided in this article, keep in mind that overall goal is to provide better developer’s (and final user’s) experience that will lead to better motivation, better quality, and better software created.

While there is gulp in examples, a similar approach can be worked out for grunt or your own build scripts with a little bit of google and stack overflow.

Compilation speed

The most common use case of developing applications is to make a change, throw a test, and see if this change was positive or it requires more effort. The amount of time between single change and receiving test result (no matter if it automatic or manual testing) is the biggest part of time that a developer spends working with an application (it is even bigger than coding itself if you go without TDD).

For example, if your application requires 10 seconds to compile before you will be able to open a browser and start clicking around, then you spend 10 seconds in vain for each test. Consider you can throw up to 100 tests a day and you’ll receive 1000 seconds loss (which is around 15 minutes a day and 60 hours a year). Also, the waiting time kills motivation and lowers focus of a developer, because them tend to start frustrating when they have plain waiting. This can produce an accompanying losses that could be significantly bigger than plain time losses.

I consider situation is good is when your application takes no more than 0.5 seconds to compile a single change you made in any file.

Two tasks run for single change taking total 0.35 seconds to complete

So, run only those tasks that should be run for single change; do not compile sass when modifying coffee and vise versa:

gulp.watch(‘js/**/*.*’, [‘compile-js’]);
gulp.watch(‘css/**/*.*’, [‘compile-css’]);

Recompile only those files that were changed (apparently it is one file for single save); there is gulp-changed plugin that will help you to do this:

gulp
.src(‘src/*.coffee’)
.pipe(changed(‘dist’, {extension: ‘.js’}))
.pipe(coffee(‘dist’))
.pipe(gulp.dest(‘dist’));

Do not concatenate or minify files for a development version of an application. Just do a minimal compilation that will allow testing. If you concatenate or build everything into one file, then you’ll end up with long build process sooner or later.

Do not compile files at request time (when a browser requires a file from a server) or on a browser side (this is the worst idea). It can be good while you have one or two source files, but when you’ll have 10 or more it’ll lead to degraded performance. Instead, compile files at the moment they were changed on hard drive.

Assets being built in browser result almost 10 seconds delay of document ready event firing

Also, do not include any sources from CDN’s for a development version. Serving files locally always faster than serving it from CDN’s (also, CDN can cause unexpected delays for developers in different locations). Also, having source concatenated into one file and serving it from an own server without a CDN is faster than serving two files from a server and from a CDN.

Build process feedback

Any error occurred during compilation should be reported explicitly in order to provide remarkable developer’s experience. It is quite often for developers to frustrate around “my code does not work”, while they just dealing with an old version of compiled file, because a file was not recompiled due to syntax mistake. Or worse, watcher task have stopped because there was a unhandled compilation error.

Use gulp-plumber for proper error handling:

gulp
.src(‘src/*.coffee’)
.pipe(plumber())
.pipe(coffee())
.pipe(gulp.dest(‘dist’))

Deliver message to developer explicitly on error by writing it to a file and including this file to your application:

.pipe(plumber({
errorHandler: function(error) {
util.log(util.colors.red('!!! ERROR !!!'));
console.log(error.toString());
require('fs').writeFileSync(
'dist/js/error.js',
'alert("ERROR! Check the logs and clean error.js");'
);
},
}))
Explicit error reporting in logs

Make an explicit exit code when making release; it is really important to notify build system that something went wrong on error, so build system will stop a build and will not deploy a broken frontend application:

.pipe(plumber({
errorHandler: function(error) {
// …
// consider we build with: RELEASE=yes gulp release
if(process.env.RELEASE) {
util.log(util.colors.red('BUILD FAILED'));
process.exit(1);
}
},
}))
Note that build explicitly reports failure and return correct exit code

It should be explicitly clear what is going on inside build process at each point of time, so any problems could be resolved quickly; it could be achieved by using gulp-debug plugin:

gulp
.src('src/*.coffee')
.pipe(coffee())
.pipe(debug({title: 'compile-js:'}))
.pipe(gulp.dest('dist'))

Gulp debug reports that config was recompiled as well as other javascript files

Source code access

Developers should always have access to source code files from their debug tools. Otherwise, they can frustrate by searching specific file across the project instead of opening it directly. For example, if there is main.scss main.css compilation, developer have to see main.scss location in a developer’s console.

It is clear where specific SCSS declarations made; a developer can even edit this file through browser development tools

In order to achieve clearness in debugging stuff, so-called source map files should be used. This files contains information about relations between a source and a compiled code and used by a browser in order to help a developer. For example, a browser will point where an error was occurred or where specific CSS declaration located in SCSS file. Gladly there is gulp-sourcemaps plugin that do all stuff:

gulp
.src('src/*.coffee')
.pipe(sourcemaps.init())
.pipe(coffee())
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist'));

I observed a common approach to copy minified and concatenated files from the npm and bower packages into the project. This approach makes debug process really hard, because a developer needs to access unminified source code in order to put breakpoints, run through it, understand it and consider modification locations.

It is hard to debug such files; take care and include unminified versions of file into developer’s version of project

There is only several bytes change that brings a big advantage to debug process:

gulp
// .src('node_modules/angular/angular.min.js') // nope!
.src('node_modules/angular/angular.js') // yes, please!
.pipe(gulp.dest('dist'));

It is common for frontend projects to have multiple include points for the single file attached to a project. For example, if I need to add a file to project I have to add it to gulp-file and to index.slim (sometimes there are more endpoints, e.g. for release purposes). On my experience more than one endpoint cause troubles because developers attach a file to one end point and do not attach it to another.

My advice is to have a single file where all required files described. Build scripts should extract file lists from that file and compile them for given environment. Also, it’ll be good to have file lists taken from filesystem so it’ll be enough just to create a source file without manually adding it to compilation list.

Here is single location of including any files into the project

Surplus or untracked sources

It is quite common to use themes from themeforest.net or other websites. While it is a good idea to use an existed theme, I’ve seen many applications that just connect every single file from such a theme and don’t use most of them. This result to a bigger size of applications and to more expensive maintenance (and to worse developer’s experience).

Always make sure that every single file attached to a project are used somewhere. Do not attach files that are not used (for example, do not attach chart.js if you don’t have a single chart in a project). Do not attach libraries “for the future”, but attach it in the commit where a feature that requires that library is implemented.

Also, do not copy files or libraries thoughtlessly from a theme. Check if there is npm-package for given library before copying a file. If there is npm or bower packages for given files: use this packages instead of adding files to project. Less code in a repository is better. It makes dependency management easier and allows developers to refer a documentation on npmjs.com or bower.io.

Keep you package.json and bower.json compact and think twice before including a library here. Fewer libraries in package.json is less code will be sent to a user, that will lead to better experience (but do not overdo — try to find a balance that will suit you well). And consider using npm only (it is rare for a library not to present on npm).

Release

It is common to have different build cycles for development and production versions of an application, however, a lot of frontend developers try to save their effort and to make one version that will suit both developers and users.

It is not possible to satisfy both of them with one version because developers and users have a different situation and different requirements. While developers can download megabytes of sources without noticing it, they need access to plain sources and fast build cycles. Users are sensitive to sources sizes, but they don’t care how much time it takes to build that sources and what is in sources itself (as they typically not going to read sources).

Make sure your project has separate “release” command. It is quite easy to create such command and will not take much time, but significantly improve developer’s and user’s experience. Make sure that release command produces minified assets with a unique filename (which is a most reliable way to prevent assets caching).

Ready to release frontend application

Also, make sure that you have at least several automatic acceptance tests fired before you deploy a new version of an application (it should test not only frontend but full stack integration). It is crucial to have such tests so developers can focus on testing logic but not integration. It is easy to create it with contemporary open source stack like Protractor, Capybara and Jenkins and professional testing tools like Travis and Circle.

The build made built with travis-ci.org

Conclusion

I hope that advises and ideas I listed in this article will lead you and your colleagues to better code and better experience. I’ll be happy to answer any questions in comments or by email leonid@shagabutdinov.com.

P.S. If you use Angular and Coffee, star my angular-class-coffee library that I made in order to avoid duplication of dependencies definition in Angular source files.