Fundamentals 24 min read

How to Write Clean, Maintainable Code: Principles and Practical Tips

This article compiles practical guidelines and personal experiences on writing clean, maintainable code, covering principles such as eliminating duplication, separating concerns, unifying abstraction levels, function design, naming, unit testing, and broader software‑engineering concepts to reduce mental load and improve code quality.

ITPUB
ITPUB
ITPUB
How to Write Clean, Maintainable Code: Principles and Practical Tips

Guiding Principles: Eliminate Duplication, Separate Concerns, Unify Abstraction Levels

Good code should be locally clean and have concise core logic. The author summarizes decades of experience into three guiding principles. Duplication—whether exact code, structural, or procedural—should be removed by extracting functions, classes, or automating repetitive tasks.

Eliminate Duplication : Extract common code into shared functions; treat similar structures as opportunities for inheritance, generics, template methods, or higher‑order functions.

Separate Concerns : Keep related code together and unrelated code isolated. Highlight the main flow (e.g., order creation) and move auxiliary logic (permissions, logging) to separate layers such as AOP, interceptors, or filters.

Unify Abstraction Levels : Objects at the same abstraction level should interact directly and collaborate closely. Mixing high‑level intent with low‑level details leads to confusing code.

Typical Function Structure

A well‑designed function resembles a tree with clear levels, each containing 2‑5 steps. Ideally a function should not exceed five lines of code; the author uses a personal threshold of 10 lines for acceptable size. Example: placing an elephant in a fridge involves three clear steps—open the door, put the elephant in, close the door.

Typical function structure diagram
Typical function structure diagram

Isolation and Hiding

Information hiding is a form of abstraction. By exposing only what is necessary and keeping internal details private, code becomes easier to modify without breaking callers. Techniques include limiting public methods in a package and using AOP or interceptors to separate technical utilities from business logic.

Coding Tips

1. Classes

Keep classes small to avoid God‑class syndrome; each class should have a single responsibility (the "S" in SOLID) and a clear, noun‑based name.

Encapsulate fields, expose only what is needed, and prefer private over package or protected visibility to follow the Open‑Closed Principle.

Ensure high cohesion: every field should be used by at least one method; otherwise split the class.

2. Functions

Make functions as small as possible—ideally under 10 lines, never more than 20 unless dealing with low‑level details.

Each function should perform a single task; its body must operate only at its own abstraction level.

Never cross abstraction layers within a function; higher‑level functions call lower‑level ones, never the reverse.

Limit parameters: aim for zero, at most three, to reduce cognitive load.

Avoid side effects; functions should behave like pure black boxes.

Restrict if‑statement nesting to two levels; use early returns, extract nested logic into helper functions, or apply functional constructs like streams.

When intent and implementation diverge, extract the intent into a well‑named helper (e.g., isAdult() instead of raw age comparison).

Follow the “boy‑scout rule”: leave the code cleaner than you found it.

Avoid hard‑coding values; use constants or configuration.

3. Naming and Comments

Names should be clear, unambiguous, and expressive of intent rather than implementation. Use nouns for classes, verbs for actions, and avoid misleading abbreviations. Consistent terminology across a team (DDD) prevents misunderstandings. Good code is self‑documenting; comments are only needed for non‑obvious decisions.

4. Unit Testing

Unit tests are essential for guaranteeing both functional and non‑functional quality. Tests should be isolated (use mocks instead of real dependencies), cover all branches, avoid conditional logic inside the test itself, and be as clean as production code. They act as a safety net for refactoring.

Other Topics

Mental Load and Complexity

Complexity arises from dependencies and obscurity, increasing mental burden. Reducing duplication, separating concerns, and layering help tame complexity.

Layering and Packaging

Proper layering ensures higher layers only call lower layers, preventing circular dependencies. Packages should group cohesive concepts; cross‑package interactions increase coupling.

Design Principles

Familiarity with SOLID, DRY, KISS, YAGNI, and other principles guides the creation of clean, maintainable software.

Non‑Functional Characteristics

Beyond functional correctness, code should aim for operability, robustness, testability, maintainability, usability, and reusability.

"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

unit testingsoftware designclean codenaming conventionsabstractioncoding best practices
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

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.