Codecamp 2019: Question everything: The art of reducing bugs by challenging assumptions – Part 1

Problem solving

We talk and hear constantly about problem solving skills, all the companies are looking for developers that are good problem solvers, but what is this skill, more specifically? Aren’t all of us doing this on our day-to-day job?

Problem solving is the process of finding solutions to problems.

When i talk about problem solving, i’m thinking at the following analogy:

Optimist people have a solution for every problem, whereas the pessimists have a problem for every solution. This is also an analogy between the development team and the system test team. 🙂

For me, a good problem solver is the person that finds at least a solution for a problem, find multiple problems that may occur from that solution, and comes with solutions for those problems.

This actually means that the thought process does not stop once a solution has been found, and about what is the impact of such solution, how it affects it, with what dependencies it comes, and so on.

Let’s talk about how to write code, how to read code, and what questions we can ask ourselves when we receive a task. Thus, we can truly understand the limits of our task and anticipate future bugs or problems that may appear in our code.

Transforming assumptions into assertions

Before we write code, we need to eliminate all the assumptions we have about the task.

When we read code, we need to understand what the developer wanted to write there. Here, we can make assumptions about the code, so we can read it faster. And we actually do that, because we assume that the function works, that it returns correct data, basically that it is implemented correctly.

The more code we read within the project, the more we understand more about the vision of our development team.

Let’s start with some code snippets, the idea is about how the keywords are used or not and what impact it has for the functionality and for the people that read the code.

In-class keywords

What can we say about the following code?

We have a class called IntHolder that can store a number, increment it, can retrieve it from the getNumber function, and can take it as a string through the toString method.

For now, we may or may not have any opinions about this class.

Let’s look further into other classes within the project.

What conclusions we may take from these 2 classes?

We see that the constructor is marked as explicit, so it means we cannot create an object of this type through implicit cast. In the first class, we were able to.

Then, we can see that all functions are marked as const.

In the first class, the toString function was not marked const – we can assume that it modifies a class member, or we can think that it was added later on and the developer that wrote it forgot or didn’t knew to mark it const. Other than that, new functions could also be added without const, because we now have a precedent.

Not having a language keyword could mean that we either thought about it and decided not to add it, or that we haven’t heard about it or didn’t take it into account.

That’s why, the intent of developers should be explicit, and not presumed by the code readers.

Having functions declared const, we can take a conclusion: in those functions the members will not be modified.

But what happens if the variable is marked as mutable?

Well, in this case, since it can be modified in const functions, we can safely say that there’s at least one const function that will modify the variable.

Ranged-based for

Let’s iterate through a container now. The way we do it it’s important.

What conclusions we can take from this line ?

The values from the container are taken by value, therefore objects are temporary and copied, thus we will not modify our original container, and it’s probably a container of primitives or small objects.

When we see this line, we can say that we iterate efficiently through the container, the objects are not copied but taken by reference, thus we can directly modify the data from the container.

We can also think that the objects are non-copyable and therefore we have to take them as reference.

If we see both functions in the project, we can draw some conclusions regarding what we do with those objects.

This is the real reason why people within a project have to follow the same coding style, so they can draw these conclusions.

If someone is using both lines in their code, although they want to do the same thing in both cases, then we can no longer assume anything, and we need to read in detail what’s written to understand what is the intention.

A much better example – we take thee objects by reference, but we declare them as const, so they are read-only.

If we see the last 2 lines in the code, we can safely assume (almost guarantee) that in the non-const iteration we will modify the data within the container.

When we have a pointer in the class, if it’s initialized (memory allocated) on the constructor, it means we have ownership on it.

If itt’s initialized as nullptr, it means that it’s either created by us at a later time (lazy-loading?) – thus we still have ownership on it, or received by someone else – and we don’t have ownership.

What i want for you to understand is that the code speaks for you, so the intentions you have need to appear in the code. If the language allows you to explicitly write something, then use it.

What we write and also what we don’t write has an impact on our readers.

You may also like...