One Line of Code that Cost Me An HOUR to Fix
Tags: java, softwaredevelopment, logging
Published: 2024-07-10 07:03:00


![https://www.vecteezy.com/vector-art/7591399-inefficiency-time-management-overload-stress-or-burnout-at-work-concept-confused-businessman-and-large-amount-of-work-on-seesaw-comparing-on-seesaw-lying-on-timer-clock](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5lpsqoxp2d3vzfxlcsw.png)

Hello developers. In today’s article, I am going to share story of how one line of code cost me an hour to find and fix. Let’s get started.

The project was developed in _Spring Boot 2.6.4_ initially, right now it is operating on _Spring Boot 3.2.3_ version. I upgraded the version to _Spring Boot 3.2.3_ because I’ve experienced some issues which blocks and delays the upcoming feature. I will talk about the upgrade journey in another article. Make sure to follow to get notified about upcoming articles.

## The Problem

I started my SE journey in a new company and joined an interesting project. The project contains multiple schedulers and each of them runs in different times such as every 10 seconds, 30 seconds, 30 minutes and so on. If you have an experienced in such kind of the project, then you’ve already know how hard it is to detect problems.

Okay let’s go straight to the line that cost me hours of debugging.

```java
public void sendChatRequest(Long chatId) {
   chatService.findChatByName("demo-chat").orElse(save(new Chat("demo-chat")));
   // other codes ommited...
}
```

Can you spot the problem?

That `save()` method will be called every time regardless of existence of Chat. This is because `orElse()` method evaluated eagerly. On the other hand `orElseGet()` method is evaluated lazily and it will be executed only when `Optional` is empty.

In order to understand difference between them, execute following code in your IDE and look at logs.

First, we are going to return empty `Optional` to see what the results will be.

```java
public class OrElseAndOrElseGetMethods {

  record Chat(String name) {
  }

  class ChatService {
    public Optional<Chat> findChatByName(String name) {
      return Optional.empty();
    }
  }

  private final ChatService chatService = new ChatService();

  public static void main(String[] args) {
    var main = new OrElseAndOrElseGetMethods();
    main.testOrElse();
    System.out.println("*************************");
    main.testOrElseGet();
  }

  public void testOrElse() {
    System.out.println("Testing orElse() method");
    chatService.findChatByName("demo-chat")
        .orElse(save(new Chat("demo-chat")));
  }

  public void testOrElseGet() {
    System.out.println("Testing orElseGet() method");
    chatService.findChatByName("demo-chat")
        .orElseGet(() -> save(new Chat("demo-chat")));
  }

  public Chat save(Chat chat) {
    System.out.println("Saving " + chat);
    // saving Chat to database
    return chat;
  }
}
```

Output of above code snippet is represented below:

```txt
Testing orElse() method
Saving Chat[name=demo-chat]
**********
Testing orElseGet() method
Saving Chat[name=demo-chat]
```

As you can see on the above in both methods (`orElse()` and `orElseGet()`) `save()` method is executed.

Let’s see what will happen if `Optional` **is not empty**.

```java
public class OrElseAndOrElseGetMethods {

  record Chat(String name) {
  }

  class ChatService {
    public Optional<Chat> findChatByName(String name) {
      // searching inside db
      var foundedChat = new Chat(name);
      return Optional.of(foundedChat);
    }
  }

  private final ChatService chatService = new ChatService();

  public static void main(String[] args) {
    var main = new OrElseAndOrElseGetMethods();
    main.testOrElse();
    System.out.println("**********");
    main.testOrElseGet();
  }

  public void testOrElse() {
    System.out.println("Testing orElse() method");
    chatService.findChatByName("demo-chat")
        .orElse(save(new Chat("demo-chat")));
  }

  public void testOrElseGet() {
    System.out.println("Testing orElseGet() method");
    chatService.findChatByName("demo-chat")
        .orElseGet(() -> save(new Chat("demo-chat")));
  }

  public Chat save(Chat chat) {
    System.out.println("Saving " + chat);
    // saving Chat to database
    return chat;
  }
}
```

Whole code snippet on the above is same as before with only difference on line 11. Instead of returning `Optional.empty()`, `Optional.of(foundedChat)` is returned.

Let’s see the results

```txt
Testing orElse() method
Saving Chat[name=demo-chat]
**********
Testing orElseGet() method
```

As you can see on the output, `save()` method is called when executing `orElse()` method, however, this didn’t happen on `orElseGet()` method.

## Why It Took so Much Time to Detect and Fix the Issue?

I didn’t see some contingencies, but after 20 minutes later I realized, I have more chat request on the database than expected. It was the test environment and my teammates were working on the server at that time. But they tell me they didn’t touch anything on the server at that day.

Debugging session began and two things made me struggle.

There were too many schedulers.
Lack of logging especially on methods where changes happens.
In order to fix this issue, I changed method from `orElse()` to `orElseGet()`.

## Improvements on the Project after That Issue

The first improvement was reducing schedulers count from five to one. I observed and saw that those schedulers were pretty much served the same purpose, so it would be okay to combine them into one scheduler operation. In addition, one of them was outdated and removed immediately.

The second improvement was the addition of self-explanatory logging statements to methods to facilitate debugging process.

## Lessons Learned After that Issue

When source of the problem found, I realized I’d looked at this class twice but couldn’t find the first time. This is because I was in rush and tried to fix the problem as fast as possible. However, that cost me more time to figure out where the problem was.

> Next time when you encounter this kind of problem, read every line out loud or murmur to yourself, that way code will become much more clear for you.

When you start to apply above strategy into your working, you will find lots of problems or poorly written code. This will give you an advantage to find the places where refactor is needed.

Here’s the example for the above:

```txt
- You: Okay, in the first line of the method, it do null checks
- You: if it is null, then return immediately, otherwise
- You: send the method parameter to method findByName()
- You: and findByName() returns Optional. 
```

You understand the idea. When you do above, don’t be in rush, otherwise this won’t have so much impact. Be patient and slow.

This is the end of the article, if you like the article don’t forget to share and write comments.

If you have any questions, you can reach me via [LinkedIn](https://www.linkedin.com/in/mammadyahya/).

At last, code is available on [GitHub](https://github.com/mammadyahyayev/blog-posts/blob/master/java-codes/src/main/java/com.blogs/OrElseAndOrElseGetMethods.java).