Kacper Does Software - October Edition!

Clean Functions, Eating a Frog and More

October 2024 Edition

Welcome to the first post of Kacper Does Software Newsletter!

I hope you had an excellent October. I wanted to prepare a wide range of topics so that you can find something for yourself.

What you can find in this post 👇

Clean Functions

Have you ever seen a method that looked like this:

public void a() {
  // 100 lines of code
                           // such large indentations
  // returned null
}

Yeah, me too 😉

Functions / Methods are often the first line of organization for our code. If you wrote some code you probably wrote some functions or methods.

That’s why it is so important to write them correctly.

Method vs Function

I want to clear the difference between these two.

Methods and Functions are very similar.

The common part of their definition would be:

Method/Function is a piece of code called by name. It can be passed data to operate on (by the parameters) and can optionally return data (the return value)

What is the difference then?

A method is associated with an object. It can use its fields in the code.

I won’t go into details here. There are static methods in Java, etc., but I just wanted to give you an overall definition.

From now on I will use the word ‘Function’, because the fact that a method is associated with the object won’t really matter and the same rules apply to methods.

Length of the function

I would say:

1. Function should be small

2. Functions should be smaller than 1.

That’s the simplest rule and the most visible one.

There isn’t a hard proof that it is better, but let me ask you a question:

Which functions are more readable in your opinion— 10 lines long or 50 lines long?

There is a reason why we use the divide-and-conquer approach. We understand the problem better if we divide it into small pieces. Also, it is easier to understand a small portion of code than large blocks.

Let me quote a guy that you probably know:

Over almost 40 years I have written functions of very different sizes. I have written some nasty 3000-line monsters. I have also written many functions that have 100 to 300 lines. I have also written functions having 20 to 30 lines. After much trial and error, I can say that functions should be very small

Robert C. Martin “Clean Code”

But what exactly does it mean that a function should be very small?

The next chapter will help you with that.

One level of abstraction

If I had to choose the most important of the rules I describe in this article I would choose this one.

Following this rule makes our function’s code extremely well-organized and readable.

But what does it mean?

I will show you the example:

public static double calculateTotalRevenue(double[] prices, int[] quantities) {
    if (prices.length != quantities.length) {
        throw new IllegalArgumentException("Prices and   quantities arrays must have the same length");
    }

    double totalRevenue = 0.0;

    for (int i = 0; i < prices.length; i++) {
        double price = prices[i];
        int quantity = quantities[i];
        double productRevenue = price * quantity;

        if (quantity >= 100) {
            productRevenue *= 0.85;
        } else if (quantity >= 50) {
            productRevenue *= 0.90;
        } else if (quantity >= 10) {
            productRevenue *= 0.95;
        }

        totalRevenue += productRevenue;
    }

    return totalRevenue;
}

We calculate total revenue based on prices and quantities. We do that for all the prices and quantities.

Counting revenue for a single price and quantity pair would be a level below that. We can go further.

Calculating revenue includes calculating a discount. That’s a part of the revenue calculation, which is a level below that.

A function should only have a code that is a level below its name.

Let’s enhance our code in a way that every method will have only one abstraction level:

    public static double calculateTotalRevenue(double[] prices, int[] quantities) {
        if (prices.length != quantities.length) {
            throw new IllegalArgumentException("Prices and quantities arrays must have the same length");
        }

        double totalRevenue = 0.0;

        for (int i = 0; i < prices.length; i++) {
            double productRevenue = calculateProductRevenue(prices[i], quantities[i]);
            totalRevenue += productRevenue;
        }

        return totalRevenue;
    }

    private static double calculateProductRevenue(double price, int quantity) {
        double productRevenue = price * quantity;
        return applyQuantityDiscount(productRevenue, quantity);
    }

    private static double applyQuantityDiscount(double revenue, int quantity) {
        if (quantity >= 100) {
            return revenue * 0.85;
        } else if (quantity >= 50) {
            return revenue * 0.90;
        } else if (quantity >= 10) {
            return revenue * 0.95;
        }
        return revenue;
    }

Isn’t it way more readable and open to extensions?

Exception handling

If the function does something more than exception handling it probably has more than one level of abstraction and it does not do just one operation (more about it below)

It is a good practice to handle the exception in a separate method. It might depend on the programming language of your choice and exception handling mechanism, but in most cases, it will apply.

Let me show you an example with Java:

    public static List<Person> parsePersonsFromFile(String fileName) {
        List<Person> people = new ArrayList<>();

        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] parts = line.split(",");
                if (parts.length == 4) {
                    int id = Integer.parseInt(parts[0].trim());
                    String name = parts[1].trim();
                    int age = Integer.parseInt(parts[2].trim());
                    String hobby = parts[3].trim();
                    people.add(new Person(id, name, age, hobby));
                }
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Error reading file: " + fileName, e);
            return new ArrayList<>();
        }

        return people;
    }

It violates many rules, but they all complete each other. Let’s enhance it and extract other functions from this one.

    public static List<Person> parsePersonsFromFile(String fileName) {
        try {
            List<String> lines = readLinesFromFile(fileName);
            return parsePersonsFromLines(lines);
        } catch (IOException e) {
            logError(fileName, e);
            return new ArrayList<>();
        }
    }

Done!

It is much more readable and now it has only one level of abstraction and it does only one operation.

The function should do one operation

And it should do it well.

And it should do only that.

It is connected to rules about abstraction levels. If a function does only things that are one abstraction level below its name, it usually performs one operation.

Another way to check if our function performs one operation is to try to create another function from it.

If the change is not just code reorganization/extraction and does something different, then our previous function did more than one operation.

Functions naming

It is a common practice to use verbs to name functions.

For example, we do not name our function:

public String dateFormatting(Date date) {...}

But we would name it:

public String formatDate(Date date) {...}

Another important aspect is to name functions so that they tell exactly what they do. The name must be descriptive

Here the approach can differ depending on if it is a function or method.

The function named load() would not give you any information about what it loads, but if it was a method then the invocation could be PersonMetadataLoder.load() (static method) — class gives us context here.

I know naming things is called the hardest thing in programming for a reason, but try to come up with a name that tells a reader what it does without looking at the code inside the function.

Arguments of the function

Let me quote Robert C. Martin and his ‘Clean Code’ book again:

The ideal number of arguments for a function is zero. Then we have one and two argument functions. The construction of functions with three arguments should be avoided. More than three arguments require special justification — and even then such functions should not be used

Nobody likes the long, snake-like function’s declarations. It is not readable and not usable when we want to invoke those functions.

Another problem is the test of that method. More arguments mean that we need to write exponentially more tests to cover all the possible argument combinations.

If you have more than 3 arguments, grouping them into an object might be a solution.

Another thing that every programmer has done at least once in their life is to pass a boolean as an argument.

Why is that wrong? You may ask.

It usually means that inside of the function, we have two branches — often an “if” statement that checks the value of that boolean.

It means that the function does more than one operation.

Usually, it is better to split that function into two separate ones and avoid passing a boolean argument.

Returning values

There is one anti-pattern here, that’s also called a million-dollar mistake. That is returning null.

I described this problem in detail in one of my articles:

Summary

Functions are often the smallest code organization structure. They are so important that we even have a paradigm based on functions — functional programming.

That’s why it is important to write them clean and I hope that after reading this chapter it is clearer to you how to do it

Eat That Frog!

If you tend to postpone things this chapter is for you.

"If the first thing you do each morning is eat a live frog, you can go through the day knowing the worst is behind you."

Brian Tracy

In productivity terms, your "frog" is your biggest, most important, and often most challenging task of the day. If you do it right away other tasks become easier and your mind is peaceful.

I read the Book "Eat that Frog!" by Brian Tracy and I want to show you the main ideas and how to implement them.

So, how do you eat that frog?

1. Evening Preparation

  • Identify your "frog" for the next day before you end work

  • Choose the task that's most important for your goals or the one you're most likely to procrastinate on

  • Write down other tasks you have to do

  • Have everything ready so you can start immediately in the morning

Grade every task with the following scale:

  A Tasks - "Must Do"

  • Highest priority, significant consequences if not completed

  • Directly impact your key goals and results

  • Cannot be delegated, require your personal attention

B Tasks - "Should Do"

  • Important but less urgent than A tasks

  • Supports your long-term goals

  • Can sometimes be delegated

  • Moderate consequences if not completed

C Tasks - "Nice to Do"

  • Contribute to progress but aren't critical

  • Minor consequences if not completed

  • Can often be delegated or batch-processed

D Tasks - "Delegate"

  • Tasks that others can do

  • Important for the organization but not for your specific role (if task is work-related)

  • Should be delegated whenever possible to focus on A and B tasks

E Tasks - "Eliminate"

  • No real value or consequences

  • Time-wasters and unnecessary activities

  • Should be eliminated from your task list entirely

2. Morning Execution

  • Start work on your frog immediately, before checking email or messages

  • Block out 60-90 minutes of uninterrupted time

  • Turn off notifications and minimize distractions

  • Don't take long breaks until the task is complete

Implementation tips

Use the 2-minute rule: If unsure where to start, commit to working on it for just 2 minutes

- Break down large "frogs" into smaller, manageable chunks

- Time-block your calendar specifically for frog-eating

- Don't schedule meetings or check communications during your frog time

Build the Habit

Start small - maybe your first frog isn't your biggest task

- Track your completion rate to build momentum

- Celebrate small wins when you complete your frog

- Reflect on how much better your day feels when you tackle it early

Common Mistakes

- Don't try to eat multiple frogs at once

- Avoid the temptation to do small tasks first

- Don't wait for the "perfect time" to start

- Don't check emails or messages "just for a minute" before starting

What tools do you use for eating frogs?

You have the theory now, but what tools should you use to implement what I just told you? I have a few.

1. First and the simplest is to have a dedicated notebook for daily tasks. These can be bought in almost every store. Some people prefer to write some things on paper and if you are one of them then go with this one. The only problem is that you have your tasks in one place and you have to take your notebook everywhere.

2. If you like to write things down by your hand, but you want to have it on multiple devices, you can write the tasks down on your iPad (or other tablet). I do this myself using the Good Notes app. I still get the hand-written notes experience and I have all my tasks on every device

3. Another way is to use any note-taking application to put your tasks there. If you prefer to type things on a keyboard this is your choice. You can use applications like Notion, Obsidian or Apple Notes.

4. I found a free, dedicated Notion template for "Eating a Frog". You can use it especially if you keep your things on Notion. You can get it here https://systemeverything.com/eat-the-frog/

Now, let's eat that frogs 😉

If you have any questions regarding this topic feel free to respond to this email and I will be happy to answer you.

Latest News & Interesting Stuff

Chat GPT o1 model

Here you can read more about it 👇

Which programming language uses the least electricity?

Let’s be honest - no programmer thinks about it, but it is funny. I found a full article with this table showing usage of energy, time and memory for programming languages.

No surprise - low-level programming languages like C and Rust use the least energy.

The full article:

RSA defeated by quantum computer (22-bit RSA)

Don’t worry. It is just 22-bit and the industry standard now is 2048 bits, but it is good to be aware of this.

The source:

Thanks for reading!

I hope you enjoyed this month’s Kacper Does Software newsletter edition. I would love to hear back from You. Feel free to respond to this email, share your thoughts, and suggest what you want me to cover in the next edition.

Connect with me!

See you on other platforms 😉

x / Twitter

LinkedIn

Medium

Instagram

Have a wonderful day ☀️