All posts | Code

Code Complete: Module Interfaces

Code Complete: Module Interfaces main picture

In some languages, such as Python and JavaScript, variables and constants declared at module level can be imported to other parts of the program. On the one hand, this opens up a world of possibilities when compared to languages that require all data to be contained within functions, classes, etc. On the other hand, it makes it much easier to write code with issues.

Assume we are creating a web service that converts currencies. This converter specifies which currencies can be converted into which and at what rate:

// currency.js
export const rates = {
  eur: {
    usd: 1.3
    rub: 80.99
  },
  usd: {
    eur: 1.4
  },
  rub: {
    usd: 72.10
  },
};

Other parts of the program use this information for their own calculations and checks:

import rates from './currency.js';

// A handler that returns a page showing the exchange rate
// for a specific currency pair
const getRate = (req, res) => {
  // Parameters from the requested page address
  const { from, to } = req.query;
  const rate = rates[from][to];

  // Here the HTML is generated and sent to the user browser
  res.render('rate.pug', { rate });
}

The code proved to be simple but unreliable. If the user requests an exchange rate for a currency that does not exist in the system, the following access error will occur:

// Boom! rates['gdr'] === undefined
const rate = rates['gbr']['usd'];

Even if the first currency exists, then when the second one is absent, the constant rate will contain undefined, which can lead to an error.

Let's fix our code:

import rates from './currency.js';

const getRate = (req, res) => {
  const { from, to } = req.query;
  const rate = rates[from] ? rates[from][to] : null;
  // another option is to use lodash
  // const rate = _.get(rates, [from, to], null);

  res.render('rate.pug', { rate });
}

The error won't occur again, but the solution remains flawed. Working with rates will almost certainly not be limited to a single handler. As the program grows, currency rates will be used all over, which means that error handling will be required everywhere. Furthermore, any changes to the data structure will lead to rewriting all the pieces where it was used.

These issues typically arise when working with data directly without creating at least a minimal abstraction (functions) for data access. It’s easily solved, you just need to add a function to the module currency.js.

// currency.js
// The data is not exported outside
// and cannot be accessed directly
const rates = {
  eur: {
    usd: 1.3
    rub: 80.99
  },
  usd: {
    eur: 1.4
  },
  rub: {
    usd: 72.10
  },
};

export const getRate = (from, to) => {
  if (!_.has(rates, [from, to])) {
    throw new Error(`Unknown rate: from '${from}', to '${to}'.`);
  }
  return rates[from][to];
}
User avatar Kirill Mokevnin
Kirill Mokevnin 01 June 2022
Suggested learning programs

From a novice to a developer. Get a job or your money back!

Frontend Developer icon
Profession
beginner
Development of front-end components for web applications
start anytime 10 months
Layout Designer icon
Profession
New Under development beginner
Layout with the latest CSS standards
start anytime 5 months