“*Utils” classes can be a code smell: an example

You might have heard that “*Utils” classes are a code smell.

Lots of people have written about that before, but I tend to find the reasoning a bit vague, and some of us work better with examples.

So here’s one I found recently while working on this bug: you can’t know what part of the Utils class is used when you require it, unless you do further investigation.

Case in point: if you place a method in VariousUtils.js and then import it later…

var { SomeFunction } = require('VariousUtils');

it’ll be very difficult to actually pinpoint when VariousUtils.SomeFunction was used in the code base. Because you could also do this:

var VariousUtils = require('VariousUtils');
var SomeFunction = VariousUtils.SomeFunction;

or this:

var SomeFunction = require('VariousUtils').SomeFunction;

or even something like…

var SomeFunction;
lazyRequire('VariousUtils').then((res) {
  SomeFunction = res.SomeFunction;
});

Good luck trying to write a regular expression to search for all possible variations of non-evident ways to include SomeFunction in your codebase.

You want to be able to search for things easily because you might want to refactor later. Obvious requires make this (and other code manipulation tasks) easier.

My suggestion is: if you are importing just that one function, place it on its own file.

It makes things very evident:

var SomeFunction = require('SomeFunction');

And searching in files becomes very easy as well:

grep -lr "require('SomeFunction');" *

But I have many functions and it doesn’t make sense to have one function per file! I don’t want to load all of them individually when I need them!!!!111

Then find a common pattern and create a module which doesn’t have Utils in its name. Put the individual functions on a directory, and make a module that imports and exposes them.

For example, with an `equations` module and this directory structure:

equations
  linear.js
  cubic.js
  bezier.js

You would still have to require('equations').linear or some other way of just requiring `linear` if that’s what you want (so the search is “complicated” again). But at least the module is cohesive, and it’s obvious what’s on it: equations. It would not be obvious if it had been called “MathUtils” — what kind of utilities is that? formulas? functions to normalise stuff? matrix kernels? constants? Who knows!

So: steer away from “assorted bag of tricks” modules because they’ll make you (or your colleagues) waste time (“what was in that module again?”), and you’ll eventually find yourself splitting them at some point, once they grow enough to not make any sense, with lots of mental context switching required to work on them: “ah, here’s this function for formatting text… now a function to generate UUIDs… and this one for making this low level system call… and… *brainsplosion*” 😬

An example that takes this decomposition in files to the “extreme” is lodash. Then it can generate a number of different builds thanks to its extreme modularity.

Update: Another take: write code that is easy to delete. I love it!

Organising files in Google drive

We use Google Drive at work and for a particular project we ended up in a situation where there were multiple documents related to it, but not an easy way to have “links” to all of them on the same place.

One solution would be to create yet another document and link to the documents. Another one is to bookmark documents in your own browser—but the issue is that if someone wants to collect all the links together in their computer, then they have to bookmark the stuff individually as well.

But there’s an easier way: you can make a folder in Google Drive and add any file to it (whether it’s owned by you or not). Then you can share the folder, and voilà! everyone has access to the collection of documents.

To make a folder in your drive, first go to your google drive. Click NEW… folder. Give it a name, for example: Magnificent folder.

Then go to the document you want to add to the folder, click on the File… menu, select Add to my drive. A little pop up will show up, click on the Organize link… Choose Move to folder, and choose the Magnificent folder you created before.

If you refresh the folder in your drive, the document should be present there.

To share the folder, click on the down-facing arrow on the right hand side of the name of the folder. This opens a drop down menu and you can select “Share…” to open the usual Google docs interface to share stuff with people.

Done!

I’d also suggest that changing the folder view from grid to list, showing the document titles, might be useful in many cases, as the document titles tend to get truncated way too easily.

Copy using the command line without overwriting existing files

I learnt about cp‘s -n (or “no clobber”) option that lets you copy, but not overwrite files that already exist:

cp -Rvn folderA folderB

The above would Recursively copy the contents of folderA into folderB, and print a message per file it copies (with the v option). Great to know when things get stuck or interrupted 🙂

It’s quite useful when trying to copy from something like a folder in a remote drive that keeps disconnecting. So if the network times out you can always run the command again after reconnecting, and it will skip files that are already there.

Note that if a file has been partially copied, then cp will skip it the next time and not complete the download. So don’t use this method for stuff that requires some degree of integrity–you’d better reach for tools such as scp.

Don’t force users to install node modules globally when you can avoid that

How often have you seen instructions for installing a project that are something like this?

git clone https://wherever/the/project/is
cd cloned_folder
npm install -g gulp # <-- 😱

(or, instead of gulp, any other utility that the project requires for development)

This is not a good idea. You’re installing the same version of a utility globally, and perhaps the project has not been tested with that version. Things might break or even worse, work not-so-well in a strange, undebuggable way!

Don’t do it.

A better way is to use npm scripts, which belong to the project, and specify development dependencies in package.json, so when you run npm install, it also installs devDependencies locally to the projects’ node_modules folder. This keeps everything under control and does not pollute other people’s computers global space.

So, for example, if a project was asking users to run npm install -g gulp, they should do this instead:

  1. Install gulp locally as a development dependency, saving it to package.json:
    npm install --save-dev gulp
  2. Add a new entry to the scripts array in package.json:
    "build": "gulp"
  3. Ask users to run
    npm install

    the first time. And

    npm run build

    each time they need to build the project

The reason this works is because when npm runs a script, it modifies the current PATH variable to add node_modules/.bin, which contains any binaries that you installed as part of your modules. So your computer will search for binaries starting in the .bin folder, before searching in any other default location for binaries in your computer (e.g. /usr/bin)

You can find more documentation about this behaviour in the run-script page of the npm manual. I really encourage you to read it as it has lots of good stuff and Things You Wished Had Known Before 🙂

And in case it makes things clearer, this is how a hypothetical package.json would look before:

{
  devDependencies: {}
}

And after:

{
  devDependencies: {
    "gulp": "12.34.56"
  },
  scripts: {
    "build": "gulp"
  }
}

With this, the devDependency is clearly stated on the package.json file, the project developer will be developing with that version, and will not get surprises! (and the users of the module won’t either).

Another advantage of this approach is that you are not tying the project to a specific utility.

If you decide to use grunt instead of gulp later on, you just need to do whatever changes you need (Gruntfile instead of Gulpfile, edit package.json etc), and yet users of the project will still start the build process with npm run build.

It’s invisible to them (except perhaps running npm install again)!

You’re hiding internals that they do not necessarily need to know about, so it’s a good abstraction. Seriously—you won’t even need to update your getting started guide! Good stuff.

Hope that helps!

If you want to learn even more cool npm scripts tricks, I recommend Kate’s talk and accompanying post. Actually, I just realised she explained the same thing I did, so here’s her take, haha!

Fixing a “git mess” with cherry pick (from the command line)

Yesterday we were remotely pair programming (by me sharing my screen and my colleague Alex looking at it), and at some point I had to send a PR with a test change to validate the process would trigger the automation he had set up… but turns out he had changed something on the repo after I had forked and cloned it, and so I had a conflict!

I somehow resolved it (note to self: talking while resolving conflicts is not a good idea) but when I then pushed my branch to GitHub and created the pull request, it would not run automation because of that conflict (even if I had ‘resolved’ it).

I did not want to clone again and apply the changes… and neither wanted Alex! He said that everything is fixable with Git, so he showed me how to get myself out of this situation.

First we add the upstream remote to our repository, so we can check it out:

git remote add upstream https://github.com/user/repo.git

We fetch the data from the upstream repo:

git fetch upstream

Run a git log to find the hash of the commit we do want to keep (the Good Commit):

git log

Say it produces something like this:

commit 0BAD0BAD0BAD0BAD0BAD0BAD0BAD0BAD0BAD0BAD
Author: sole
Date:   Thu Mar 2 16:02:03 2017 +0000

    AAAAARGHHHH

commit 1337133713371337133713371337133713371337
Author: sole
Date:   Thu Mar 2 15:55:26 2017 +0000

    testing at the request of @ochameau

commit 0b225a66cf3ad67b3c67360d0e7c1e329ca3ce34
Author: Alexandre Poirot
Date:   Tue Feb 28 11:57:23 2017 +0100

    Upload screenshot and status

We don’t want the last commit (0BAD0BAD), we want the previous one (13371337). So make a note of that hash somewhere.

Now we check out their master branch (which we want to use as the base for our modification):

git checkout upstream/master

And we tell git to apply our changes from The Good Commit, using the hash we found before:

git cherry-pick 1337133713371337133713371337133713371337

Since I didn’t even change the same file he changed in his other commit, this applied neatly. No conflicts!

The problem is that my local repository is now different from the GitHub copy, because I had pushed a version which had an additional commit to resolve ‘the conflict’ (I tell you, this was quite messy!)

The solution is to force push to my GitHub repository (gasp!):

git push origin HEAD:master -f

And you don’t need to update the PR that had “conflicts”–GitHub already picks that you updated the repository, and since there are no conflicts anymore, the integration stuff works 🙂