When Bad Code Leaves: Surviving and Refactoring Legacy Nightmares
The article explores how developers inherit poorly written, uncommented code from departing colleagues, illustrates the pain with real Java examples, and offers a step‑by‑step guide to understand, refactor, and prevent such code‑base sabotage.
1. Revenge‑Style Code Scenarios
Imagine walking into the office with a cup of goji‑chrysanthemum tea, only to be pinged by a project manager asking you to fix a problematic order module left by a departing teammate, "Xiao Zhang". The code you inherit is a five‑thousand‑line monster with two half‑written comments, cryptic variable names, and deeply nested if statements.
1.1 Nonsensical Naming
One snippet shows a method doSomething(List list, Map map, int num) where list actually holds order numbers, map stores validation results, and num is a length threshold. Without a comment, you waste half a day deciphering its purpose.
public void doSomething(List list, Map map, int num) {
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
if (obj != null) {
String s = obj.toString();
if (s.length() > num) {
map.put(s, "OK");
} else {
map.put(s, "NO");
}
}
}
}Another fragment uses opaque variables a, b, c to represent start date, end date, and a threshold, turning the code into a cryptic puzzle.
String a = "2025-01-01";
String b = "2025-12-31";
int c = 100;
// ... 200 lines later
if (date.compareTo(a) > 0 && date.compareTo(b) < 0 && count > c) { ... }1.2 Minimal Comments
The module contains only three comment lines such as // loop, // check, // return, offering no insight into business rules. A payment‑callback method even hides a hard‑coded IP behind a comment // this code must not be changed, which later causes environment‑specific failures.
if (data.get("sign").length() == 32 && "2".equals(data.get("status")) && !List.of("4", "6").contains(data.get("type"))) {
// success
handleSuccess(data);
} else {
// fail
handleFail(data);
}1.3 Deep Nesting
A permission‑checking method nests if statements seven levels deep, making it hard to modify when new roles are added. The author humorously notes that medical research shows humans start to feel dizzy after more than three levels of indentation.
public String checkAccess(User user) {
if (user != null) {
if (user.getToken() != null) {
if (user.getToken().length() == 64) {
Long expire = user.getExpire();
if (expire != null) {
if (expire > System.currentTimeMillis()) {
if (user.getRole() != null) {
if (user.getRole().equals("admin")) {
return "ALLOW";
} else if (user.getRole().equals("editor")) {
return "ALLOW";
} else if (user.getRole().equals("viewer")) {
return "ALLOW_VIEW_ONLY";
} else {
return "DENY_ROLE";
}
} else {
return "DENY_NO_ROLE";
}
} else {
return "DENY_EXPIRED";
}
} else {
return "DENY_NO_EXPIRE";
}
} else {
return "DENY_TOKEN_FORMAT";
}
} else {
return "DENY_NO_TOKEN";
}
} else {
return "DENY_NO_USER";
}
}1.4 God‑Object Methods
One 1,200‑line method handles order submission, validation, pricing, notification, logging, and more. Changing a single requirement (e.g., adding a coupon link to an SMS) unintentionally breaks database persistence because the method mixes unrelated responsibilities.
Validate user login
Validate product stock
Calculate discounts
Call payment API
Persist order
Send SMS/email
Log activity
Update inventory
Push WebSocket notification
Generate coupon
A temporary // TODO: remove later block of 200 lines sits inside, turning the method into a “dog‑skin plaster” that is hard to maintain.
1.5 Magic Numbers
Hard‑coded literals like if (order.getStatus() == 3) appear dozens of times with no explanation, forcing developers to guess their meaning. Negative flags such as -1 for failure are also scattered without constants.
if (order.getStatus() == 3) {
// handle status 3
}
if (result == -1) {
// failure
}1.6 Copy‑Paste Mania
The same validation logic is duplicated in ten places. When a requirement changes, developers spend days hunting down every copy, often missing one and introducing bugs.
2. Why Developers Write Such Code
2.1 Rushed Deadlines
Teams often prioritize shipping over quality, leaving no time for proper naming or comments. The author argues that a few extra seconds for meaningful names or a brief comment save hours later.
2.2 “I Know It, So It’s Fine” Attitude
Some developers believe code only needs to be understandable to themselves, ignoring future maintainers. Examples include using pinyin variable names ( yonghuming, dingdanhao) or personal abbreviation systems ( usrOrdPrdRlt).
2.3 Misunderstanding “Good Code”
Prioritizing performance or brevity over readability leads to tricks like bitwise addition ( return (a ^ b) + ((a & b) << 1);) or one‑liner streams that are hard to debug.
3. How to Write Good Code
3.1 Understand Before Changing
Run the code with various inputs to infer behavior, and draw flowcharts for complex logic.
3.2 Split Before Consolidating
Break large methods into small, single‑purpose functions. Use verb‑noun naming (e.g., validateOrder, calculatePrice, saveOrderToDB).
3.3 Enforce Naming and Commenting Standards
Variable names should be descriptive ( orderList, resultMap), not generic ( list1, temp). Method names follow verbNoun pattern. Class names are nouns in PascalCase. Constants are uppercase with underscores.
/**
* Validate order number length
* @param orderNo non‑empty order number
* @param maxLength maximum allowed length
* @return true if valid, false otherwise
*/
public boolean isValidOrderNo(String orderNo, int maxLength) {
return orderNo.length() > maxLength;
}3.4 Reduce Nesting with Guard Clauses
if (user == null) return;
if (!user.isActive()) return;
if (!user.hasPermission()) return;
doSomething();3.5 Replace Chains of if with switch or Collections
switch (status) {
case 1: handlePending(); break;
case 2: handlePaid(); break;
case 3: handleCancelled(); break;
case 4: handleRefunded(); break;
default: handleUnknown();
}3.6 Extract Repeated Logic
Encapsulate token validation, date formatting, etc., into reusable methods or strategy classes.
PaymentProcessor processor = PaymentProcessorFactory.getProcessor(payType);
processor.process(order);4. Preventing Revenge‑Code
Conduct thorough code reviews that focus on naming, comments, and nesting depth.
Adopt and enforce a coding standard using automated tools (Checkstyle, ESLint).
Allocate time each sprint for technical debt reduction and refactoring.
Promote knowledge sharing through regular tech talks and mentorship.
Ensure proper hand‑off when someone leaves: documentation, walkthroughs, and optional cleanup of obvious anti‑patterns.
5. Final Thoughts
Writing comments is not for others but for your future self; clean, well‑named, and well‑structured code prevents colleagues from spending nights cleaning up “revenge” code left behind by departing developers.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
