Concurrency in Clean Code

Victor Elizalde
5 min readOct 30, 2020

In this article I will be talking about Chapters 13 from Robert Martin’s Clean Code book. This practices of clean code will help us developers to polish our programming skills and code quality.

Clean Concurrency

This is a very difficult topic, it is really hard to write clean concurrent code and also testing it is difficult. Writing single threaded code is much easier. Also you can write easy multithreaded code that works fine in the beginning but when the system is put under a lot of stress, things start to break.

Why should we use Concurrency?

When you maintain a single threaded code, it is very easy to set breakpoints, find bugs, and know why and when they are happening, when you use concurrency this is not that simple. As we said before, concurrency is really hard and you can make a mess if you are not careful. But, concurrency is needed for many problems and it solves them quite good.

Concurrency not always improves performance

It can improve performance it many cases but not only because you are using concurrency it means that performance will improve, only when there is a lot of wait time that can be shared between multiple threads or multiple processors, only then, concurrency can help.

Concurrency changes the design of a single threaded program

When you implement concurrency, sometimes the design will change a lot and this is not a bad thing. Correct concurrency is complex, even for simple problems.

Defending our systems form concurrent code issues

Concurrent code can make mess, so we will go through some techniques and principles to help mitigate this risks and make concurrent code something that helps instead of giving us more problems.

Single Responsibility Principle

The SRP states that a given method/class/component should have a single reason to change. Concurrent code should be separated from the rest of the code, sometimes this is hard but it is required. Here are some things to consider:

  • Concurrency-related code has its own life cycle of development, change, and tuning.
  • Concurrency-related code has its own challenges, which are different from and often more difficult than non-concurrency-related code.
  • The number of ways in which miswritten concurrency-based code can fail makes it challenging enough without the added burden of surrounding application code.

Corollary: Limit the Scope of Data

Two threads should not modify the same field of a shared object. You should protect this critical sections in code that uses the shared object.

Corollary: Use Copies of Data

In order to avoid shared data is to avoid sharing it. Use copies of the original object and use them for read only, this way you will keep the data secured from changing if something goes wrong.

Corollary: Threads Should Be as Independent as Possible

Each thread should exist in its own world, it should not share any data with other threads. A thread just processes one client request and that’s it.

Know what you are working with

Every language has their libraries to manage all of this. You should consider this points when using them:

  • Use the provided thread-safe collections.
  • Use the executor framework for executing unrelated tasks.
  • Use nonblocking solutions when possible.
  • Several library classes are not thread safe.

Head’s Up Between Synchronized Methods

Some bugs can occur when you use more than one method on the same shared class. Sometimes you need to use more than one method on a shared object, if this is the case then consider these 3 practices: Client-Bases Locking, Server-Based locking, and Adapted Server.

Client-Bases Locking

The client will lock the server before calling the first method and make sure the lock’s extent includes code calling the last method.

Server-Based Locking

In the server create a method that locks the server, calls all the methods, and then unlocks. Then the client call the new method.

Adapted Server

Create an intermediary that performs the locking. This is an example of server-based locking, where the original server cannot be changed.

Synchronized Sections Should be Small

These sections are critical and hard to maintain if something goes wrong, you should keep them small and simple. It’s better to have many of them but small instead of a very big one.

Testing Threaded Code

When we implement tests it does not guarantee that the code will be bug free, but it will minimize risk tremendously. When testing multithreaded code things become more difficult in the testing side also. There are a few tips we should do when testing this:

  • Treat spurious failures as candidate threading issues. — Don’t treat errors as one-offs, you should consider every single one of them, always.
  • Get your nonthreaded code working first. — This pretty much speaks for itself, multithreaded is pretty tough by itself and adding more bugs from a single thread code doesn’t help at all.
  • Make your threaded code pluggable. — This helps you run your code in several different configurations that help you cover more ground.
  • Make your threaded code tunable. — Allow the number of threads to be easily tuned. Consider allowing it to change while the system is running.
  • Run with more threads than processors. — With this practice you will more likely encounter code that is missing a critical section or causes deadlock
  • Run on different platforms. — Each OS has a different thread policy so it may happen that in some OS’s the code works just fine but in others it doesn’t.
  • Instrument your code to try and force failures. — You can instrument your code and force it to run in different orderings by adding calls to methods. Affect the order of execution and you will increase the odds of detecting a flaw.

Code Instrumentation

There are two options to instrument your code: Hand-Coded and Automated.

Hand-Coded

You insert calls by hand on your code like yield, sleep, etc, when testing a particular part of code that you know has an issue or an issue can happen. If something fails it doesn’t mean that the reason was the call you added, the flaw was already there but you didn’t knew. This approach has many problems:

  • You have to manually find appropriate places to do this.
  • How do you know where to put the call and what kind of call to use?
  • Leaving such code in a production environment unnecessarily slows the code down.
  • It’s a shotgun approach. You may or may not find flaws. Indeed, the odds aren’t with you.

Automated

You can create methods that add randomness to the code in a certain method or received parameters. Also another method randomly add methods in the code like sleep, yield, etc. The solution is simple but it can cover a lot of ground when put to test and it helps a lot. All of this is called jiggling and it is a pretty good testing technique.

The end

Concurrency is pretty hard but it can help the system a whole lot if we use it correctly, it’s a high risk high reward programming technique but we need to apply all this principles in order to always get the high reward. As always if you have any doubt feel free to contact me!

--

--

Victor Elizalde

Software Engineer with a passion for sharing knowledge. Also, sports lover, musician, gamer, and a tremendous food fanatic.