Main | All posts | Code

Code Complete: Module Interfaces

Time Reading ~2 minutes
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
0
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time
profession
Layout with the latest CSS standards
5 months
from scratch
under development
Start at any time
profession
new
Developing web applications with Django
10 months
from scratch
under development
Start at any time