Apprentiship blog

Creative and Defensive programming by example.

December 05, 2018

Continuing from the post yesterday when I briefly touched on the concept of creative and defensive programming i thought it would be best to show an example. This example is of a unit that can tell if two words are different by one letter.

The creative approach

In essence it’s a get an implementation that passes the tests to quickly as possible, The advantage of the “creative” mind set encourages experimentation and fast fail/pass cycles in order to reach working solutions.

describe('Validate that two words are different by one letter', () => {

  it('WHEN GIVEN two words that are different by one letter, return true', () => {
    assert.equal(diff('cat', 'cot'), true)
  });

  it('WHEN GIVEN two words that are different by one letter THEN return false', () => {
    assert.equal(diff('cat', 'dog'), false);
  });
  // fast "creative" solution
  function diff(a, b) {
    let count = 0;
    for(let i = 0; i < a.length; i+=1) {
      if(a[i] !== b[i]) { count += 1;}
    }
    return count === 1;
  }
})

Where it fails

  • obscure naming
  • words of different length, ie (cat and kata, would pass);

Refactoring for a defensive approach

Things that are required,

  • better naming
  • the function and variables
  • validate inputs

In the following example, there are some tests and a refactored version of our diff, which has been renamed to diffrenByOneLetter which handles potential use-cases more predictably.

describe('Validate that two words are different by one letter', () => {

  it('WHEN GIVEN two words that are different by one letter, return true', () => {
    assert.equal(diff('cat', 'cot'), true)
    assert.equal(diffrentByOneLetter('cat', 'cot'), true)
  });

  it('WHEN GIVEN two words that are different by one letter THEN return false', () => {
    assert.equal(diff('cat', 'dog'), false);
    assert.equal(diffentByOneLetter('cat', 'dog'), false);
  });

  it('WHEN GIVEN two words which are of unequal sizes of more than one, THEN return false', () => {
    assert.equal(diff('foo', 'foods'), false); // Fail
    assert.equal(diffrentByOneLetter('foo', 'foods'), false); // Pass
  })


  // fast "creative" solution
  function diff(a, b) {
    let count = 0;
    for(let i = 0; i < a.length; i+=1) {
      if(a[i] !== b[i]) { count += 1;}
    }
    return count === 1;
  }


  // defensive approach
  function diffrentByOneLetter(firstWord, secondWord) {

    // Type checking
    if(typeof(firstWord) !== 'string' || typeof(secondWord) !== 'string') {
      return false;
    }

    // exit early if one of the word is longer by more than two characters
    const diffrenceInLength = Math.abs(firstWord.length - secondWord.length);

    if(diffrenceInLength > 1) { return false; }

    // length of the longest word
    const maxLength = (
      firstWord.legnth < secondWord.length
    ) ? secondWord.length : firstWord.length;

    let numberOfUniqueLetters = 0;

    for(let i = 0; i < maxLength; i += 1) {
      if(firstWord[i] !== secondWord[i]) {
        numberOfUniqueLetters += 1;
      }
    }

    return numberOfUniqueLetters === 1;

  }
});

Changes made

  • The function has a more descriptive name.
  • More descriptive names for variables.
  • Checks the inputs are valid.
  • Checks that number of characters in the strings are the same or off by one at the most.
  • Uses the number of characters of the longest sting for the max number of iterations.
  • More test coverage.

Result of both mindsets

The results of using both minds sets is that when using the “creative mindset” more time can be put in to trying-out solutions with minium time and effort (like the struggle to name things), once a workable solution is found it can then be refined in to a more robust unit by refactoring with a “defensive mind set”.

What else did I do today?

Watched 7 minutes, 26 seconds, and the Fundamental Theorem of Agile Software Development . The main idea of the talk was to reduce the cost caused by accidental complicity, which in layman terms would making a job more difficult than it needs to be.

Today’s exercise from learn-c-the-hard-way, Intermediate Makefiles was focused building a skeleton project template (for later use) which automates the process and running tests.

Then I read into types of unit tests, specifically stubs, spies and mocks to find out what I didn’t know and when to use each of them. In short spies are callbacks, stubs are simple interfaces /objects for emulating a dependency or parameter so the unit to be tested with controlled values, and mocks duplicate the behaviour of a dependency such as the DOM.


Written by Marc McIntosh Find him on github