Fundamentals 14 min read

16 Essential Coding Habits Every Developer Should Master

This article compiles sixteen classic coding habits—from self‑testing changes and validating parameters to handling concurrency, cache consistency, and idempotency—providing practical guidance that helps developers avoid common bugs and improve overall software quality.

macrozheng
macrozheng
macrozheng
16 Essential Coding Habits Every Developer Should Master

Preface

Every good habit is a wealth; this article compiles 16 classic coding habits that can help avoid most non‑business bugs. Adopt them to improve code quality.

1. Self‑test after modifying code

“After changing code, self‑test” is a basic skill for every programmer. Do not assume that changing a single variable or configuration line eliminates the need for testing. Testing your changes can prevent many unnecessary bugs.

2. Validate method parameters

Parameter validation is also a fundamental skill. Your method must first validate parameters, such as nullability and length. Many low‑level bugs stem from missing validation.

If your database field is varchar(16) and a 32‑character string is passed without validation, inserting into the database will cause an exception.

3. Consider compatibility when modifying old interfaces

Many bugs arise from changing public interfaces without maintaining compatibility, which can cause release failures. When extending an existing interface (e.g., a Dubbo service), ensure backward compatibility.

<code>// old interface
void oldService(A, B) {
    // compatible with new interface, pass null for C
    newService(A, B, null);
}

// new interface, keep old one for compatibility
void newService(A, B, C);
</code>

4. Add clear comments for complex logic

While good naming often suffices, complex business logic requires clear comments to aid future maintenance.

5. Close I/O streams after use

Opening too many files or streams can exhaust resources, leading to resource waste. Always close streams, preferably using try‑with‑resources in JDK 7+.

<code>FileInputStream fdIn = null;
try {
    fdIn = new FileInputStream(new File("/jay.txt"));
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
} finally {
    try {
        if (fdIn != null) {
            fdIn.close();
        }
    } catch (IOException e) {
        log.error(e);
    }
}
</code>

JDK 7+ offers the cleaner try‑with‑resources syntax:

<code>try (FileInputStream inputStream = new FileInputStream(new File("jay.txt"))) {
    // use resources
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}
</code>

6. Prevent runtime errors (e.g., array out‑of‑bounds, division by zero)

Take measures to avoid common runtime exceptions such as out‑of‑bounds access or null pointers.

<code>String name = list.get(1).getName(); // may cause IndexOutOfBoundsException
</code>

Correct approach:

<code>if (CollectionsUtil.isNotEmpty(list) && list.size() > 1) {
    String name = list.get(1).getName();
}
</code>

7. Avoid remote or database calls inside loops; batch when possible

Remote or DB operations are resource‑intensive. Prefer batch queries over per‑iteration calls, but avoid fetching excessively large batches.

Good example:

<code>remoteBatchQuery(param);
</code>

Bad example:

<code>for (int i = 0; i < n; i++) {
    remoteSingleQuery(param);
}
</code>

8. Imagine multithreaded execution and check concurrency consistency

Operations that first read then write are not atomic; under concurrent threads they can cause race conditions.

Bad example:

<code>if (isAvailable(ticketId)) {
    // add cash
    deleteTicketById(ticketId);
} else {
    return "No available coupon";
}
</code>

Use atomic DB delete to avoid the issue:

<code>if (deleteAvailableTicketById(ticketId) == 1) {
    // add cash
} else {
    return "No available coupon";
}
</code>

Thread A adds cash

Thread B adds cash

Thread A deletes ticket flag

Thread B deletes ticket flag

9. Null‑check objects before accessing their properties

Even if you expect an object to be non‑null, always verify it to prevent NullPointerExceptions.

<code>if (object != null) {
    String name = object.getName();
}
</code>

10. Prefer proper thread pools over creating new threads; consider pool isolation

Thread pools manage resources, reduce creation overhead, improve response speed, and enable reuse. Different critical business areas should use isolated pools with appropriate configurations.

11. Run and explain SQL statements manually before embedding them

Execute written SQL in the database to catch syntax errors and use EXPLAIN to review execution plans and index usage.

<code>explain select * from user where userid = 10086 or age = 18;
</code>

12. Handle exceptions, timeouts, and retries when calling third‑party APIs

When invoking external services, consider exception handling, set appropriate timeouts, and decide on retry strategies based on business needs.

Simple example: an HTTP request to another service should configure connect‑time and retry count.

For critical operations like payments, also address signature verification and encryption.

13. Ensure interface idempotency

Idempotent interfaces prevent issues from repeated submissions, such as double clicks on a “redeem coupon” button.

Idempotence is a concept from mathematics and computer science. In programming, an idempotent operation yields the same effect no matter how many times it is executed with the same parameters.

Common idempotent solutions include query operations, unique indexes, token mechanisms, delete operations, optimistic/pessimistic locks, and distributed locks (e.g., Redis, Zookeeper).

14. Consider linear safety in multithreaded contexts

Non‑thread‑safe collections like HashMap can cause dead loops under high concurrency; use ConcurrentHashMap or other thread‑safe alternatives.

HashMap, ArrayList, LinkedList, TreeMap are not thread‑safe. Vector, Hashtable, ConcurrentHashMap are thread‑safe.

15. Account for master‑slave replication delay

Writing to the master and immediately reading from the slave may encounter replication lag, causing reads to miss recent writes. Critical business may require forcing reads from the master or redesigning the flow.

16. Ensure cache‑DB consistency and guard against cache penetration, avalanche, and breakdown

Caching speeds up queries, but you must maintain consistency with the database and mitigate issues such as cache penetration (queries for nonexistent data), cache avalanche (mass expiration), and cache breakdown (hot key expiration under heavy load).

JavaPerformancebackend developmentsoftware engineeringcoding habits
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.