Reducing Temporal Coupling in Our Code
By: John Au-Yeung
Tags: Technology, Software Development, Software Engineering, Programming, Web Development

Photo by Alex Iby on Unsplash

Time is often something that’s ignore in software architecture. The only thing that we’re preoccupied with is our schedule.

However, we got to concern ourselves with time in our own code as well. We’ve to concern ourselves with concurrency and ordering.

In this article, we’ll look at the ways we reduce temporal coupling in our code.

Allowing for Concurrency

We’ve to allow for concurrency in our code. This way, we don’t have to force one procedure to be run before another if they don’t have to.

This increases performance by allowing things that don’t depend on each other to run concurrently, saving everyone’s time.

Workflow

We have to analyze our program’s workflow to see which parts can be decoupled from each other in terms of ordering.

For instance, if we’re making HTTP requests to get unrelated data, then we can decouple those workflows by letting them run in parallel.

There’s no point for them to run serially and waste time if they don’t have to run in a serial fashion.

For instance, instead of writing the following to get 2 pieces if unrelated data from different sources:

async () => {

const michael = await fetch('https://api.agify.io?name=michael')

const joe = await fetch('https://api.agify.io?name=joe')

}

We can instead write the following to get them in parallel:

async () => {

const responses = await Promise.all([

fetch('https://api.agify.io?name=michael'),

fetch('https://api.agify.io?name=joe')

])

}

We can see the run time of the first snippet is much longer than the second one.

Therefore, if we allow them to run concurrently, then we can make sure that that one request won’t be held up unnecessarily since they aren’t related, to begin with, so they can be run concurrently.

Design for Concurrency

Linear code leads to sloppy programming. With concurrency, we’ve to think about time-based dependencies.

We’ve to make sure that one resource is available before the other. To call an object, we must make sure that the state is what we want before we call the next procedure that depends on the value of the produced from the previous process.

Therefore, we’ve to make sure to check for valid values and wait for them if they aren’t there.

The way to get dependent data differs in different languages. For example, in JavaScript, we make sure that dependent data from asynchronous processes are obtained before calling the next asynchronous process by using promises.

With promises, we can chain them so that we make sure that the previous promise returns the value we want before running the next one.

For instance, if we have the following JavaScript code:

const foo = async () => {

const res = await fetch('https://yesno.wtf/api')

const {

answer

} = res.json();

if (answer === 'yes') {

const res = await fetch('https://api.agify.io?name=joe')

const result = res.json();

console.log(result)

}

}

Then we make sure that we have the answer property from the response body and that the value of it is 'yes' before we call the next API to get more data.

This is something we’ve too often in JavaScript so always have to check that promise code resolves to what we want before we do anything.

Photo by Carly Rae Hobbins on Unsplash

Deployment

We can also be flexible with how our app is deployed. We can deploy the whole system at once or just one part of it.

If we divide them up into smaller services, then we don’t have to deploy the whole system every time we make some small change to some part of the system.

Decoupling them let us deploy different parts concurrently rather than deploying the whole thing in one go.

Adding concurrency to a non-concurrent system is much harder, so we got to think about that when we’re designing our systems.

Conclusion

Allowing for concurrency is something that we have to think about when designing our systems.

We don’t want unrelated things to run in series so that they aren’t coupled together unnecessarily and also saves the user time by not having to wait for something that they don’t have to wait for.

Concurrency also applies to deployment. If we divided our system into smaller services, then we only have to deploy that part if we want to deploy.

It’s much better than deploying the whole system every time we have a small change.