Code is written once and read hundreds of times. The developer who writes a function is rarely the one who debugs it six months later, extends it for a new requirement, or has to understand its edge cases during a production incident. Every decision made while writing code, the name of a variable, the length of a function, the structure of a class, either makes the next reader’s job easier or harder. Clean code is the discipline of systematically making it easier.
The concept was formalized by Robert C. Martin, “Uncle Bob”, in Clean Code: A Handbook of Agile Software Craftsmanship (2008), which remains the canonical reference on the topic. Martin Fowler’s Refactoring: Improving the Design of Existing Code addresses the complementary problem: what to do when existing code is not clean and needs to be. Together, these two works define the intellectual foundation of clean code practice. This guide covers their key principles with working code examples across the most widely used languages, including RPG and PL/I for developers maintaining enterprise legacy systems.
Clean Code Violations You Can’t See
SMART TS XL finds complexity, duplication, and dead code across COBOL, Java, Python, RPG, and more.
More InfoWhat Is Clean Code?
Clean code is source code that is easy to read, easy to understand, easy to test, and easy to change. The phrase “easy” is doing real work in that definition. Code that a compiler accepts is not necessarily clean code. Code that passes all tests is not necessarily clean code. Code is clean when another developer, someone who did not write it, in a context that was not anticipated when it was written, can understand its intent quickly, modify it confidently, and extend it without unexpected consequences.
Martin Fowler’s definition is the most quoted: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” The measure of clean code is human comprehension, not machine execution.
Clean code is not about aesthetics. It is not about following a particular style guide for its own sake. It is about the structural properties of code that determine how much effort every future change will require. A codebase that violates clean code principles consistently accumulates technical debt, the compounding cost of past shortcuts, until every new feature requires understanding and carefully navigating the existing complexity before adding anything new.
Clean Code Principles: Quick Reference
Robert C. Martin’s clean code principles can be summarized as a set of actionable guidelines. The table below maps each principle to its core rule and the problem it prevents:
| Principle | Core Rule | Problem It Prevents |
|---|---|---|
| Meaningful Names | Names should reveal intent, variables, functions, classes | Cognitive overhead decoding what x, tmp, or obj actually represents |
| Small Functions | Functions do one thing; fit on one screen | Untestable, unreadable monoliths that mix responsibilities |
| Single Responsibility | Each class/module has one reason to change | God Classes that break when any of a dozen unrelated things change |
| DRY (Don’t Repeat Yourself) | Every piece of knowledge has one representation | Bug fixes applied in one copy but not others; logic drift |
| KISS (Keep It Simple) | Prefer the simplest solution that works | Over-engineered code that solves problems nobody has |
| YAGNI (You Aren’t Gonna Need It) | Don’t build features until they are needed | Dead speculative code that adds complexity with no benefit |
| Open/Closed Principle | Open for extension, closed for modification | Code that breaks existing behavior when adding new behavior |
| Separation of Concerns | Different responsibilities in different places | Tangled code where changing one thing breaks unrelated things |
| Avoid Comments for What; Use Them for Why | Code explains what; comments explain why | Outdated comments that mislead more than they inform |
| Boy Scout Rule | Leave the code cleaner than you found it | Gradual quality decay through incremental neglect |
The Core Clean Code Principles Explained
DRY, Don’t Repeat Yourself
DRY is the principle that every piece of knowledge must have a single, unambiguous, authoritative representation within a system. When the same logic appears in multiple places, those representations inevitably diverge. A business rule change becomes a search-and-replace across multiple files, and missing one copy produces a bug.
DRY applies to more than copy-pasted code. It applies to configuration, documentation, and data schemas. If the same information must be maintained in two places, you have violated DRY regardless of whether any code was literally copied.
KISS, Keep It Simple
KISS argues that systems work best when they are kept simple, and that simplicity should be a primary design goal. Complexity is not a sign of sophistication, it is a sign that something could have been expressed more clearly.
The practical implication: when two solutions solve the same problem, prefer the simpler one. The more complex solution may handle edge cases you haven’t encountered yet. It will definitely make the code harder to understand for everyone who encounters it.
YAGNI, You Aren’t Gonna Need It
YAGNI discourages adding functionality until it is needed. It is a response to the common impulse to build flexible, extensible systems for anticipated future requirements that never materialize. Every abstraction, interface, and configuration option that exists for a hypothetical future use case is cognitive overhead for every developer who reads the code today.
Meaningful Names
Names are the primary communication mechanism in source code. A function named process() communicates nothing about what it processes, when, or what it returns. A function named calculateMonthlyInterest() communicates precisely.
The key tests for a name: Can you tell what it represents without reading its implementation? Does the name reveal its purpose, parameters, and return value? Would you need a comment to explain what it does?
Small Functions
Robert C. Martin’s rule of thumb: functions should be small, smaller than you think necessary. A function that does one thing can be named clearly, tested in isolation, and understood without reading its implementation. A function that does several things requires understanding all of them simultaneously.
The single responsibility principle applied to functions: a function should do one thing, do it well, and do it only. If you need the word “and” to describe what a function does, it probably does too much.
Clean Code in Java
Java’s verbosity makes clean code discipline especially important. The boilerplate that the language requires can hide the intent of the code if not managed carefully.
java
// Before: unclear names, mixed responsibilities, magic numbers
public double calc(int x, int y) {
double r = 0;
if (y > 1000) {
r = x * y * 0.1;
} else {
r = x * y * 0.05;
}
return r;
}
// After: meaningful names, single responsibility, named constants
private static final double PREMIUM_DISCOUNT_RATE = 0.10;
private static final double STANDARD_DISCOUNT_RATE = 0.05;
private static final int PREMIUM_THRESHOLD = 1000;
public double calculateDiscount(int quantity, int unitPrice) {
double subtotal = quantity * unitPrice;
return isPremiumOrder(unitPrice)
? subtotal * PREMIUM_DISCOUNT_RATE
: subtotal * STANDARD_DISCOUNT_RATE;
}
private boolean isPremiumOrder(int unitPrice) {
return unitPrice > PREMIUM_THRESHOLD;
}
Key clean code practices for Java: favor composition over inheritance, use streams for data processing instead of verbose loops, extract magic numbers to named constants, and keep classes focused on a single responsibility. Robert C. Martin’s clean code principles for Java include the rule that classes should be small, measured not in lines but in responsibilities.
java
// Clean Java: streams over imperative loops
List<String> activeUserEmails = users.stream()
.filter(User::isActive)
.map(User::getEmail)
.collect(Collectors.toList());
Clean Code in Python
Python’s design philosophy, explicit is better than implicit, simple is better than complex, aligns naturally with clean code principles. PEP 8 is Python’s official style guide and the baseline for clean Python code.
python
# Before: vague names, long function, no separation
def do_stuff(d):
res = []
for i in d:
if i['a'] > 18:
res.append(i['n'].upper())
return res
# After: meaningful names, separated concerns, Pythonic style
def get_adult_names_uppercase(users: list[dict]) -> list[str]:
return [
user["name"].upper()
for user in users
if user["age"] > 18
]
python
# Clean Python: context managers for resource handling
# Before: manual, error-prone
f = open("data.txt")
data = f.read()
f.close()
# After: guaranteed cleanup, self-documenting intent
with open("data.txt") as f:
data = f.read()
Pythonic clean code uses list comprehensions over manual loops for simple transformations, type hints for self-documenting function signatures, context managers for resource management, and dataclasses or named tuples instead of bare dictionaries for structured data.
Clean Code in JavaScript and TypeScript
JavaScript’s flexibility is both its strength and its primary clean code challenge. Without discipline, JavaScript codebases accumulate inconsistent patterns, implicit type coercions, and tangled callback chains.
javascript
// Before: var, callback hell, no error handling
function getUser(id, cb) {
db.query('SELECT * FROM users WHERE id = ' + id, function(err, rows) {
if (err) cb(err);
cb(null, rows[0]);
});
}
// After: async/await, parameterized query, proper error handling
async function getUserById(userId: number): Promise<User | null> {
const [rows] = await db.execute(
'SELECT * FROM users WHERE id = ?',
[userId]
);
return rows[0] ?? null;
}
typescript
// Clean TypeScript: explicit types replace implicit any
// Before
function process(data) {
return data.map(x => x.v * 2);
}
// After
interface DataPoint {
value: number;
label: string;
}
function doubleValues(dataPoints: DataPoint[]): number[] {
return dataPoints.map(point => point.value * 2);
}
Clean JavaScript uses const by default, let when rebinding is necessary, and never var. Pure functions, same input always produces same output, no side effects, are the cleanest building block for JavaScript logic.
Clean Code in C#
C# offers powerful features for writing clean, expressive code. The language’s evolution, LINQ, records, pattern matching, nullable reference types, consistently moves toward more declarative, readable syntax.
csharp
// Before: magic numbers, verbose loop, mutable state
public double CalculateTotal(List<OrderItem> items)
{
double total = 0;
foreach (var item in items)
{
if (item.Quantity > 10)
total += item.UnitPrice * item.Quantity * 0.9;
else
total += item.UnitPrice * item.Quantity;
}
return total;
}
// After: named constant, LINQ, single expression
private const double BulkDiscountRate = 0.9;
private const int BulkDiscountThreshold = 10;
public double CalculateTotal(IEnumerable<OrderItem> items) =>
items.Sum(item => item.Quantity > BulkDiscountThreshold
? item.UnitPrice * item.Quantity * BulkDiscountRate
: item.UnitPrice * item.Quantity);
C# clean code principles: use properties instead of public fields for encapsulation, leverage LINQ for declarative data operations, use records for immutable data structures, prefer interfaces over concrete types in method signatures, and use nullable reference types (string? vs string) to make null safety explicit.
Clean Code in Kotlin
Kotlin’s design specifically reduces the boilerplate that makes Java hard to keep clean, while adding features, data classes, extension functions, null safety, that support clean code naturally.
kotlin
// Before: verbose Java-style Kotlin
class User {
var name: String = ""
var email: String = ""
var age: Int = 0
}
fun processUsers(users: List<User>): List<String> {
val result = mutableListOf<String>()
for (user in users) {
if (user.age >= 18) {
result.add(user.email)
}
}
return result
}
// After: idiomatic clean Kotlin
data class User(val name: String, val email: String, val age: Int)
fun getAdultEmails(users: List<User>): List<String> =
users.filter { it.age >= 18 }.map { it.email }
Kotlin’s data class provides equals, hashCode, copy, and toString automatically, eliminating the boilerplate that makes Java data classes verbose and error-prone. Extension functions let you add clean utility methods to existing classes without inheritance.
Clean Code in RPG and PL/I: Legacy Languages, Modern Principles
Clean code principles apply to every language, including the enterprise languages that run financial systems, insurance platforms, and government applications worldwide. RPG (Report Program Generator) and PL/I are still actively maintained in many organizations, and the same discipline that makes Java or Python clean makes RPG and PL/I sustainable.
Clean code in RPG (ILE RPG):
rpg
// Before: cryptic two-character names, magic numbers
C EVAL D = Q * P * 1.05
C IF Q > 100
C EVAL D = Q * P * 0.95
C ENDIF
// After: meaningful names, named constants, clear intent
/free
dcl-c BULK_DISCOUNT_THRESHOLD 100;
dcl-c BULK_DISCOUNT_RATE 0.95;
dcl-c STANDARD_RATE 1.05;
dcl-proc CalculateOrderTotal;
dcl-pi *N packed(15:2);
quantity packed(7:0) value;
unitPrice packed(9:2) value;
end-pi;
if quantity > BULK_DISCOUNT_THRESHOLD;
return quantity * unitPrice * BULK_DISCOUNT_RATE;
else;
return quantity * unitPrice * STANDARD_RATE;
endif;
end-proc;
/end-free
Key clean RPG practices: use ILE RPG’s free-format (/free) syntax rather than fixed-format for readability, name procedures descriptively, replace hard-coded values with named constants using dcl-c, and decompose long programs into focused procedures using dcl-proc.
Clean code in PL/I:
pli
/* Before: single-letter names, no structure */
CALC: PROC(X, Y) RETURNS(FLOAT);
DCL (X, Y, R) FLOAT;
IF Y > 1000 THEN R = X * Y * 0.1;
ELSE R = X * Y * 0.05;
RETURN(R);
END CALC;
/* After: meaningful names, named constants, clear intent */
DCL PREMIUM_THRESHOLD FIXED DECIMAL(7) INIT(1000);
DCL PREMIUM_RATE FLOAT INIT(0.10);
DCL STANDARD_RATE FLOAT INIT(0.05);
CALCULATE_DISCOUNT: PROC(QUANTITY, UNIT_PRICE) RETURNS(FLOAT);
DCL (QUANTITY, UNIT_PRICE) FLOAT;
DCL SUBTOTAL FLOAT;
SUBTOTAL = QUANTITY * UNIT_PRICE;
IF UNIT_PRICE > PREMIUM_THRESHOLD
THEN RETURN(SUBTOTAL * PREMIUM_RATE);
ELSE RETURN(SUBTOTAL * STANDARD_RATE);
END CALCULATE_DISCOUNT;
PL/I clean code principles mirror those of modern languages: meaningful identifiers (PL/I’s 31-character limit is sufficient for descriptive names), named constants instead of magic numbers, procedures focused on a single task, and explicit error handling using the ON condition system.
Clean Code Tools and Static Analysis
Applying clean code principles manually through code review is necessary but not sufficient at scale. Static analysis tools enforce clean code metrics automatically, flagging violations before they merge.
| Tool | Languages | What It Enforces |
|---|---|---|
| SonarQube / SonarCloud | 30+ languages | Complexity, duplication, code smells, security |
| Checkstyle | Java | Naming conventions, formatting, structure |
| PMD | Java, Apex | Duplicate code, unused variables, complexity |
| ESLint + typescript-eslint | JavaScript, TypeScript | Style, complexity, unused code, async patterns |
| Pylint + Radon | Python | PEP 8, complexity, maintainability index |
| ReSharper | C# | Code style, redundant code, refactoring suggestions |
| Clippy | Rust | Idiomatic patterns, common mistakes |
| SMART TS XL | COBOL, RPG, PL/I, Java, Python, and more | Cross-language complexity, duplication, dead code |
The most common clean code metrics that tools measure: cyclomatic complexity (number of decision branches, above 10 is a warning, above 20 is critical), cognitive complexity (how difficult code is to understand, a SonarQube-specific metric that improves on cyclomatic complexity for readability measurement), and duplication rate (percentage of code that is duplicated, above 3% warrants attention).
How SMART TS XL Enforces Clean Code Across Enterprise Codebases
The clean code tools listed above operate within a single language. In enterprise environments where Java services, Python pipelines, COBOL batch programs, RPG modules, and JCL job streams coexist, each language’s clean code violations need to be measured simultaneously, and the relationships between components across languages matter as much as the quality within any single file.
SMART TS XL’s static code analysis applies clean code metrics across every language in the environment simultaneously. Cyclomatic complexity, duplication rates, dead code identification, and structural coupling metrics are calculated for COBOL programs using the same methodology as for Java classes, producing comparable, unified quality measurements across the full application portfolio.
The impact analysis capability makes clean code violations actionable at the architectural scale: when a COBOL paragraph has high coupling to dozens of other programs, the impact analysis shows exactly which components are affected before any refactoring begins. This transforms the refactoring paralysis that teams experience in large codebases into a structured remediation program with defined scope.
The application dependency mapping identifies the architectural violations of clean code at the system level: which components have become God Classes at the enterprise scale, where circular dependencies violate separation of concerns across language boundaries, and where the same business logic has been independently implemented in multiple systems without either copy being aware of the other. This cross-language DRY violation, the same business rule maintained separately in a COBOL program, a Java service, and a Python pipeline, is the most expensive category of clean code violation in enterprise systems, and it is invisible to any single-language tool.
For teams applying clean code principles during legacy modernization programs, SMART TS XL provides the pre-refactoring analysis that makes the work tractable: dead code excluded from scope before conversion begins, highest-complexity components identified for prioritized attention, and the full dependency graph available before any change is made.
Clean Code Is a Team Discipline, Not an Individual Practice
The principles in this guide are easier to state than to sustain. Any individual developer can write a clean function in isolation. The challenge is maintaining clean code across a team, across time, and across a codebase that grows while its contributors change. That requires three things: shared standards that everyone knows and has agreed to, tooling that enforces those standards automatically, and a culture of incremental improvement, the Boy Scout Rule applied consistently, leaving every part of the code slightly cleaner than it was found.
Robert C. Martin’s framing remains the most useful: “Clean code always looks like it was written by someone who cares.” The evidence that someone cared is not the absence of bugs, it is the presence of readable names, focused functions, clear structure, and an absence of surprises. That evidence is what makes a codebase worth working in, worth contributing to, and worth maintaining across the years and team changes that any successful system will accumulate.