That code jumps too much

“That code jumps too much” my colleague said to me once while reading my code. I just acknowledge his observation and didn’t say anything. I knew his style was more about putting logics closely together. The style tends to result in longer code in minimal number of methods.

I prefer breaking logic to small methods and hide some underlying details. Yes, the code will result in more small methods and I need to follow more method calls if I want to get all the detail (but we always need to know all details of a logic? ) . It is always debatable to say how much of breaking down the logic is too much.

Is it unnecessary abstraction

I read it somewhere (I could not find the original source ) that normal developers write program, Java developers write classes. It is a sarcasm to imply that Java developers tends to over complicate things and love to create unnecessary code rather than focusing on getting something done.

Another good one is the joke on how to spot a senior software engineer. They tends to break down classes and methods over and over just to show that they are thinking in a lot more advanced logic.

I don’t think it is something specific to Java developers at all. Abstraction is definitely a good tool to handle logic complexity. But too much of anything is always not a good thing. And that is the case for any programming languages.

But on the other hand, the sarcasm is also a good reminder for developer to think carefully and ask ourself if we are creating too much layer or abstraction.

My small jumping methods

Here is the code I wrote yesterday. It is an simple example code for my another blog.

 private fun generateUserMessages(userName: String, subPerUser: Int){
        val userId = UUID.randomUUID()

        producer.send(newCreateUserMessage(
                        userId = userId,
                        email = "{$userId}@gmail.com" ,
                        name = userName ))
        
        (1..subPerUser).forEach { subId ->
            producer.send( newSubscriptionMessage(userId, subId))
        }
    }

It looks concise and easy to read. You should be able to get some idea on what it is doing within 10 seconds of reading. What seems to be the issue then?

I have extract out the code in composing messages to 2 methods.

private fun newCreateUserMessage(
            userId: UUID, email:
            String, name: String

    ) : ProducerRecord<String, String>{

        return ProducerRecord(COMMAND_TOPIC,
                userId.toString(),
                Message(
                    userId = userId,
                    userDetail = UserDetail(
                            email = email,
                            name = name),
                    subscription = null
                ).serialize()
        )
    }

    private fun newSubscriptionMessage(
            userId: UUID,
            subscriptionId: Int
    ) : ProducerRecord<String, String>{

        return ProducerRecord(COMMAND_TOPIC,
                userId.toString(),
                Message(
                        userId = userId,
                        userDetail = null,
                        subscription = Subscription(
                                newsLetterId = subscriptionId
                        )
                ).serialize()
        )
    }

I use a model; the Message class to represent both create-user and add-subscription message. This is because I don’t want to deal with type hierarchy when doing deserialization.

The observation points

  • I have extracted out the method just to give it names to add more context in the logic; newCreateUserMessage(), newSubscriptionMessage().
  • The methods are not reused anywhere else at all.
  • The reader need to jump to 2 more methods to know the whole details
  • Is this unnecessary layer?

My intention

I have been using this style of code for long time. My intention is to make the method generateUserMessages() easy to read as much as possible. Developers tend to read code from higher level of logic far more often. Over the course of project maintenance, we tends to read and change business logic more often than code dealing with persistent or networking layer.

If the code is fresh in my mind then it may not be much to load code from all layers to my brain. But if it is 3 months later then I will definitely be struggling to read the code if all layers are wired closely together in large methods. That is why we want to create abstraction to hide the underlying detail.

The issue I can see is that I have been using this style of code for a long time and it become my habit to apply it even for a short code like example above. In the example, I still prefer to extract out the methods but I also understand if there is someone thinking it is too much for too little gain.

It is often not something you could tell exactly whether it is too much or too less. I am also considering myself not doing good enough in composing logic in more cohesive ways. I have been trying to be more direct and put more code inline to make it less jumping. It thinks it is a balancing act.