A hermit's advice on Code Maintainability

Throughout the years, I've gained an understanding about code maintainability as I've made my own products from scratch alone and had to maintain them for clients. This is simply a personality flaw that I dislike working with others but consequently has promoted my efficiency. With no one else to blame but myself, I could not defer my shortcomings to someone else and had to self improve in order to keep my head above water or drown in my own bad decisions.

My brain also is not one that contains a huge amount of memory at one time, only in rare cases when I'm able to be alone in thought as I'm usually bombarded with noise working at home. I tend to forget the details most of the time and focus on the high level. Consequently, I break things up into small manageable chunks that are easy to reason about, quick to fix and are intended to be side effect free so I can unit test them and rely on them like lego blocks.

Here's my own personal mantra.

  1. At any point, if you cannot reason about your code within 10 seconds, you are doing it wrong. Your code should be easily readable or at least easily abstracted to the point where it is.
  2. Code lines per module should be within your screen height ideally. You might consider breaking up your code if it is well above scroll height. I find it easier to be able to reason about a page than several.
  3. Make the modules side effect free, meaning they can be unit tested.
  4. Organize recursively, with folders in folders as you are refactoring.
  5. If a thing can be used across your app or apps, place it in its own folder (utils or lib or its own submodule in git)

Here are some examples of things I do in JavaScript as I tend to do JavaScript most.

Big files into small files

before

codefolder  
   index.js (main)
   bigcode.js

after

codefolder  
   index.js (main)
   bigcode
      index.js (main)
      codeA.js
      codeB.js
      ...

This has the benefit of refactor as you go while keeping the code entry points the same.

Simplicity over complexity

before

saveSomething()  
.then(saved => {
  return anotherThing(true);
})
.then(doAnother)
.catch(err => { /*handle error*/ });

after

try {  
  await saveSoemthing();
  var resp = await anotherThing();
  await doAnother(resp);
} catch(err) {
  // handle error
}

This was a move requiring a framework update with async but well worth it as the thought process needing to understand the code went down dramatically.

readability enhancement

before

if (user.postCount === 1 && user.invitedFrom && post.author._id === user._id)  
    notifyUserFirstPost();

after

var firstPost = user.postCount === 1;  
var wasInvited = !!user.invitedFrom;  
var isAuthor = user._id === post.author._id;

if (firstPost && wasInvited && isAuthor)  
    notifyUserFirstPost();

This makes it easy to reason about the intent of the boolean statements.

easy remote debugging

#IFDEF DEBUG
console.error = _debounce(function() {  
  // email myself 1 every 30 seconds if an error occurs within that time.
}, 30000);

Some love log files only. I prefer getting an email but in a debounced way so I can look at the log files if I really need to. Also, this only is ran on if the debug flag is active and stays out of the code otherwise with no if-else blocks.

To conclude

The majority of bug fixes I do take around 5 seconds because I'm pretty adamant about following my own rules and I go for very simple code over clever tricks. Most of programming is just dumb, iterating over stuff or accessing stuff. Why cruft it with intellectual bravado? It just makes stuff hard to reason about.

This really breaks down in a team environment for me though as I find others do not do this and tend to create crufty code or "clever" code that seems to give them job security and slows down the entire project. I feel very claustrophobic in those environments.