Earning Maintainability badge

Writing long lines of code can be a simple task depending on the use case, but maintaining the code can be really hard. Debugging, refactoring, extending and improving it will be difficult if it is not properly maintained. 

Every day, new software is developed and deployed. As requirements change, software must be updated. To update or implement new features in the software, it must be properly maintained (i.e. organized). This is the crux of the maintainability badge. 

Common pitfalls while writing maintainable code

  1. The code is not organized logically. 
  2. Code is duplicated (i.e. it violates the DRY principle). 
  3. All the code rests in a single file. 
  4. LOC is not evenly distributed across all files.
  5. The code has nested if and for statements.
  6. The code contains complex methods.

Refer details and examples below.

The code is not organised logically

When all code is in one place, it is very hard to maintain. Therefore, it is critical to keep folder structures intact. There are two well-known methods for organizing code efficiently.

  1. Stack style- separate modules at various layers of the tech stack 
  2. Entity style - As logical boundaries become clearer, refactoring becomes easier.

Fig1. stack style

Fig2. entity style

Code is duplicated

One of the core principles of writing maintainable code is Don’t Repeat Yourself(DRY). Duplication makes it difficult to maintain code. If you need to change some functionality, you must find all of the places where the code has been duplicated and change it there. This is both time consuming and error prone. 

Example for code duplication:

Bad code:

public class SettingPanel {
    public UserContext userObj;
    
    public SettingPanel(UserContext currentUser){
        this.userObj = currentUser
    }
   
   public void addUser(User newUser){
        if (this.userObj.getRole() == "ADMIN"){
            /*
            Add new user
            **/
        }else{
            /*
            throw exception
            ***/
        }
    }
    
    public void deleteUser(String userId){
        if (this.userObj.getRole() == "ADMIN"){
            /*
            Delete that user
            **/
        }else{
            /*
            throw exception
            ***/
     
    }
    
}

<br>

Here, we can see that the piece of  code that checks if the role is an admin is duplicated throughout this class. 

It can instead be rewritten as:

public class SettingPanel {
    public UserContext userObj;
    
    public SettingPanel(UserContext currentUser){
        this.userObj = currentUser
    }
    
    
    private boolean isAdmin(){
        if (this.userObj.isAdmin()){
            return true;
        }
        
        return false;
    }
    
    public void addUser(User newUser){
        if (this.isAdmin()){
            /*
            Add new user
            **/
        }else{
            /*
            throw exception
            ***/
        }
    }
    
    public void deleteUser(String userId){
        if (this.isAdmin()){
            /*
            Delete that user
            **/
        }else{
            /*
            throw exception
            ***/
     
    }
    
}

<br>

There are complex methods in the code

To ensure code has correct logic, developers often end up writing a large number of complex conditional statements and looping statements. This makes debugging code extremely difficult.

Example of complex code:

public class TheTieBreaker {
    public static void main(String[] args) {
        // contains players of the team
        HashMap < Integer, Player > team1 = new HashMap < Integer, Player > ();
        team1.put(0, new Player(0, "Kirat Boli", 5, 10, 25, 10, 25, 1, 14, 10));
        team1.put(1, new Player(1, "N.S Nodhi", 5, 15, 15, 10, 20, 1, 19, 15));
        HashMap < Integer, Player > team2 = new HashMap < Integer, Player > ();
        team2.put(0, new Player(0, "DB Vellyers", 5, 10, 25, 10, 25, 1, 14, 10));
        team2.put(1, new Player(1, "H Mamla", 10, 15, 15, 10, 20, 1, 19, 15));
        // contains list of teams
        List < HashMap < Integer, Player >> teams = new ArrayList < HashMap < Integer, Player >> ();
        teams.add(team1);
        teams.add(team2);
        teams.get(0).get(0).setStrike(true);
        teams.get(1).get(0).setStrike(true);
        // contains the total score of each team
        int[] teamScores = new int[2];
        // commentary
        StringBuffer[] commentary = {
            new StringBuffer(""),
            new StringBuffer("")
        };
        Ball ball;
        int ballCount = 0;
        // loops 2 times, for each team
        for (int i = 0; i < 2; i++) {
            List < Ball > balls = new ArrayList < Ball > ();
            // loops 6 times, 1 over
            for (ballCount = 1; ballCount < 7; ballCount++) {
                ball = new Ball();
                ball.setCount(ballCount);
                PlayerAndBall ballOutput = null;
                if (teams.get(i).get(0).isStrike()) {
                    ballOutput = Methods.getBallOutput(ball, teams.get(i).get(0));
                    ball.setScore(ballOutput.getBall().getScore());
                    ball.setPlayerId(ballOutput.getBall().getPlayerId());
                    balls.add(ball);
                    teams.get(i).get(0).addBall(ball);
                    if (ball.getScore() == -1) {
                        teams.get(i).get(0).setOutStatus(true);
                        commentary[i].append("0." + ballCount + " " + teams.get(i).get(0).getName() + " gets OUT.\n");
                        break;
                    } else {
                        teamScores[i] += ball.getScore();
                        commentary[i].append("0." + ballCount + " " + teams.get(i).get(0).getName() + " scores " +
                            ball.getScore() + " runs.\n");
                    }
                    // changes the strike of the batsman depending upon the runs
                    if (!ballOutput.getPlayer().isStrike()) {
                        teams.get(i).get(0).setStrike(false);
                        teams.get(i).get(1).setStrike(true);
                    }
                    if (i == 1 && teamScores[1] > teamScores[0])
                        break;
                } else if (teams.get(i).get(1).isStrike()) {
                    ballOutput = Methods.getBallOutput(ball, teams.get(i).get(1));
                    ball.setScore(ballOutput.getBall().getScore());
                    ball.setPlayerId(ballOutput.getBall().getPlayerId());
                    balls.add(ball);
                    teams.get(i).get(1).addBall(ball);
                    if (ball.getScore() == -1) {
                        teams.get(i).get(1).setOutStatus(true);
                        commentary[i].append("0." + ballCount + " " + teams.get(i).get(1).getName() + " gets OUT.\n");
                        break;
                    } else {
                        teamScores[i] += ball.getScore();
                        commentary[i].append("0." + ballCount + " " + teams.get(i).get(1).getName() + " scores " +
                            ball.getScore() + " runs.\n");
                    }
                    // changes the strike of the batsman depending upon the runs
                    if (!ballOutput.getPlayer().isStrike()) {
                        teams.get(i).get(1).setStrike(false);
                        teams.get(i).get(0).setStrike(true);
                    }
                    if (i == 1 && teamScores[1] > teamScores[0])
                        break;
                }
            }
        }
        // printing the match result
        if (teamScores[0] > teamScores[1]) {
            System.out.println("Lengaburu won by " + (teamScores[0] - teamScores[1]) + " runs");
        } else if (teamScores[0] < teamScores[1])
            System.out.println("Enchai won with " + (6 - ballCount) + " balls remaining.");
        else
            System.out.println("TIE AGAIN.");
        // printing players scores
        System.out.println();
        System.out.println("Lengaburu");
        for (int i = 0; i < teams.get(0).size(); i++) {
            if (teams.get(0).get(i).isOutStatus())
                System.out.println(teams.get(0).get(i).getName() + " - " + teams.get(0).get(i).getTotalScore() + " (" +
                    teams.get(0).get(i).getBalls().size() + " balls)");
            else
                System.out.println(teams.get(0).get(i).getName() + " - " + teams.get(0).get(i).getTotalScore() + "* (" +
                    teams.get(0).get(i).getBalls().size() + " balls)");
        }
        System.out.println();
        System.out.println("Enchai");
        for (int i = 0; i < teams.get(1).size(); i++) {
            if (teams.get(1).get(i).isOutStatus())
                System.out.println(teams.get(1).get(i).getName() + " - " + teams.get(1).get(i).getTotalScore() + " (" +
                    teams.get(1).get(i).getBalls().size() + " balls)");
            else
                System.out.println(teams.get(1).get(i).getName() + " - " + teams.get(1).get(i).getTotalScore() + "* (" +
                    teams.get(1).get(i).getBalls().size() + " balls)");
        }
        // printing commentary
        System.out.println();
        System.out.println("Commentary");
        for (int i = 0; i < commentary.length; i++) {
            if (i == 0)
                System.out.println("Lengaburu innings:");
            else
                System.out.println("Enchai innings:");
            System.out.println(commentary[i]);
        }
    }
}

In the above example, we can see a lot of conditional statements, which increases the size and complexity of the code - especially when we need to add more logic in the future. However, by following design patterns, we can organize the code to avoid conditional statements. 

The above code can be rewritten as:

public class TheTieBreaker {
    private Team batting;
    private Team bowling;
    private Match match;
    
    public TieBreaker(Team batting, Team bowling) {
        this.batting = batting;
        this.bowling = bowling;
        match = new Match(batting, bowling);
    }
    public void playInning(int targetScore, int maxOvers) {
        int score = 0
        for (int over = 0; over < maxOvers; over++) {
            int runs = this.match.simulateOver(score, over, maxOvers, batting.OnStrike());
            score += runs;
        }
        printResult(score, targetScore);
    }
    public void printResult(int score, int target) {
        // logic to decide what need to be the result
        System.out.println(result);
    }
}
public class Match {
    // init state variables
    // constructor etc
    public int simulateOver(int currentScore, int currentOver, int maxOvers, Player player) {
        // logic to simulate an over
        printCommentary(ball, ballScore, player, currentScore);
        // do something
        return runs;
    }}
<br>

Code has nested if and for statements

Nested for and if statements in the codebase makes it difficult to troubleshoot any issues. This can be avoided by using proper data structures and design patterns.

LOC is not evenly distributed across files

While developing an application, the codebase is often organized in different files for better maintainability. But one trap that developers fall into is concentrating the majority of their logic in  one or two files. While it is not necessary to have all files with the same LOC, it is advised to distribute your code more evenly across different files. Doing this makes it easier to find and modify specific parts of the code, which can save time and effort when working on a project.

All the code rests in a single file

It is generally recommended to write code in multiple files instead of just one. Using multiple files can make it easier to manage and maintain your code. When your code is organized into multiple files, you can make changes to individual files without having to dig through a large and complex codebase. This can make it easier to fix bugs and add new features to your program. 

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us