← Back to list

Untangling Java, Part 2: How to Refactor Spaghetti Code (Step by Step)

Tired of Java files that look like a plate of pasta? Learn how to clean up your code without writing a single class.

Sourojit Das in Javarevisited · 2025-08-22 11:30 · 94 claps · 3.2 min read
#java #clean-code #spaghetticode #refactoring #software-development
Open on Medium ↗
Wiki topics: 💻 · Programming

Untangling Java, Part 2: How to Refactor Spaghetti Code (Step by Step)

Photo by Stephen Bedase on Unsplash

Photo by Stephen Bedase on Unsplash

TL;DR: If your main() method is doing all the work, you’re one feature away from chaos. In this beginner-friendly walkthrough, we break a messy Java login script into reusable, easy-to-read pieces—no frameworks or OOP required (yet).

🍝 Quick Recap from Part 1

In Part 1, we met a working Java login app that was tangled, repetitive, and hard to grow.

Here’s the spaghetti:

// SpaghettiLogin.java
public class SpaghettiLogin {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String user, pass;

        System.out.println("Enter username:");
        user = scanner.nextLine();

        if (user.equals("admin")) {
            System.out.println("Enter password:");
            pass = scanner.nextLine();

            if (pass.equals("1234")) {
                System.out.println("Access granted.");
                System.out.println("Enter new username:");
                user = scanner.nextLine();

                if (!user.isEmpty()) {
                    System.out.println("Username updated to: " + user);
                } else {
                    System.out.println("Username can't be empty.");
                }
            } else {
                System.out.println("Wrong password.");
            }
        } else {
            System.out.println("User not found.");
        }
    }
}

It runs. But it’s:

  • Hard to test
  • Annoying to modify
  • A nightmare to debug once it grows

Today, we clean it up.

🧼 The Refactoring Plan

You don’t need frameworks, advanced Java, or design patterns to write cleaner code. We’ll start with a few small wins:

What we’re aiming for:

  • Smaller methods
  • Meaningful names
  • Less nesting
  • Logic that’s easier to reuse, test, and read

🪓 Step-by-Step Refactor

🔹 Step 1: Extract Input & Output

We’re repeating System.out.println() and scanner.nextLine() all over the place. Let’s fix that.

public static String getInput(Scanner scanner, String prompt) {
    System.out.println(prompt);
    return scanner.nextLine();
}

public static void print(String message) {
    System.out.println(message);
}

Why it matters: Cleaner code. Easier to change later. Want to log to a file or sanitize input? Now you only change it once.

🔹 Step 2: Handle Login Logic Separately

We’ll move the username and password checks into tiny, purpose-driven methods:

public static boolean isValidUser(String username) {
    return username.equals("admin");
}

public static boolean isValidPassword(String password) {
    return password.equals("1234");
}

Why it matters: Easy to test. Clear to read. If you want to switch to database validation later, you only update one place.

🔹 Step 3: Make Username Update Its Own Thing

This chunk was buried deep in the main() method before. Now, it’s isolated:

public static void updateUsername(Scanner scanner) {
    String newUsername = getInput(scanner, "Enter new username:");

    if (!newUsername.isEmpty()) {
        print("Username updated to: " + newUsername);
    } else {
        print("Username can't be empty.");
    }
}

Why it matters: Logic is reusable and testable. It also avoids overwriting the login username by mistake.

💻 The Clean Version: CleanLogin.java

Here’s the cleaned-up, fully refactored version:

import java.util.Scanner;

public class CleanLogin {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // Clear and readable — first action: prompt for username
        String username = getInput(scanner, "Enter username:");

        // Separated logic — checks are done using clean helper methods
        if (isValidUser(username)) {
            String password = getInput(scanner, "Enter password:");

            if (isValidPassword(password)) {
                print("Access granted.");

                // Update logic is isolated — no more deep nesting
                updateUsername(scanner);
            } else {
                print("Wrong password.");
            }
        } else {
            print("User not found.");
        }

        scanner.close();
    }

    // 🧹 Extracted method: handles prompting and reading input
    // No repeated Scanner or println logic inside main()
    public static String getInput(Scanner scanner, String prompt) {
        System.out.println(prompt);
        return scanner.nextLine();
    }

    // 🧹 Extracted method: handles printing
    // If we wanted to log instead of print, change it here once
    public static void print(String message) {
        System.out.println(message);
    }

    // 🧠 Separated validation logic
    // No hardcoded checks buried in if-else blocks anymore
    public static boolean isValidUser(String username) {
        return username.equals("admin");
    }

    // 🧠 Also testable and self-explanatory — follows same structure as above
    public static boolean isValidPassword(String password) {
        return password.equals("1234");
    }

    // ✨ All logic related to username update is now encapsulated here
    // Clear responsibilities = no spaghetti
    public static void updateUsername(Scanner scanner) {
        String newUsername = getInput(scanner, "Enter new username:");

        if (!newUsername.isEmpty()) {
            print("Username updated to: " + newUsername);
        } else {
            print("Username can't be empty.");
        }
    }
}

🔍 What Improved?

Let’s compare:

[embed]

💡 What You Learn Here

  • You don’t need OOP or frameworks to write cleaner Java.
  • Breaking code into smaller, named methods = clarity.
  • Your code is for humans first, machines second.

🔜 Coming Up in Part 3: Java Meets OOP

Next time, we’ll introduce a basic User class, and see how even a little object-oriented structure can help manage state, encapsulate logic, and future-proof your code.

We’ll walk through:

  • Creating a User class
  • Moving validation into it
  • Connecting it with the logic we just refactored

🏁 Final Thought

Bad code runs. Good code survives change.

Clean code doesn’t mean writing fewer bugs — it means writing code that’s easier to fix, explain, and grow. That’s how you level up, one refactor at a time.


메타데이터
post_id
4d6990fa27bf
slug
untangling-java-part-2-how-to-refactor-spaghetti-code-step-by-step-4d6990fa27bf
url
https://medium.com/javarevisited/untangling-java-part-2-how-to-refactor-spaghetti-code-step-by-step-4d6990fa27bf
canonical_url
https://medium.com/javarevisited/untangling-java-part-2-how-to-refactor-spaghetti-code-step-by-step-4d6990fa27bf
author_url
https://medium.com/@sourojitdas
status
ok
fetched_at
2026-06-09 15:37:30