A very subjective overview of common practices in modern front-end development
A few thoughts and reflections on what front-end development generally looks like in 2017, what practices are most common and what is the baseline of knowledge an average front-end developer is expected to possess.
- write functional code,
- write universal code,
- use unit testing,
- use static type checking,
- avoid coupling,
- keep the number of dependencies as low as possible, and
- aim at smallest total size of assets possible.
Just a few years ago, it was quite easy to see a project done in jQuery, Backbone or, later, Angular and Ember frameworks. Mutating an object prototype was quite common, and replacing context of a function in order to make “this” refer to something you need was the most preferred solution. An object in global scope that ruled all the controllers didn’t raise any concerns. (maybe it did, but what other options did we have?)
These days, the right way of doing web means the code is more deterministic, and sometimes quite verbose. A little more typing is better than time spent debugging.
- immutable data structures,
- pure functions,
- and generally more diversified code that consists of smaller pieces rather than of huge object declarations.
As a positive side-effect, the code gets self-documented over time automatically and requires lesser amount of comments. As a side-effect, it requires relatively short time to pick up by freshly on-boarded engineers, regardless of their general knowledge of front-end technologies.
This makes it way easier to enable server-side rendering, for one. Which makes it easier to serve visitors better, humans or robots. Which makes the web be a powerful driver in marketing, and eventually, greater optimization in the conversion funnel.
Well, of course.
There are two remarkable tools these days that are worth using with the front-end code: ESLint and Prettier.
It’s especially worth the effort for a team to use ESLint to keep code styling consistent and aid the development process. The best part of it? Well, for one, there is eslint-import-plugin and its “import/no-unresolved” rule. Ensuring the project compiles because all the references are valid is critical for productivity, and of course it’s always nice to let your editor do this kind of task.
Prettier, on the other hand, doesn’t recommend. It formats the code as it feels should be the right way. It’s constantly evolving and does weird things sometimes (try to use it with multi-line template string) but it’s promising and it does exactly the job needed: without any discussion and painful search for consensus, just normalize the code formatting so that everybody gets used to exactly the same visual patterns.
Unit testing is small effort, big improvement. Originally used in teams to avoid getting code broken without noticing, it should be practiced in front-end development starting on day 1. In modern era of decoupling the code from browser DOM API, it’s easier than ever.
Static type checking
Yes, the pendulum swung there again. Static type checking is back.
But, really, here’s Flow for that. It does amazing job detecting discrepancies in types between modules and giving valuable information about your code that you might miss in haste, especially in the code that is referred to from many different places.
A classic example would be React and React Router. It’s a perfect example of coupling: you just can’t use the latter without the former. They are coupled.
On the other hand, Universal Router is completely decoupled from React. You can use it with any other library or just alone. You can use it on the server. What it provides is a simple as a stone library that takes two arguments, a path and a list of possible routes, and returns the matching route. No black magic. Works like a charm. A pure function with clear logic.
Same goes for setting meta-tags and other head information: there is React Helmet for that, but it’s coupled with React again. It acts as a component while it’s clearly not. It’s a function that outputs the same component and causes a bunch of side-effects to update properties in DOM in “head” section of the document.
It’s not just a React thing. It is a common pattern. And it is possible to do better, with no extra effort.
What distinguishes modern front-end development practices from them 10 years ago is that, along with FP, engineers should be (and tend to be) really careful about bringing any sorts of libs that depend on each other. Other engineers who will be taking over that codebase in a few months or years will be really happy to see clear code that is hard to break.
Minimum number of dependencies
There’s number of libraries every front-end developer relies on these days, including Babel for latest ES6+ language features and Webpack for bundling all the things into a single JS file. Which is great, these tools are hard to replace or reimplement on one’s own. But when it comes to utility functions or reusable logic, well, it can go really bad.
Whenever possible, consider these simple things:
- static type checking already ensures that a certain variable has expected type,
- whenever it’s hardly possible, for example in parsing and traversing data from an API, being cautious and using default values and doing some extra type checking in run-time works, and
- you really, really don’t need a library because there’s already language syntax for that.
Every library added to a project comes with the running cost of maintenance and some extra cost of bloating bundle size. A library that implements X, Y and Z, when you only use Z, still brings all three with it!
There’s a nice library that fully depicts our philosophy, blunderscore. The one that shows that many things we blindly relied on in the past are actually not worth extra kilobytes delivered to every browser that hits our servers.
The whole point of front-end is to bring information and experience to the user, to the visitor of a web resource. Even if it is a web app, it is likely updated on a regular basis, so the assets every user downloads are always new. In this case the user will never enjoy browser caching.
What should regularly appear in focus is that, more often than not, people experience all sorts of bad connection. I like this line from “Most of the web really sucks if you have a slow connection” post:
Complaining that people don’t care about performance like they used to and that we’re letting bloat slow things down for no good reason is “old man yells at cloud” territory; I probably sound like that dude who complains that his word processor, which used to take 1MB of RAM, takes 1GB of RAM. Sure, that could be trimmed down, but there’s a real cost to spending time doing optimization and even a $300 laptop comes with 2GB of RAM, so why bother? But it’s not quite the same situation — it’s not just nerds like me who care about web performance. In the U.S., AOL alone had over 2 million dialup users in 2015. Outside of the U.S., there are even more people with slow connections.
Regarding websites and all sorts of landing pages, the results may be even worse: they get opened so little times, by so many visitors. Almost none of them will ever enjoy that cached version of assets because it’s simply their first visit. Bring them a 500KB JS bundle and be sure that growing bounce rate will ruin all your hopes.
It’s really worth investing in discovery of ways to minimize the bundle size, and of course it may sometimes affect the scope and set of features brought to the user.
I’m curious about what’s coming to the front-end next. It already is cheaper than before to create truly great experience on the web and maintain clean and scalable code at the same time. And it’s feels to me that front-end has become “a real job” only in the last 5–10 years, and was more like a form of punishment before. So buckle up and drive!