How to Write Clean, Readable Code: Practical Tips from the Classics
This article distills essential clean‑code practices—covering comments, naming, method design, error handling, concurrency, unit testing, structure, and design—drawn from classic books and real‑world engineering experience to help developers produce elegant, maintainable software.
Abstract: Any fool can write code that a computer can understand; good programmers write code that humans can understand. Ordinary engineers pile code, excellent engineers write elegant code, and outstanding engineers simplify code.
Writing elegant, clean, and understandable code is a discipline and a crucial part of software engineering practice. The author recommends three classic books—"Clean Code", "The Art of Readable Code", and "Refactoring"—and summarizes best practices across comments, naming, methods, exceptions, unit testing, concurrency, structure, and design.
1. Comments
Do not add comments to bad names; a good name is more important than a comment.
Avoid "crutch comments"; good code > bad code + good comments.
Use file‑ or class‑level comments to explain how all parts work.
Always comment constants.
Team‑wide markers: TODO (pending issues), FIXME (known problems), HACK (necessary rough solutions).
Illustrate comments with carefully chosen input‑output examples.
Comments should state the high‑level intent, not obvious details.
Do not embed copyright information in code; let Git handle it.
HTML comments in source code increase reading difficulty.
Comments must describe the code they are closest to and correspond to it.
Public APIs need comments; use them sparingly elsewhere.
Typical bad comments: irrelevant information, obsolete comments, redundant comments, terrible comments, commented‑out code.
The only truly good comment is the one you manage to avoid writing.
Avoid boilerplate comments for setters/getters and log‑style comments (e.g., modification timestamps).
Comments should express information that cannot be conveyed by the code itself.
If a comment is necessary, explain the *why* (intent), not the *how* (implementation).
Add warning comments when appropriate.
2. Naming
Prefer standard naming conventions, such as design‑pattern terms and widely accepted academic terminology.
Choose expressive words: use verbs like fetch or download instead of generic get.
Avoid vague names like tmp.
Use specific names that describe the entity in detail.
Include important details in variable names, e.g., units like ms.
Longer names for wide‑scope variables; short names for narrow‑scope variables.
Boolean variables should be prefixed with is, has, can, or should for clarity.
Variable name length should match its scope.
Do not fear long, descriptive names; they are better than short, cryptic ones.
Function names should reveal side effects and fully describe their purpose; avoid hiding effects (e.g., CreateAndReturnXXX).
3. Methods
Functions should not exceed about 100 lines; ideally cap at 20 lines.
Control statements (if, else, while, etc.) should contain a single line—a function call.
Function nesting should not exceed two levels.
A function should do one thing and not abstract another function.
Public functions should be followed by their private helper functions.
Ideal parameters: zero; at most three; avoid output parameters.
If a function requires three or more parameters, consider extracting a class.
Avoid passing boolean flags that control multiple behaviors; split into separate functions.
Do not return null; throw exceptions or return special objects to avoid NPEs.
Never pass null as an argument.
4. Exceptions and Errors
Extract the code inside try‑catch blocks into separate functions.
Each thrown exception should provide sufficient context to identify its source.
Do not attribute system errors to random events.
5. Concurrency
Separate concurrent code from other code.
Strictly limit access to potentially shared data.
Avoid using multiple synchronized methods on the same shared object.
Keep synchronized regions small; minimize critical sections.
6. Unit Testing
Do not fear long or verbose test method names; they act as documentation.
Do not chase excessively high coverage; the first 90% yields most value.
Use the simplest inputs that fully exercise the code.
Name test functions descriptively, e.g., Test_....
Test code is as important as production code.
If test code is not clean, it will be quickly abandoned.
Each test should contain a single assertion; minimize assertions per test.
Follow the FIRST principles: Fast, Independent, Repeatable, Self‑Validating, Timely (TDD).
7. Code Structure
Limit line length to 100–120 characters.
Prefer files of 200–500 lines for a well‑structured system.
Place closely related code near each other.
Declare variables close to their usage.
Place callers above callees; keep call order logical.
Show function call dependencies top‑down.
Extract functions that explain conditional intent; express conditions positively.
Do not inherit constants; avoid using interfaces to define constants.
Modules should not know internal details of the objects they manipulate.
DTOs are classes with only public fields and no behavior.
Objects should expose behavior, hide data.
Avoid “Yoda conditions” like if (null == obj); modern compilers warn on such patterns.
Prefer if‑else; use ternary operators for simple statements.
Early returns reduce nesting and improve readability.
8. Design
Classes should be small and adhere to the Single Responsibility Principle (SRP).
Classes should have few instance variables.
Follow the Dependency Inversion Principle (DIP); depend on abstractions, not concrete details.
Minimize the number of methods in a class; each method should know as few variables as possible.
Reduce variable count and keep them lightweight for better readability.
Prefer immutable or constant variables when possible.
The best readable code is code that can be eliminated; remove unnecessary features and avoid over‑design.
Regularly read the standard library API to stay familiar.
Simple design: run all tests, avoid duplication, express programmer intent, minimize classes and methods, prioritize rules by importance.
When designing systems or modules, start with the simplest workable solution.
Clean code provides a single way to accomplish a task with minimal dependencies and a well‑defined, small API.
Reduce duplicate code, increase expressiveness, build early, and abstract simply.
9. Summary
This first article in the "Clean Code" series offers concise best‑practice recommendations on comments, naming, methods, unit testing, concurrency, and more; future posts will provide concrete examples for each topic. Every excellent engineer seeks code excellence—what are your suggestions for maintaining clean, consistent code in large‑scale collaborations?
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
