Read my book

I wrote books about Webpack and React. Check them out!

Tuesday, January 6, 2015

Thoughts on the Future of Web Development

Since my previous post about React.js during the Summer, I've had time to level up my development skills and reflect. You could say frontend development tools move forward quite fast. The same goes for backend of course.

On Build Tools

During the past few years I've moved from Grunt to Gulp and Browserify and then onto Webpack. The smart guys have stuck with Make. You can even write simple configuration directly to package.json scripts field but I tend to do that only in panic. My sweet spot is a combination of Gulp and Webpack.

Grunt - It's Magic

The problem with Grunt is that it's filled with magic. That's never good. You don't want to have to maintain a three hundred line Gruntfile. It's doable but not particularly fun.

Gulp is a step towards something better. After all it's just about piping. You can pick it up in ten minutes. Gulp isn't without its issues but it's a significant step ahead. Given it's just JavaScript, you can always hack it if the going gets too tough.

Browserify - a Step Ahead

Compared to RequireJS, which I blogged about years ago, Browserify is a step ahead. The primary benefit over RequireJS is the fact that you can continue writing code in CommonJS module format. You can also directly hook into NPM infrastructure which is a massive bonus.

Webpack - The Holy Grail?

Webpack can be considered the next logical step. What if instead of bundling just JavaScript you had a tool that could bundle pretty much anything including CSS, LESS, SASS, CoffeeScript, Jade, whatnot? Well, this is exactly what Webpack is meant for. Instead of having to build configuration in your Gruntfile or Gulpfile you can just let Webpack deal with it.

You still get the goodies Browserify gives you (NPM, bundling) and then some!

It can even create bundles per page for you. No longer you are forced to download everything on the first load. Instead it will split up the source appropriately and provide partial loads. This can improve site performance massively especially if you have a lot of dependencies split on multiple views. Pete Hunt's guide to Webpack covers the basic approach. I'm aware you might be able to achieve something similar using other tooling but so far this seems very novel approach to me.

Webpack works very well with Dan Abramov's react-hot-loader and takes your React development to the next level. Developing using good tooling makes you work so much faster it's almost unbelievable.

As I'm not an absolute guru with Webpack yet I prefer to use some other tooling, such as Gulp, to copy distribution files around. No doubt there are ways to manage just with Webpack, though, but right tools for right tasks and all that.

The Future

It is difficult to say where build tools might be moving. Perhaps yet another tool comes out and kicks Gulp in shins in turn? Too early to say. Webpack seems to solve the biggest issue for me and no doubt will become more popular as people discover it.

Hot loading might become more than just a development goodie. At some point people will start doing hot loading in production and people will receive updates to JavaScript as they use the app without having to reload. No doubt that opens new cans of worms but in theory that sounds very fun!

On Libraries and Frameworks

On library/framework side the route has been from jQuery to Angular and finally React and friends. You could say jQuery is the PHP of JavaScript. It gets the job done and it's everywhere. Unfortunately it doesn't scale that well for larger scale development. If you need to spruce things up a bit on a static site it's a good pick but I wouldn't develop a JavaScript driven site using it if I can avoid that.

Angular over jQuery

Angular can be considered the next logical step over jQuery and you can even use them together. Sometimes you might want to avoid jQuery altogether. In fact often it's quite trivial to implement something in Angular that would require a yet another plugin in jQuery. I would say Angular is a very good fit for small projects and prototypes. I have my doubts about scaling.

Given Angular is a framework it provides tons of functionality. The problems begin once you hit the boundaries. What if instead of loading each and every dependency the way Angular expects you want to start loading them dynamically per page? Let's just say you have just found a world of pain.

This is a recurring theme during explorations to the Angular world. It works just fine until you hit some sharp edge and hurt yourself. Particularly directives hide a lot of complexity and it's easy to get them wrong. Performance-wise watchers can bite you and you will need to be very careful with them. It may be better to avoid them altogether and consider some alternative approaches.

In fact people are experimenting with ways to simplify Angular development by borrowing ideas from the world of React. Two-way binding isn't your friend always. Some might even go as far and say it's an anti-pattern and I agree. If you can get something done with one-way binding, prefer that to two-way. It's just less headache for everyone.

React.js over Angular

The lack of two-way binding without helpers is one of the strong points of React. Given the flow goes to one direction, it is easy to reason about. As vanilla React deals with just the view portion of an application, you will eventually run into questions like how to deal with models, how should I share data between my components and so on.

To make React shine, you will need to complement with a couple of libraries. I've stuck with react-router, axios (http client) and Reflux. In a future stack I might replace axios with a Swagger client that will generate the frontend API for me automatically. I'll get back to Swagger in a bit.

Flux Architecture and Reflux

The Flux architecture starts where vanilla React stops. In short its implementations and derivatives allow you to scale up from mere components. I have found Reflux a particularly light and smart implementation. It answers to the problems I highlighted very effectively. The idea is quite simple. Your components may trigger Actions. Actions in turn modify Stores in some way. The state of stores gets propagated to your components and the cycle is complete.

Let's say we're modeling selection. I would define a SelectionAction and a SelectionStore. SelectionAction would contain actions select/deselect (accepts item to select/deselect). SelectionStore would maintain the state and on change let components know it changed. In vanilla React the state would be within components themselves. Here we have effectively extracted it out.

By default we are dealing with Singletons so to avoid sharing state you would have to create separate instances but this is more of a special case. Another thing to keep in mind is that if you want to deal with asynchronous operations (say backend query), you should implement these in a particular way.

Reflux provides a preEmit hook for this purpose. In case we implement a basic operation like fetch to initialize our Store, we would define three actions: fetch, fetchComplete, fetchError. fetchComplete and fetchError would then get triggered within the preEmit hook of fetch depending on the result. This in turn would cause our Store to either populate itself or deal with the error somehow.

Error in turn could be passed to some other place, say ErrorStore. You could then listen to that and show the errors to the user, log them and so on. The approach has definite power in it.

Of course you would have to play around with React and Reflux to appreciate the approach. Initially it might feel that you are writing a lot of code for nothing but that's not the point. The goal here is not to minimize the amount of code written but rather to make it easy to follow and reason about. This is something that can get obscured in the Angular and jQuery world unless you are careful.

The Future

It feels like React and Reflux are steps towards a better future. So far I haven't had to worry about performance when dealing with React. There have been gotchas of course and the way you need to think in is quite different than what you might have gotten used to. The approach forces you to keep your entities small and pushes towards components. The cognitive load for creating new components is lower than in Angular as there are less concepts to worry about.

One of the main benefits of React is that it allows you to develop isomorphic JavaScript. Initial attempts have been made to allow Reflux support isomorphism as well. Instead of throwing a bit of HTML and JS to the client and expecting the client to construct the UI using JavaScript, in isomorphic approach we let the backend render HTML and perform initial queries needed. The frontend will then continue from this.

It's back to the same old but this time we are better prepared and gain benefits from the both world. Performance is better and SEO is improved. In a world where latency and poor SEO means lost sales and poorer visibility, what is there not to like?

If you want to take a peek at post-React world, you should study Cycle. There is still room for improvement and perhaps React was just a start. It would not surprise me a lot if it started feeling obsolete within a year or so.

On Backend

As frontend development has become more prominent during the past few years, the purpose of backend has changed. Now it's more about providing a sane API for frontend. RESTful patterns, HATEOAS and whatnot have appeared. You still have to deal with some basic concerns here such as authentication, authorization, business logic, validation and databases. In addition it would be awesome if there was decent documentation available.

Swagger - Definition for Your API

Lately I have been benchmarking Swagger. It is a definition that builds on top of JSON Schema. In short Swagger can be used to describe your API. Various tools can be developed on top of this description. For instance you get interactive API documentation and frontend API client for free.

Depending on the tooling you choose there is of course actual work to do. You will still need to deal with plenty of concerns but using a definition such as Swagger has potential to simplify work. Using a tool like this avoids the pain of having to maintain documentation that is separate from your API. You could of course generate one based on an existing API but that's still extra work that can be avoided.

Furthermore the approach has potential to simplify validation a lot. Given each data model is described in JSON Schema, you can validate against the same schema in both frontend and backend. If the schema changes, the code doesn't have to change necessarily. In ideal world migrations could be generated based on schema changes (JSON Diff?) and propagated to database automatically. In simple cases even databases could be generated without having to maintain duplicate definitions.

The Future

It would not surprise me a lot if usage of definitions like Swagger became more common. Especially when you are working alone or in a small team, you will want to avoid waste. Tools like this have a great potential to do that and allow you to be more agile and responsive towards changes.

From frontend point of view having an API definition simplifies things as it means you don't need to maintain a separate API client. You just generate one based on the definition. Furthermore the definition gives you something to fuzz with. This in turn can be used to improve API quality and security.

Conclusion

Web development moves forward fast. It doesn't take long for new technologies to appear and old ones to stagnate. In a couple of short years we've gone through a couple of build tools and there is no end in sight. I do wonder what on earth could replace Webpack and how?

Just when it looked like Angular had "won", backwards incompatibility of Angular 2.0 was announced. I have a feeling that might have stolen their thunder especially given the release date is still about a year away. In the meanwhile library based approaches will have time to evolve. I would bet on React and friends.

On the backend side approaches like Swagger seem very promising. They take away some complexity while providing a lot if you have patience and time to write out a formal definition for your API. You will have to do that eventually so why not to start with it? This doesn't answer to the problem of API evolution but it's a starting point and much better than nothing!