Brain friendly programming in Java

We have many principles for writing better more maintainable software, but still end up with a tangled mess.

Most of us have heard about the Single Responsibility Principle (SRP), Robert C Martin expressed this principle as “A class should have only one reason to change”.

He later clarified this by stating “Gather together the things that change for the same reasons. Separate those things that change for different reasons”.

We also have the Single Layer of Abstraction Principle (SLAP) which is expressed as “Don’t mix levels of abstractions”.

Dan Terhorst-North presented his Unix Philosophy as “does one thing well”.

The goal is to create more maintainable software, not to fulfill the principle. It’s still possible to create a mess even though we follow all the SOLID principles. So what do these principles mean, and how can I as a developer use them to create better software?

The Science Behind Brain-Friendly Programming Methods

In “The Programmer’s Brain“, Dr. Felienne Hermans talks about how our brain works working with code.  It’s a highly recommended read.

One of the key takeaways for me was the fact that our working memory can only keep track of 3 – 5 chunks of information at the same time. How big a chunk is depends on how much experience we have with the current task.

It can be individual statements, lines, or entire design patterns.

We can make our code easier to understand, to lower the cognitive load of the reader, by limiting the number of chunks of information we have to keep track of.

Ok, so how do we do that then?

Cognitive Load: Minimizing Mental Strain for Optimal Programming Performance

This is when the presentation “Simple Made Easy” from Rich Hickey, comes in.

He talks about how we complect our code.

Complect is an old word that means to braid together, to weave together. By completing our code we make it harder to understand by requiring the reader to keep more chunks of information in memory. We are increasing the cognitive load for the reader.

He suggests that we should separate our code based on the following questions:

  • What needs to be done?
  • Who wants to do it?
  • How should it be done?
  • When should it be done?
  • Where should it be done?
  • Why should it be done?

By mixing these, complecting them, we create code that is harder to understand because it requires the reader to keep more chunks in memory, which creates a higher cognitive load.

By keeping these separate, we get code that is easier to change and reuse, for instance, we can then change how something is done without affecting who wants it done.

Another thing we can do to make our code easier to understand is to take advantage of new features in the language we are using.

We often hear that we should update to the latest version of our language for performance and security reasons. But there are also improvements in the language that can make our code easier to understand.

Let’s take Java as an example.

Leveraging New Java Language Features to Lower the Cognitive Load of the Programmer

Java, being one of the most popular programming languages in the world, is constantly evolving to meet the changing demands of software development. With each iteration, Java introduces new language features that not only enhance the performance and scalability of applications but also aim to make the lives of programmers easier.

I’ll list a few of the new Java language features that, when used, can lower the cognitive load of programmers, allowing us to write more efficient and maintainable code.

Concise Code with Lambda Expressions

Lambda expressions, introduced in Java 8, allow developers to write more concise code by expressing instances of anonymous functions. By using lambda expressions, programmers can reduce boilerplate code and focus on the core logic of their applications. This feature is a good match for handling collections, stream processing, and event handling. For example, instead of writing lengthy loops to iterate over a list, lambda expressions can be used to achieve the same result in a more compact and readable manner.

Improved Null Safety with Optional

Java 8 introduced the Optional class, which helps handle null values more effectively. By using Optional, programmers can avoid NullPointerExceptions and write safer code. Optional encourages developers to handle null values explicitly, leading to more robust and error-free applications. This feature reduces the cognitive load of programmers by providing a clear and standardized way to deal with null values, making the code more predictable and easier to reason about.

Enhanced Pattern Matching

Java 14 introduced pattern matching for instanceof and this has been extended and improved on in newer versions since then. This feature simplifies the code by combining type checking and type casting into a single operation. Programmers can write cleaner and more concise code when working with complex data structures by using pattern matching. Pattern matching reduces the cognitive load of programmers by eliminating the need for if-else blocks and switch statements, leading to more readable and maintainable code.

Simplified Asynchronous Programming with CompletableFuture

Asynchronous programming is essential for building responsive and scalable applications. Java 8 introduced the CompletableFuture class, which makes asynchronous programming simpler by providing a convenient way to work with future values. Programmers can chain asynchronous operations and handle their results more intuitively using CompletableFuture. This feature reduces the cognitive load of developers by abstracting the complexity of asynchronous programming and allowing them to focus on the business logic of their applications.

The evolution of Java language features has brought significant improvements to the way programmers write and maintain code. By leveraging features such as lambda expressions, Optional, pattern matching, and CompletableFuture, developers can lower the cognitive load associated with programming tasks. These features enable programmers to write more concise, robust, and maintainable code, ultimately leading to increased productivity and efficiency in software development. 

As Java continues to evolve, developers can expect more innovations to enhance their programming experience further and make Java an even more powerful and developer-friendly language.

Conclusion

By understanding how the brain processes information, developers can create programming languages and environments that are more intuitive, efficient, and less cognitively demanding for users. This approach can improve the overall user experience, increase productivity, and promote greater accessibility for individuals of all cognitive abilities.

Embracing brain-friendly programming will not only benefit individual programmers and users but has the potential to revolutionize the way we interact with technology and shape the future of computing.

Continuous Feedback

Fixing performance problems can be tricky. I joined a new team last spring, and my first assignment was to investigate and fix some performance problems they were having.

Most of the reported performance problems were related to a complicated function with many moving parts.

I started with creating some test data that I could use to measure the current performance to have something as a baseline to compare my changes to.

I then used a combination of manually inspecting the code and running a profiler to try and pinpoint the performance bottlenecks in the code.

After a few days of work, we managed to locate and fix several issues that negatively affected the performance, most related to database access through Hibernate. We found that the same data was read multiple times from the database and had to be cached. It turned out that Hibernate produced N+1 queries due to misconfiguration of the entities. We solved one issue by adding a missing table index.

Manually finding these issues is doable but very work-intensive. There must be a better way. Our applications can give us lots of information we can utilize by enabling OpenTelementry, something I wrote about in my previous article. Observability for Agile Development: Why It’s a Game Changer

Digma is a tool that analyzes OpenTelemetry data to give us continuous feedback from our applications. I got involved as a beta-tester around the same time as I was working with the performance issues I described above, so I could directly see the benefit of having a tool to help me identify problem areas.

With Digma, we get continuous feedback on the performance of the application. Digma will instantly show us where the bottlenecks are, so we don’t have to search for them ourselves. We can now get direct feedback on how our latest fix performs compared with the previous version. It also found bottlenecks that we did not even know about. It takes the guesswork out of the picture. We save lots of time finding and fixing performance issues.

The tool is free to use locally for developers. All they ask is that we register so that they can get information about how many users they have. It consists of a plugin for your IDE and a backend running in containers. No data is exposed externally, so there should be no issue with security or privacy. The best thing is that the installation is super easy. The plugin will enable OpenTelemetry automatically for many common frameworks and application servers.

There is also an option to do a central installation of Digma to get even more benefits, but there is a licensing fee. In this article, I will share my experience working with a local installation and my thoughts on why you should consider doing a central installation. Let’s get into it!

Local environment

Digma comes bundled with Jaeger, so we can get a visualization of collected tracing data directly without leaving the IDE. We can navigate between code and traces. We can follow the flow of a request and see things like parameters and queries.

Tracing data is automatically collected when you run or debug your code in the IDE or running tests. It gives us continuous feedback on how our code is performing and if the latest change has introduced any new issues.

A new feature shows you which tests have touched a particular part of your code. This feature makes it possible to run the tests affected by the changes we are making, saving us time and providing faster feedback on the issue we are working on.

It will automatically detect many of the issues I mentioned in my example above. This feature alone saves lots of time when we no longer have to locate where the problem lies. But can focus on fixing it instead. I estimate that we could have solved the performance problems in the example above in 1/10 of the time by using this tool. It is a huge deal and the main reason I’m writing this article. We should work smarter, not harder.

Make sure you try with your application. Digma is free, easy to set up, and will boost our productivity.

Doing a central installation will allow us to take full advantage of Digma.

CI/Testing environment

It will be possible to monitor our releases over time by connecting our CI environment to Digma. OpenTelemetry has to be enabled on our product to make this possible. We will then have the option to detect changes over time. It can tell us if the performance has increased or decreased over time. We can run performance suites to detect scaling issues.

We now have the option to compare the results from the code running on the CI environment with the one we currently are working on. We get feedback on whether our latest change fixes a performance problem even before submitting the code to the repository. There is no need to wait for the build and deploy process to complete.

It can do a usage analysis to find unreached code. It can also find code reached by many flows so that we know that changes to that will affect many flows.

Production environment

By connecting our production environment to Digma, we can get real-world data about your code and issues currently in the wild. It allows us to be proactive instead of reactive.

This step requires that your customer is allowing OpenTelemetry to be collected and that there is a way for developers to connect their IDE to the central Digma instance. We have to be sensitive to their concerns regarding privacy and security.

We will now have a way to compare code running in production with code currently being developed. We can also get information about problems as they happen and even where they occur.

How impressive would it look to identify and fix a problem and have it deployed into production before our customer notices it? With Digma, this is possible!

Please visit https://digma.ai to learn more about this tool.

Observability for Agile Development: Why It’s a Game Changer

Feedback is at the core of agile development. We strive to improve and shorten the feedback loops wherever we can. We do code reviews, ask for customer feedback, and run automatic test suites to locate regressions in functionality and performance. However, there is an often overlooked feedback loop where we can gain important insights. One that will allow us to be proactive instead of reactive.

Have you ever felt worried when releasing a new version of your product? How many issues will our customers find this time? Would it not be better to locate some of these issues sooner before deploying the product into production?

We can get that feedback loop by combining OpenTelemetry and Micrometer alongside various open-source tools. We can also gain insights into how our code is performing by using a new tool. It analyses the collected data and produces insights into our code that we should look at. We can get these insights before submitting our changes to the code base. More about this tool in a bit. First, let’s look at OpenTelemetry. What is it, and how can we use the information it collects?

OpenTelemetry

OpenTelemetry is an Observability framework and toolkit designed to create and manage telemetry data such as traces, metrics, and logs. It has been standardized and works with many languages and frameworks.

It allows us to get a view into how our application is performing much in the same way a racing team uses telemetry to analyze and optimize the performance of a race car during practice and a race. By using this information, we can be proactive instead of reactive to possible issues with our applications.

How to activate this depends on your framework or language. If you are using Quarkus, you use an extension. If you are using Java without a framework, you use an agent. There is already a lots of documentation on enabling OpenTelemetry for different languages and frameworks, and I won’t repeat it here.

Collecting data won’t help us much. Luckily, several open-source tools will help us visualize the collected telemetry data.

Visualize telemetry

Jaeger allows us to monitor and troubleshoot workflows in complex distributed systems.

Prometheus helps us go from metrics to insights. It collects and stores its metrics as time series data. Metrics information is stored with the timestamp at which it was recorded, alongside optional key-value pairs called labels.

Grafana enables us to query, visualize, alert on, and explore your metrics, logs, and traces wherever they are stored.

OTel Collector is a vendor-agnostic way to receive, process, and export telemetry data. It is a central hub that connects the different parts of your observability environment.

Using telemetry

Now, we can collect and visualize information about the runtime performance of our application. We can plot graphs showing the number of requests per second and timing. We can follow requests between and internally in our services.

Enabling OpenTelementry and visualizing it in Jaeger and Grafana have given us more insight into how our application is performing. We are getting earlier feedback of potential problems and can be proactive instead of reactive. The problem is that we get so much data that it’s hard to locate problem areas.

Is there something we can do about that?

As it turns out, there is. Stay tuned for the next part, where we will build on OpenTelementry to get continuous feedback from our applications.

Mastering the Craft: 7 Habits of Highly Effective Developers

Physical training

Physical training, also known as exercise or fitness training, is crucial to maintain a healthy lifestyle and improve overall well-being. It involves engaging in planned physical activities targeting specific muscle groups, enhancing cardiovascular endurance, improving flexibility, and building strength.

Besides the physical benefits, exercise positively impacts mental and emotional well-being. Physical training stimulates the release of endorphins, often called the “feel-good” hormones. These neurotransmitters alleviate stress, anxiety, and depression, promoting a positive mood and overall mental well-being.

Exercise is proven to enhance cognitive abilities, memory, and concentration.

Sharing

As a software developer, you possess a wealth of knowledge and experiences that can benefit others in the field.

Sharing your knowledge helps others, but it will also showcase your expertise and solidify your position as an authoritative figure in the software development community. Teaching is the best form of learning. Try keeping a log of problems you have encountered and how you solved them. You will likely have similar issues in the future. You can also use it to find interesting topics to share.

Why not present something you have learned to your company or at a conference? I know it can be scary, but it’s also rewarding. It will help you grow as a developer.

Automate

Automation has become an essential aspect of the developers’ toolkit, especially with the increasing complexity of software development projects and the need for faster delivery. Automating repetitive tasks and processes saves time, improves efficiency, minimizes errors, and enhances productivity.

We will be more productive and can stay in the flow when writing code if we learn the keyboard shortcuts in the IDE rather than switching to the mouse.

A good way is to learn the shortcuts for the operations we do the most. For instance, how do I quickly jump between code and tests? How do I rename something?

We should not be afraid of using the command line to perform actions if it’s more effective.

Focus

Our ability to focus and efficiently manage our tasks is crucial to the success of any project. However, one of the biggest obstacles to achieving this focus is the constant context-switching we have to deal with. Moving from one task to another is referred to as context switching. These switches may happen due to changing priorities, interruptions from colleagues or stakeholders, or simply because we work on multiple features simultaneously.

We should minimize its impact on productivity and mental well-being, even if context switching might appear unavoidable in this fast-paced industry.

Turning off all social media and emails made it easier for me to focus. I put the editor in full screen when I code and try to use as many keyboard shortcuts as possible to avoid switching between keyboard and mouse.

A quiet and organized space promotes better focus. If there’s noise, investing in noise-canceling headphones could be worthwhile.

Reflect

Reflection is an essential and powerful practice that every software developer must embrace to reach new heights of success and excellence in their field. Developers can truly become indispensable team members by investing time and effort into thoroughly reviewing their work, analyzing mistakes, honing problem-solving abilities, elevating code quality, boosting efficiency, fostering professional growth, and nurturing collaboration. The act of reflection not only accelerates individual growth but also plays a pivotal role in driving the overall triumph of software development projects.

Journaling is a valuable practice that will undoubtedly enhance your daily life. By diligently documenting your experiences, you invite introspection and self-reflection and create a sense of closure at the end of each day. This journaling ritual enables you to release any lingering thoughts or emotions, granting you the freedom to unwind and detach from the events that transpired throughout the day.

Empathy

Empathy is the ability to understand and share the feelings of another person. It involves putting yourself in someone else shoes, considering their perspective, and acknowledging their emotions. Empathy fosters better communication and stronger relationships in a collaborative work environment. It enables teams to work more effectively together. You can find more information about this in a guest post I wrote here: https://digma.ai/blog/the-empathetic-developer/

Simplify

Code is not merely a tool for businesses; it is an essential component that directly impacts the behavior and value of software systems. Good code ensures efficiency, reliability, and maintainability. Poor quality code leads to increased costs and decreased value creation. By prioritizing code quality, businesses can avoid the pitfalls of technical debt, deliver faster and more stable systems, and achieve a competitive advantage in the market. Code is cost, and behavior is value – an equation that every business should strive to balance.

We should aim to provide comments that explain the reasoning behind the design of the code rather than just describing its functionality. Do not comment to cover up poor-quality code. We should program with intent. One way to help us with this is to apply the four rules for simple design by Kent Beck. It passes all the tests. It reveals intention. It avoids duplication and utilizes the fewest possible elements in its construction.

A good code should read like a story, not like a puzzle. William Zinsser describes his principle on writing in his book On Writing Well. We can create better results by focusing on simplicity, clarity, brevity, and humanity. Write programs for people to read and occasionally for machines to execute.

Review often to get feedback to avoid turning it into a blessing. Say what can be improved, not what is wrong.

Level up Your Java Skills with These Recommended Books

In this earlier post, I recommended some books that have helped me in my career as a software developer. I was asked if I had any books to recommend for Java developers. The books in this post are listed in the recommended reading order based on your experience with Java. You will get the most out of each book if you are familiar with the contents of the previously listed books. I’ll give my thoughts on the recommended target audience and what it will teach you. Enough introduction, let’s get to the books!

Head First Java: A brain-friendly Guide

So you have decided to learn Java? Great decision! Welcome to one of the most active communities in the world. This book doesn’t require you to have any previous knowledge of the Java language. It will teach you both fundamentals and some more advanced topics of Java in a brain-friendly way.

Java 8 to 21: Explore and work with the cutting-edge features of Java 21

You should have some experience with Java to get the most out of this book. I can see that it would be perfect for someone who needs a refresher in the language or has worked with older Java versions and wants to learn about all the new and exciting features. It also includes fundamentals on things you need to build, deploy and run your Java application.

Effective Java

It assumes a certain level of knowledge and experience with Java programming concepts. I would say that its target audience is experienced Java developers. The book is structured in several concise and self-contained items, making it easy to read and reference specific topics of interest. It combines theoretical explanations with practical examples and real-world scenarios, allowing readers to apply the concepts directly to their projects.

Cruising Along With Java: Modernize and Modularize with the Latest Features

I had to mention this book even though it’s still in beta. The final version is scheduled for a September 2023 release. You can get it now and receive updates as they are made available. Dr. Venkat Subramaniam explains the new capabilities of the Java language between version 9 and 19. Its target audience is experienced Java developers. Its content overlaps some of the content in the Java 8 to 21 book, but it’s aimed at a more experienced audience. It can be a better match for you if you have a few years of Java experience and want a deep dive into the new features introduced after Java 8.

Practical Design Patterns for Java Developers: Hone your software design skills by implementing popular design patterns in Java

This is a must-have book for any serious Java developer who wants to improve the way they write code. You should be comfortable with the Java language to get the most out of the book. Maybe you are working as a senior Java developer or an architect. It starts by doing a summary of software design principles, followed by information on the design of the Java platform. This gives the reader a good base for understanding the purpose and value of using design patterns. All patterns have a motivation for why they exist and an example of where they are used in the JDK.

The Developer’s Bookshelf: My Favorite Reads for Professional Growth

I wanted to share some recommendations on books that I have enjoyed reading. They have given me valuable information that has helped me in my career as a software developer.

The Pragmatic Programmer: Your Journey To Mastery

Maybe the book that made the biggest impact on how I work as a software developer. Read it, and become a pragmatic programmer. Learn about things like avoiding programming by coincidence. This book was first released in 1999 and is now updated and revised to reflect the changes in our field. It’s filled with practical advice to make you a better developer. I think that it should be mandatory reading for any developer.

On Writing Well: The Classic Guide to Writing Nonfiction

Writing software is not that different from writing an article. This book will teach you techniques to write text that are easy to read and understand, characteristics that we want our code to have. By structuring our code as an article, we give the reader a fast overview of what it does. The reader can decide to learn more about a specific function by following the flow of the story. It explains the Single Layer of Abstraction principle in an easy-to-understand way.

Atomic Habits

You can change your life with tiny changes in behavior. By continuously making tiny changes, we can get remarkable results. It presents a way to build good habits and break bad ones.

Pragmatic Thinking And Learning: Refactor Your Wetware

As a developer, we constantly need to learn new skills. This book will teach you how the brain works and how we learn. We are introduced to a model for skill acquisition and how that affects how we learn new skills. By being aware of this model, we can adapt how we teach skills to others.

Ask

By asking the right questions, you discover what your customer wants. This book is about marketing, but we can use the method to understand what our customers want the software we are building to do.

5 Smart Ways to Use java.util.Objects

Objects provide a set of utility methods to manipulate Java objects. In this post, we will delve deep into the world of java.util.Objects and examine five smart ways to use it. From comparison, null-checking to hashing, there is much to learn, so buckle up and let’s dive in!

Introduction

The java.util.Objects class was added in Java 7 to provide utility methods for working with objects. Its purpose is to simplify common tasks such as null-checking and equality testing and to reduce the amount of boilerplate code that developers need to write.

Prior to Java 7, developers had to write their own null-checking code using if statements, which could be error-prone and time-consuming. The java.util.Objects class provides a set of static methods that handle null values in a consistent and reliable way, making it easier for developers to write robust and maintainable code.

Checking for Null objects with requireNonNull

When working with objects in Java, it is important to ensure that they are not null to avoid unexpected NullPointerExceptions. To address this, Objects contains a method, requireNonNull(), which helps check for null objects before they are used. This gives the programmer control of when to check for null objects instead of having the code blow up when the object is used.

public int doSometing(String parameter) {
   // do lots of stuff here
   return parameter.length();
}

If the method doSomething is called with a null value it will throw a NullPointerException, Cannot invoke “String.length()” because “parameter” is null. In this case, we can clearly see the problem in the code but if the method is more complicated it might not be so easy. A good programming practice is to check all parameters for validity before using them. Objects give us not one but three methods to do just that.

public int doSometing(String parameter) {
   Objects.requireNonNull(parameter);
   // do lots of stuff here
   return parameter.length();
}

This will also throw a NullPointerException. The difference, in this case, is that it will be thrown by the requireNonNull call at the start of the method. If we need to provide more information in the exception we can use one of the other two variants of this method.

Objects.requireNonNull(parameter, "Additional information");
Objects.requireNonNull(parameter, () -> "Additional information");

Both of these will use the provided message in the generated exception. The difference is that the second variant will only generate the message if the given object is null.

Providing Default Values with requireNonNullElse

The requireNonNullElse method is a convenient way to provide default values for null objects. It was introduced in Java 9 and helps reduce NullPointeExceptions by providing a value to use when a null is received, avoiding the need for the developer to explicitly check for null before using the object.

It reduces the risk of NullPointerExceptions and helps to make our code more concise and readable. By providing default values for null objects, it simplifies our code and makes it more robust. As such, it is a valuable addition to our programming arsenal and should be used whenever appropriate.

public int doSometing(String parameter) {
   String localParam = Objects.requireNonNullElse(parameter, "");
   // do lots of stuff here
   return localParam.length();
}

In addition to the basic usage described above, the method has other features that make it more flexible. For example, it works with any type of object and can handle complex objects.

To optimize performance, the alternative variant of this method, requireNonNullElseGet, can be utilized when obtaining the default value involves a resource-intensive operation. This method takes a Supplier instead of an object as the second parameter and we can use it to call a method to get the default value. The Supplier will only be called if the provided parameter is null.

public int doSometing(String parameter) {
   String localParam = Objects.requireNonNullElseGet(parameter, this::getDefaultValue);
   // do lots of stuff here
   return localParam.length();
}

private String getDefaultValue() {
   return "";
}

Comparing Objects with Objects.equals()

Objects.equals method was added to the Java language to provide a null-safe comparison of objects, and to address the limitations of the Object.equals() method. It makes it easier and more convenient to compare objects and is a useful addition to the language.

Before Java 7 and the addition of Objects.equals we had to construct something like the following example to make a null-safe comparison between two objects. It contains lots of details and boilerplate code.

private boolean isEqual(Object s1, Object s2) {
   if (s1 == null && s2 == null) {
      return true;
   }

   if (s1 == null || s2 == null) {
      return false;
   }

   return s1.equals(s2);
}

By using Objects.equals we can instead implement this method like the example below. The Objects.equals method will take care of the null checks and will only call the equals method on our object if both s1 and s2 are non-null. Not only will this remove the noise of checking for null, but it will also protect the equals method in our object from being called with a parameter that is null.

private boolean isEqual(Object s1, Object s2) {
   return Objects.equals(s1, s2);
}

Retrieving HashCodes with Objects.hashCode()

In this example, the hashCode() method is implemented using Objects.hashCode() to calculate the hash code based on the id and name fields. By passing the fields as arguments to Objects.hashCode(), it will internally handle null values and produce a consistent hash code.

import java.util.Objects;

public class MyClass {
    private int id;
    private String name;

    // Constructor, getters, and other methods

    @Override
    public int hashCode() {
        return Objects.hashCode(id, name);
    }

    // we have to implement a equals method, but it have been left out for brevity
 
}

It’s important to ensure that the fields used in hashCode() are the same fields considered in the equals() method to maintain the general contract between these two methods.

By implementing hashCode() in this manner, you can generate a hash code that takes into account the relevant fields in your class, simplifying the process and ensuring consistency.

Creating Custom Comparison Strategies with Objects.compare()

You can use the Object.compare() method if you want to compare two instances of an object that doesn’t implement a comparator or if you want to use a custom comparison strategy. It will return 0 if both instances are equal. Otherwise, it will return the result of the passed comparator. This means that it will return 0 if both instances are null. You might still get a NullPointerException, depending on the implementation of the comparator.

Integer a = 42;
Integer b = 7;
Objects.compare(a, b, Integer::compareTo);  // will return 1

By supplying a custom comparator to this method, we can create a custon comparison strategy.

Integer a = 42;
Integer b = 7;
Objects.compare(a, b, this::myCustomComparator);

private int myCustomComparator(Integer val1, Integer val2) {
   int result = 0;
   // implementation of custom comparison
   return result;
}

Conclusion: Embracing the Flexibility of java.util.Objects

In conclusion, java.util.Objects provide a wide range of useful methods that can simplify the coding process. By taking advantage of these methods, developers can write cleaner, more efficient code and reduce the likelihood of runtime errors.

Whether you’re a seasoned Java developer or just getting started with the language, understanding how to use java.util.Objects effectively is an important skill to have. By incorporating these methods into your codebase, you can improve code quality and productivity while reducing the risk of errors and bugs in your application.

The Power of Small Habits: Utilising Atomic Habits to Develop Your Team’s Skills

Small habits have significant power in our lives and work. Habits we develop over time can either make or break us, and the same goes for the results we achieve with our team. As a leader or manager, one responsibility is to foster the development of your team’s skills by motivating them and promoting positive habits.

This is where the concept of atomic habits comes in. Atomic habits are tiny modifications that are simple to adopt and stick to, yet they can have a significant impact on the outcome. By adopting atomic habits, you can develop your team’s skills gradually and consistently, resulting in significant growth, improved performance, and ultimately success.

In this article, we will explore the concept of atomic habits and how you can utilize them to develop your team’s skills. We will discuss the benefits of adopting atomic habits, the different types, and practical tips for effectively implementing them. After reading this article, you’ll know how to use small habits to improve your team’s skills and achieve more success.

Introduction to Atomic Habits: Understanding the Power of Small Changes for a Learning Mindset

Atomic Habits, a book written by James Clear, is a revolutionary guide to understanding how small changes in our daily habits can compound over time and lead to significant improvements in our lives. By mastering the art of creating and maintaining atomic habits, one can develop a learning mindset that will help them achieve their goals, whether they are personal or professional.

According to Clear, behaviour change is driven by four fundamental laws that influence our actions:

Cue – The initial trigger that signals our brain to begin a specific behavior.

Craving – The desire or craving that motivates us to take action and seek a reward.

Response – The actual behavior or action we take in response to a cue and craving.

Reward – The positive outcome that reinforces our behavior and satisfies our craving.

By understanding these four laws, we can create atomic habits that work in our favor rather than against us. For example, if we want to develop a habit of writing more maintainable code:

Cue – Create an environment that promotes maintainable code.

Craving – A desire to write maintainable code.

Response – Implement maintainable coding practices.

Reward – A sense of accomplishment for writing maintainable code.

The Importance of Small Wins

One of the key principles of Atomic Habits is the importance of making small, incremental improvements. Rather than trying to overhaul our entire lives overnight, we should focus on making small, sustainable changes that will compound over time.

Clear argues that the most significant changes in our lives come from the compound effect of small wins. For example, if we want to write maintainable code, we can focus on using meaningful variable names or avoid hard-coding values.

These small wins, when repeated consistently, can lead to significant improvements in the maintainability of the code over time. If you can get 1% better each day, you’ll end up with results that are up to 37 times better after just one year.

The Learning Mindset

Developing atomic habits is not just about achieving individual goals but about developing a learning mindset that will help us adapt and thrive in a constantly changing world. By focusing on small improvements and learning from our failures, we can build resilience and become better equipped to succeed in the face of adversity.

Clear argues that the key to developing a learning mindset is to focus on identity-based habits rather than outcome-based habits. Rather than focusing on achieving a specific outcome (writing maintainable code), we should focus on developing habits that align with our desired identity (such as becoming a sought-after expert in the software industry).

Identifying Current Habits: Assessing Your Software Development Team’s Learning Habits

As a software development team lead, identifying the current habits of your team is crucial to ensure continuous learning and development. By analyzing learning needs, evaluating current learning methods, identifying learning styles, tracking progress, providing regular training opportunities, and encouraging collaboration and communication, you can create an environment where learning becomes a natural and ongoing process. This will benefit both your team and your business by enhancing the quality of your software products and services.

Setting Clear Goals: Fostering Motivation and Direction for Individual and Team Development

Setting clear goals is an essential aspect of personal and professional development. It gives direction, focus, and motivation for individuals. Without clearly defined goals, people often lack direction and purpose, which can lead to a lack of motivation and poor performance.

Setting clear goals is essential for personal and professional development. It gives individuals and teams the direction and motivation needed to achieve their desired outcomes. Remember to set SMART goals, break them down into smaller targets, develop an action plan, and monitor progress to ensure success. With clear goals, you can achieve your dreams and aspirations!

Breaking Down Learning Goals: Creating Manageable Steps for Consistent Progress

Learning goals are what learners hope to achieve as a result of undertaking a particular learning process. These goals are essential for several reasons. Firstly, they provide guidance and focus, making it easier for learners to stay on track; secondly, learning goals enable learners to assess their progress more objectively and make adjustments where necessary. Thirdly, learning goals can help to motivate learners and boost their confidence.

Additionally, learning goals are vital because they help learners to see the bigger picture, even when studying complex subjects. When set up correctly, learning goals can shape the way students prioritize their time and resources. By breaking down goals into smaller and more manageable steps, students can manage their time and avoid feeling overwhelmed when attempting to tackle any project head-on.

Tracking Progress: Using Metrics and Data to Measure and Celebrate Small Wins

Tracking progress is an essential part of successful goal setting. However, measuring progress requires more than just setting objectives and tracking numbers. It involves establishing clear metrics and data to measure results, identifying potential barriers, and celebrating small wins.

Metrics are your tools for measuring progress. You must identify your most significant objectives, set clear goals, and determine how to measure the results. For example, if the goal is to increase website traffic, you might track metrics like sessions, bounce rates, and click-through rates. By focusing on specific metrics, you can accurately gauge your progress and make data-driven decisions to improve your performance.

It’s essential to identify potential barriers that can hinder your progress. These barriers may be internal or external factors such as lack of resources, lack of skills or knowledge, or market changes. Once you identify these barriers, you can create a plan to overcome them.

One way to identify potential barriers is to conduct a SWOT analysis, Strengths, Weaknesses, Opportunities, and Threats. A SWOT analysis can help you identify areas where you need to improve and give you a better understanding of your competitive landscape.

Monitoring progress is crucial because it allows you to measure the effectiveness of your efforts. Without monitoring, you won’t know if you’re making progress or not. Monitoring can also help you identify areas where you need to change your strategy.

People tend to focus on the big wins, but celebrating small wins can be just as important. Celebrating small accomplishments along the way can help you stay motivated and engaged.

When celebrating small wins, make sure to recognize the effort, not just the result.

Small wins provide positive reinforcement that can help you sustain your efforts toward achieving your bigger goals. Celebrating small wins can also help to create a culture of recognition within your team.

Creating a Positive Learning Environment: Encouraging Collaboration and Open Communication

One of the key elements of creating a positive learning environment is creating a culture that values collaboration and teamwork. When people feel that their opinions are valued, and they feel supported by their peers, they are more likely to participate actively in discussions and activities. Collaboration fosters a sense of community in the team and promotes positive relationships between members, leading to better communication and improved learning outcomes.

Creating a positive learning environment also involves using positive reinforcement. Recognizing and celebrating team members’ efforts and achievements, and providing them with constructive feedback on their growth, motivates them to strive for excellence. Leaders should avoid negative reinforcement, such as punishments, which can reduce team members’ confidence and discourage them from participating fully in activities.

Finally, it is vital to establish clear guidelines and expectations for behavior and conduct. Leaders should create rules and enforce them consistently, ensuring that all team members fully understand what is expected of them. The guidelines should promote respect, inclusivity, and responsibility for individual and collective learning outcomes.

Building Feedback Loops: Leveraging Feedback to Continuously Improve Habits and Skills

Human beings are born with the innate desire to improve themselves. Whether it’s learning a new skill, improving existing habits, or achieving a personal goal, people are constantly striving to make themselves better. However, improving oneself is not always an easy task. Often, people lack the knowledge, motivation, and discipline to stick to the changes they seek. This is where feedback loops become vital.

Feedback loops are an effective way of continuously improving habits and skills. They help individuals track their progress, identify areas that need improvement, and make necessary adjustments. Building feedback loops is not rocket science. It’s a simple process that involves setting clear goals, gathering relevant data, analyzing it, and making necessary adjustments. By incorporating feedback loops into your daily routines, you’ll be amazed at how much progress you can achieve toward your goals.

Adopting a Growth Mindset: Embracing Challenges and Learning from Failures

When it comes to personal development and achieving our goals, the way we approach challenges and failures can significantly impact our success. Some people have a fixed mindset, believing that their abilities and qualities are predetermined and cannot be changed. Others have a growth mindset, perceiving challenges and setbacks as opportunities for learning, improvement, and new experiences.

Adopting a growth mindset can be a powerful tool for personal growth, success, and fulfilment. By reframing challenges as opportunities, learning from failures and setbacks, focusing on the process, and surrounding yourself with growth-oriented people, you can develop a more positive and proactive outlook on life. Remember, growth is a lifelong journey, and there’s always room for improvement and learning. So embrace challenges, take risks, and never stop growing.

Developing Resilience: Staying Committed to Learning Habits Through Setbacks and Obstacles

Learning is a lifelong process. No matter how old we are, there is always something new to learn. Whether you’re a student, a working professional, or simply someone who wants to enhance your knowledge, learning takes commitment and resilience. It requires dedication and constant effort to develop skills and acquire new knowledge. But what happens when setbacks and obstacles come your way, and you feel like giving up? This is where resilience comes in.

Resilience is the ability to bounce back from setbacks, overcome challenges and persevere when faced with obstacles. It’s a key trait that helps us push through difficult times and continue to progress in our learning journey.

Developing resilience is essential for staying committed to your learning habits through setbacks and obstacles. By reframing setbacks as opportunities for growth, setting realistic goals, practicing self-care, seeking support when needed, embracing failure, and staying flexible and adaptable, you can maintain a positive attitude and continue to make progress in your learning journey. Remember, learning is a process, not a destination, so stay committed and stay resilient!

Maintaining Consistency: Making Learning a Sustainable Habit for Your Software Development Team

In the fast-paced world of software development, it is essential to keep up with the latest technologies and trends. This requires a team that is constantly learning and adapting to new changes.

To ensure that your software development team continues to learn and grow, you need to make learning a sustainable habit.

This requires creating a culture that supports continuous learning and provides the necessary resources and opportunities. As a team leader, you need to set an example, provide resources, integrate learning into the workflow, celebrate success, measure progress, and provide feedback. By doing this, you can ensure that your software development team remains agile, adaptive, and relevant in the ever-changing world of technology.

To learn more about Atomic Habits, visit: https://jamesclear.com

The CSI of Coding: Unravelling the Mysteries of Technical Debt

Does your team spend more time debugging and fixing issues instead of developing new features? Is it hard to estimate how long it will take to implement an issue? Will fixing one issue result in new issues? Chances are that your code contains lots of technical debt, which will make it hard to maintain. Maybe you feel this is true but you can’t point to where the problem lies. Maybe you know about this but don’t know how to communicate the business impact. Fear not. A new book is now available, which offers valuable principles and methods that will shed light on the darkest corners of your code. Read on.

This review is of the upcoming second edition of the book, Your Code as a Crime Scene by Adam Tornhill. It is a unique book that takes a fresh approach to code metrics, by drawing analogies from criminal investigations. A dive into this book reveals the interesting fact that software development and forensic investigations share many similarities.

TL;DR

I like that we can use techniques from other professions to improve the way we write software. In this case, it’s from criminal psychology. We can analyze our code by applying these techniques to data mined from the version control system. This gives us metrics that not only measure the quality of the code but also how we as people work together. These metrics make it possible to visualize both the state of the code but also how knowledge of it is spread among the developers. This will be useful to show how the system changes over time and gives us early warning so that we can act upon problem areas before they have a chance to become too big. It also gives us something concrete to use when communicating the business impact of technical debt in the code with the stakeholders.

The book uses a tool, code-maat, to analyze the mined data. The code is available as open source. There are references to articles describing the relevant science. Even though the book does not explain how the tool is doing the analysis, I have all I need to look up this information by myself.

Highly recommended for any developer in a senior role that is struggling with technical debt in their code.

Review

The author argues that code can be likened to a crime scene, where clues are scattered all over pointing to the offender; in this case, code smells and complexity metrics can help identify the root cause of technical debt and bugs in a software project.

The book is designed to help software developers recognize patterns that lead to problematic code and identify the root cause of software bugs, which is a critical skill in modern software development.

It is divided into three parts where you will learn to detect problematic code, improve software architecture, and how your organization affects the code.

One of the central ideas of the book is the concept of coupling, which is the degree to which components of a system depend on each other. Adam Tornhill argues that a focus on modularity and low coupling can help reduce complexity and improve the quality of software systems.

Another important concept discussed in the book is the idea of technical debt, which is the cost of maintaining software that was not designed with future changes in mind. The author presents tools and techniques to help identify and manage technical debt.

The book also presents the idea of the “hotspots,” which are the most problematic parts of software code. These hotspots are identified using traditional metrics such as Cyclomatic Complexity combined with techniques from criminal psychology. The author also presents a tool called CodeScene which is used to identify hotspots in code using visual analysis.

Apart from discussing the above-mentioned concepts, the book also provides practical advice on how to apply these concepts in practice and how to communicate the business impact of technical debt. Throughout the book, the author offers real-world examples and case studies to illustrate the practical application of the concepts.

The book “Your Code as a Crime Scene” is an essential read for software developers who wish to enhance their understanding of software quality and defect analysis. The book offers practical approaches to identifying and remedying software defects by drawing parallels between software development and forensic investigations. It gives you a better way to communicate the state of the code to the stakeholders.

In conclusion, Your Code as a Crime Scene provides valuable insights into the world of software development, and how to identify and manage defects in software code. The book offers practical advice on how to apply the concepts discussed in practice, making it a valuable resource for software developers of all levels. Whether you are a seasoned veteran or just starting your journey as a software developer, this book is a must-read.

Maintainable code: what is it and why should Java back-end developers care?

Most of us would rather work on a new project instead of on an old messy legacy system. Most developers I talk to tell me that they like to work with code that is easy to maintain. Somehow we end up with all these legacy systems that no one dares touch for fear of breaking something. Why is it that we continue to create projects that are hard to maintain when no one wants to work on them?

We are quite good at identifying if a codebase is maintainable or not by how hard it is to make changes to it. At the same time, we are not so good at preventing it from becoming that way in the first place. Some examples of common reasons code become hard to maintain are:

Over-engineering: The solution tries to solve a problem that doesn’t exist. The code is more complex than it needs to be.

Inconsistent coding practices: The team doesn’t have a common way of writing code.

Lack of refactoring: Existing code is only changed if there is a problem with it.

The lack of a good definition of what maintainable code is can make it easy to miss that the codebase is becoming less maintainable.

If we instead define some wanted characteristics of maintainable code. These are then used to check the code we create to see if we are making it more or less maintainable. We will also look into what benefits writing maintainable code can have for you and your career.

Characteristics of maintainable code

The goal of maintainable code is that it should enable developers to quickly understand and update it without introducing errors or unintended behaviors. A software system is rarely “finished” and is always evolving to meet new requirements. Code is like a garden. It has to constantly be cleared of weeds or otherwise, it will grow completely overgrown. To achieve this a codebase needs to have the following characteristics:

Modularity. It should consist of components that are small, independent, and reusable.

Readability. Written with a clear and consistent domain language, where all variables, functions, and modules have meaningful names.

Testability. It should have automated test suites that test the behavior of the system so changes to it do not cause unintended consequences.

Scalability. It should be able to handle increasing amounts of data and users without becoming slow or difficult to change. Do this with moderation. There is no need to be able to scale up to infinity unless there is a specific business need for that.

Documentability. It should have up-to-date documentation describing why it is implemented the way it is to help new developers understand how it works. Well-written tests can also be used as a form of documentation to show the intended behavior of the system.

Flexibility. Designed in a way that it’s easy to change and adapt to meet changes in the requirements.

Reusability. Designed in a way so that modules can be used to build and integrate with other systems.

What’s in it for me?

By writing maintainable code that is easier to change and debug, you will save time and increase productivity in the long run. You and your team will have better collaboration and teamwork because the code is easier for other developers to understand and work on. Demonstrating the ability to write maintainable code can be a valuable skill in the job market, increasing the likelihood of being hired for high-quality projects and advancing in your career. It will also enhance your reputation and credibility, leading to better opportunities and greater recognition in the software industry.

Overall, writing maintainable code is a best practice that can lead to better software and a more successful career for developers. In the coming posts I’ll talk about what we can do to achieve these characteristics so don’t forget to register to be notified when new articles are posted.