Effective Programming

Effective programming is developing a program correctly, securely, efficiently, and in an easily understandable way. This includes the effective use of programming languages and other tools.

An approach independent of programming languages is required to develop efficient software. However, having a deep knowledge of the programming language used can help develop more effective and efficient software.

Efficient programming means developing the program run as fast and cost-effectively as possible, using as few resources as possible.

Effective programming provides many benefits:

  • Performance: Using effective programming techniques, programs can be made faster and more efficient, meaning they run faster and use fewer resources.
  • Reliability: Code written using effective programming techniques can contain fewer errors and security vulnerabilities, making it safer for users to use the programs.
  • Maintenance: Effective programming techniques such as code readability, modularity, and reusability make it easier to maintain programs. This saves time and cost when the code needs to be updated, restructured, or modified.
  • Testing: Effective programming techniques make it easier to test code, meaning tests are written and used to ensure software works correctly.
  • Errors: Effective programming techniques can help make the code less prone to errors, making the program run more safely and with fewer problems.

For example, imagine you have 15,000 different methods, and each method logs something when executed. You have a class for logging, which has a simple method:

public static void printLog(String methodName) {
System.out.println("Method Name: " + methodName);
}

Although this method appears to perform a simple operation, unnecessary garbage can be created in memory when called by all methods. Instead, a mechanism for deciding whether to print logs can be implemented. A boolean can be defined, and used as follows:

public static void printLog(String methodName) {
if (isTrace) {
System.out.println("Method Name: " + methodName);
}
}

However, this method will also fail because, when called, a string will be defined and a copy will be created. This copy will be sent to the method, and if isTrace=false, the log will not be printed, causing unnecessary garbage to be created. Instead, by checking isTrace before calling this method, we can prevent unnecessary garbage from being created:

if (isTrace) {
printLog("test method name");
}

By using this method, the log is checked for isTrace before the method is called, avoiding the creation of unnecessary garbage.

Another important issue is loops. Loops are used in all programming languages, and small mistakes in loops can lead to significant problems.

However, creating variables inside loops can lead to unnecessary memory usage, which can negatively affect program performance.

int i = 0;
while (i < 1000000) {
String methodName = "Test service " + i;
printLog(methodName);
i++;
}

Therefore, variable declarations in loops should be done outside the loops if possible. This way, all operations can be easily and more efficiently performed using a single variable.

String methodName = null;
int i = 0;
while (i < 1000000) {
method = "Test method " + i;
printLog(method);
i++;
}

This method reduces the memory usage of the program and allows operations to be performed more efficiently. However, it is also important for the efficiency of the program to limit the loops and determine their conditions correctly. Therefore, simplifying operations in loops as much as possible and avoiding unnecessary operations will increase the program’s performance.

Another issue is careless ordering of control statements in if-else statements. During development, the ordering of code is important.

For example, the Java virtual machine evaluates control conditions from left to right. Therefore, it is important to ensure that control conditions are properly ordered.

if(A() && B() && C(D()))
1. The method A() is called first and if it returns true,
2. Then the method B() is called and if it returns true,
3. The method D() is called and its output is given as an argument to method C().

If any of the methods return false, the control exits without performing the remaining checks. Therefore, it is more reasonable and effective to place control conditions wisely. For example, a situation like the one below will cost us a lot.

if(expensiveMethodCall() && cheapMethodCall() && cheapBoolEval)

Instead, a boolean check should be performed first and conditions should be ordered as follows:

if(cheapBoolEval && cheapMethodCall() && expensiveMethodCall())

One important topic is comparison operations. Using “greater/less than” operators for comparison operations is faster than equality check. This is because, unlike equality check where all bits of a number need to be checked, in the case of greater/less than check, only a few bits of the number need to be checked. Therefore, in Java, greater/less than operators are preferred for comparison operations.

if(counter==1000000) vs if(counter>1000000)

Finally, another example that comes to mind is a comparison situation like the one below. Not much attention is paid to such comparisons, but if you have a situation like the code example below, you should consider that the Country object can be null and write this comparison statement in a better way.

    private final static String CODE = "TR";

boolean isCountryCodeTR(final Country country){
if(country.getCode().equalsIgnoreCase(CODE)){
return true;
}
return false;
}

You can see how to do this in the simplest way in the following code example.

    private final static String CODE = "TR";

boolean isCountryCodeTR(final Country country){
if(CODE.equalsIgnoreCase(country.getCode())){
return true;
}
return false;
}

Using effective programming techniques can make programs more performant, reliable, easy to maintain, and less error-prone. This translates to user satisfaction and less time and cost loss.