Think First, Code Later
This is a story that’s best told from the end, and it’s probably one you’re all familiar with.
You, or someone just like you, have been building a website, probably as part of a skilled and capable team. You’re a front-end developer, focusing on JavaScript – it’s either your sole responsibility or shared around. It’s quite a big job, been going on for months, and at last it feels like you’re reaching the end of it.
But, in a brief moment of downtime, you step back and take a look at the code as a whole. You notice that the folder called “jQuery plugins” suddenly looks rather full, and maybe there’s evidence of several methods of doing the same thing; there are loads of little niggly fixes in the bug tracker; and every place you use Ajax the structure of the data is slightly different. You sigh, and your shoulders droop slightly, and you think “Yeah, we’ll do that more cleanly next time.”
The thing is, you probably already know how to rewrite the start of this story to make the ending work better. This situation is not really anyone’s fault – it’s just an accumulation of all the things you decided along the way, all the things you agreed you’d fix later that have disappeared into the black hole of technical debt, and accomodating all the “can we just…?” requests from around the team and the client.
So, the solution to this is easy, right? More interminable planning meetings, more tightly controlled and documented specifications, less freedom to innovate, to try out new ideas and enjoy what you’re doing.
Wait, that sounds even less fun than the old way.
Minimum viable planning
Actually, planning and specifications are exactly what you need, but the way you go about them can make a real difference, both to the quality of your code, and the quality of your life as a developer. It can be as simple as being a little more thoughtful before starting on any new piece of functionality. Involve your whole team if possible, or at least those working on what you’re doing. Canvass opinions and work out what the solution to the problem might look like first, rather than coding speculatively to find out.
There are easy ways you can get into this habit of putting the thought and design up front, and it doesn’t have to mean spending more time on the project as a whole. It also doesn’t have to result in reams of functional specifications. Instead, let the code itself form the specification.
As JavaScript applications become more complex, unit testing is becoming ever more important. So embrace it, whether you prefer QUnit, or Mocha, or any of the other JavaScript testing frameworks out there. The TDD (or test-driven development) pattern is all about writing the tests first and then writing functional code to pass those tests; or, if you prefer, code that meets the specification given by the tests.
Sounds like a hassle at first, but once you get into the rhythm of it you should find that the time spent writing tests up front is no greater, and often significantly less, than the time you would have spent fixing bugs afterwards.
If what you’re working on requires an API between client and server (usually Ajax but this can apply to any method of sending or receiving data) then spend a bit of time with the back-end developer to design the data contracts, before either of you cut any code. Work out what the API endpoints are going to be, and what the data structure you’ll get back from a certain endpoint looks like. A mock JSON object documented on a wiki is enough and it can be atomic. Don’t worry about planning the entire project at once, just plan enough to get on with your current tasks.
Definition in this way doesn’t have to make your API immutable – change is still fine – but if you know roughly where you’re heading, then not only can your team’s efforts become more parallel, but you’re far more likely to have an easier time making it all work. And again, you have a specification – the shape of the data – to write your JavaScript against.
Putting everything together, you end up with a logical flow of development, from the specification agreed with the client (your backlog), to the specification agreed with your team (the API contract design), to the specification agreed with your code (your unit tests). Hopefully, there will be ample clues in all of this to inform your front-end library choices, because by then you should have a better picture of what you’re going to need.
What the framework?
As a JavaScript developer predominantly, these are the choices I’m particularly interested in – how and why you use JavaScript libraries and frameworks, both what you expect from them and what you actually get.
If we look back at how web development, and specifically JavaScript development has progressed – from the earliest days of using lines and lines of Dreamweaver code-barf to make an image rollover effect, to today’s large frameworks that handle working with the DOM, Ajax communication and visual effects all in one hit – the purpose of it is clear: to smooth over the inconsistent bumps between browsers and give a solid, reliable, predictable base on which to put our desired functionality.
Understanding what we expect the language as a specification to do, and matching that to what we observe browsers actually doing, and then smoothing out the differences, is a big job. Since the language and the implementations are also changing as we go along, it also feels like a never-ending job. So make full use of this valuable effort. Use jQuery or YUI or anything else you’re comfortable with, but it still pays to think early on about what you need your library to do and what the best choice is to meet that need.
I’ve come in to projects as a fixer and found, to take a recent example, that jQuery UI was being used just to provide a date picker and a modal effect. That’s a lot of code weight to provide two fairly simple pieces of functionality that could easily be covered by smaller plugins. Which isn’t to say that jQuery UI itself is a bad choice, but I could see that it had been included late on just to do those things, whereas a more considered approach would have been to put the library in early and use it more universally.
There are other choices, too. If you automatically throw in jQuery (or whatever your favourite main library is) to a small site with limited functionality, you might only touch a tiny fraction of its scope. In my own development I started looking at what I actually needed from a JavaScript library. For a simple project like What the Framework?, all jQuery needed to do was listen for .ready()
and then perform some light DOM selection before handing over to a client-side MVC framework. So perhaps there was another way to go about this while still avoiding the cross-browser headaches.
Deleting jQuery
But the jQuery pattern is compelling and familiar. And once you’re comfortable with something, it’s a bit of an effort to force yourself out of that comfort zone and learn. But looking back at my whole career, I realised that I’ve relearned pretty much everything I do, probably several times, since I started out. So it’s worth keeping in mind that learning and trying new things is how development has advanced to where it is now, and how it will keep advancing in the future.
In the end this lead me to Ender, which is billed as an NPM-style package manager for the browser, letting you search for and manage small, loosely coupled modules and their dependencies, and compile them to one file with a common API.
For What the Framework I ended up with a set of DOM tools, Underscore and Knockout, all minified into 25kb of JavaScript. This compares really well with 32kb minified for jQuery on its own, and Ender’s use of the dollar variable and the jQuery-like syntax in many modules makes switching over a low-friction experience.
On more complex projects, where you’re really going to use all the features of something like jQuery, but want to minimise the loading of other dependencies when you don’t need them, I’ve recently started looking at Jam. This uses the RequireJS pattern to compile commonly used code into a library file and then manage dependencies and bring in others on a per-page basis depending on how you need it. Again, it all comes down to thinking about what you need and using it only when you need it. And the configurability of tools like Ender or Jam allow you to be responsive to changing requirements as your project grows.
There is no right answer
That’s not to say this way of working automatically makes things easier. It doesn’t. On a large, long-running project or one where future functionality is unknown, it’s still hard to predict and plan for everything – at least until crystal balls as a service come about. But by including strong engineering practices in your front-end, and trying to minimise technical debt, you’re at least giving yourself a decent safety net to guard against the “can we just…?” tendencies that are a fact of life.
So, really, this is not an advocation of using a particular technology or framework, because I can’t tell you what works for you or your team. But what I can tell you is that working this way round has done wonders for my productivity and enthusiasm, both for code quality and for trying out new libraries. Give it a go, you might like it!
About the author
Stephen Fulljames is a freelance interface developer working mostly (these days) in Javascript and Node. Having started out writing about video games in the late 1990s, he was nominated to build the magazine’s first website because “you know a bit about HTML” and it’s kind of grown from there. Stephen lives near Brighton with his wife, Nat, and two kids and fills his spare time making atoms for railway modellers at Narrow Planet. You can find him on Twitter @fulljames.