Fundamentals 12 min read

Understanding Bad Code: Causes, Characteristics, Risks, and Solutions

This article examines why developers produce low‑quality code, describes typical symptoms such as unreadability and excessive nesting, outlines the long‑term costs and risks of bad code, and presents expert‑recommended practices—including code reviews, design documentation, technical debt repayment, and disciplined development processes—to improve code quality.

IT Services Circle
IT Services Circle
IT Services Circle
Understanding Bad Code: Causes, Characteristics, Risks, and Solutions

Martin Fowler once said, "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." This article reflects on that principle and explores why bad code is so common.

1. How Bad Code Is Generated?

Developers often blame urgent requirements for low‑quality code, but the real causes are twofold: a lack of solid theoretical guidance combined with long‑term practice, and insufficient psychological resilience under pressure. Those who write high‑quality code tend to be busier even when not rushing to meet deadlines.

Common reasons for producing bad code include:

Rushing to meet tight project deadlines.

Unawareness of best coding practices.

Carelessness and a desire for shortcuts.

Frequent requirement changes that lead to tangled, bloated code.

Absence of contingency plans for emergencies.

Fundamental issues in requirement management, where managers prioritize speed over quality.

2. What Bad Code Looks Like

Bad code typically ignores future changes and maintenance, resulting in several problems:

Hard to read and understand: Developers spend excessive time deciphering the code before they can modify it.

Example of unreadable code:

system.whenTerminated.onComplete(result => session.close())
implicit val materializer: Materializer = Materializer.matFromSystem(system)
val dataPath = args(0)
var batchSize = 512
if (args.length > 1) { batchSize = Integer.valueOf(args(1)) }
var skip = 0
if (args.length > 2) { skip = Integer.valueOf(args(2)) }
val lines = scala.io.Source.fromFile(dataPath)
try { import session.profile.api._ }
val parallels = Runtime.getRuntime.availableProcessors
val source = Source.fromIterator(() => lines.getLines())
val action = source.map(line => { val number = counter.incrementAndGet(); (number, line) })
.drop(skip).mapAsync(parallels)(tuple => Future(decode(tuple._1, tuple._2)))
.filter(_.isDefined).map(_.get).grouped(batchSize)
.mapAsync(1)(articles => { val max = articles.map(_.line).max; val min = articles.map(_.line).min; println(s"group of articles ${articles.size} [$min, $max]"); Future.sequence(articles.map(a => nonExists(db, a))).map(_.flatten).recover { case err: Exception => println(s"unexpected error $err when check in [$min, $max]") } })

Excessive nesting: Deeply nested if‑else blocks and loops make the logic hard to follow.

Example of overly nested code:

anchors foreach { anchor =>
  if (anchor.hasAttr("href")) {
    val href = anchor.attr("href")
    val next = completeUrl(href)
    if (next.contains(domain) && !roadMap.contains(url) && !next.contains("/email-protection#")) {
      roadMap.add(next)
      if (process(next)) {
        println(s"processed $next")
        Thread.sleep(sleep)
      } else {
        println(s"skip $next")
      }
    } else {
      println(s"skip $next")
    }
  }
}

Poor naming: Using vague names like X, Y, Z makes the code unintelligible to others.

Example of bad naming:

x = "abdon"
y = 314
z = 151
r = f(x).f2(y).f3(z)

Unnecessary comments: Over‑commenting can drown important notes, causing readers to skip them.

Example of excessive comments:

// comment
class Foo {
// run program
public void run() {
// call foo
foo(args);
// call
abc(args);
// check return value
if (exists(efg(c))) { foo(x); } else { // discount
      dazhe(efg(d)); }
}
}

3. The Dangers of Bad Code

While bad code may work in the short term, it becomes a "time bomb" that leads to:

Significant time and monetary cost to fix.

Difficulty or impossibility of adding new features.

Unpredictable failures.

Negative impact on developer morale.

Potential project abandonment.

4. Expert Recommendations to Solve the Problem

Industry expert @宝玉xp suggests several concrete actions:

Implement code reviews with automated testing before merging to the main branch.

Perform thorough system design and documentation before coding.

Regularly repay technical debt: for legacy projects, apply patches only; for actively maintained projects, refactor incrementally with tests.

Adopt a two‑week sprint model: week one for feature development, week two for bug fixing and technical tasks such as debt repayment and exploring new tech stacks.

Establish and evolve best‑practice guidelines after initial architecture design, using them as a reference for all developers and enforcing them through code reviews.

By following these practices, teams can shift from producing low‑quality, “bad” code to delivering clean, maintainable software.

software engineeringcode reviewcode qualitytechnical debtbad code
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.