What Is Clean Code?

By 쉬었음.com

Clean code is code written not merely to compile and run, but so that other developers can understand its intent and later modify, extend, and verify it safely. It is not a concept defined by one strict international standard or score. Rather, it is a practical term that encompasses quality goals such as readability, understandability, maintainability, consistency, and safety of change. google.github.io

At first, it is easy to think of it as simply “good-looking code.” In practice, however, the important moments come after the code is first written: when fixing a feature, finding a bug, adding a requirement, or reviewing a colleague’s work. Clean code is more about reducing the time and likelihood of mistakes in those moments. Therefore, the key is not memorizing particular syntax techniques, but considering what a reader needs to know and where a change will have an impact.

What exactly does clean code mean?

Software is not a document that is written once and then finished. Existing code is read again when adding an order status, changing a pricing rule, or investigating an error. The reader may be the original developer, but is often another team member or your future self. Clean code refers to a state in which this reader can relatively quickly understand the code’s role, inputs and outputs, important conditions, and likely points of change.

Here, “clean” does not mean only an aesthetic judgment. For example, even well-formatted code is risky to modify if its names are ambiguous, several responsibilities are mixed into one function, and there is no way to verify it. Conversely, code may be better from a maintenance perspective if its role is clear, it fits the team’s conventions, and it has tests that can confirm changes, even if it does not use an especially distinctive style. Code review examines not only style, but also design, functional correctness, complexity, testing, and documentation. google.github.io

The term clean code became widely known through Robert C. Martin’s 2008 book Clean Code. However, the book’s recommendations are situated in the context of particular languages and object-oriented development practices. Rather than applying a book or well-known rule unchanged to every language and program size, it is more appropriate to judge whether it solves a problem in the current codebase and team. www.informit.com

Why is code that runs not enough?

Producing the desired result for current inputs is the most basic requirement of a program. But even correct functionality is difficult to manage in the long term if it breaks easily during the next change. For example, a long function may contain discount calculation, permission checks, display rendering, and data storage. It may work now, but someone trying to change only the discount policy is more likely to affect permission handling or storage order as well.

Hard-to-read code is not merely a matter of taking longer to read. Without confidence about the intent, developers may copy similar logic, modify a broader area than necessary, or recreate rules that already exist. Reviewers also have difficulty judging the impact of a change. Maintainability is the property of not blocking future changes, and clean code focuses on improving that maintainability.

Still, no one can eliminate every future change cost in advance. When requirements themselves are complex or external systems impose strong constraints, the code will be complex to some extent as well. The better goal is not to pretend reality is simple, but to distinguish avoidable complexity from unavoidable complexity. If complexity is necessary, its reason should be made visible through structure, names, tests, and documentation.

How do good names reveal code intent?

Names are the information readers encounter most often when first understanding code. Broad names such as x, data, process, and flag may be familiar to their author, but do not tell others what they represent. Names such as expiredCouponCount, isEligibleForRefund, and calculateShippingFee, by contrast, communicate the purpose of a value or operation relatively directly. Meaningful names are also a way to move information that would otherwise need to be explained in comments into the code itself. google.github.io

Good naming is a matter of specificity, not length. A widely agreed-upon concept in a small scope can have a short name, while a value used in a broader scope may need more context. For example, the loop index i can be understandable within a very short loop. But if a function return value or object field is named only result, it is difficult to tell whether it represents success, an amount, or a query result.

Distinguishing verbs from nouns is also helpful. Reading tends to flow naturally when functions use verb-based names that reveal what they do, while values and objects use noun-based names that reveal what they are. sendReceipt() is an action, while receiptEmail is data. However, lengthening a name does not automatically remove ambiguity. handleUserData is longer, but it is still unclear what it handles.

// Example with unclear intent
if (a) {
  doIt(b);
}

// Example where the purpose of the condition and action is visible
if (isPaymentApproved) {
  sendOrderConfirmation(order);
}

The names in the second example should still be adjusted for the actual context. The point is to let readers understand the important decision without having to search far away for the definitions of a and b. Compared with a structure in which comments repeat what names already explain, making the names and composition of the code explain themselves creates less risk that the explanation will become outdated after a change.

How much should functions and structure be divided?

When a function or module does too many things, readers must hold several rules in their heads at once. If input validation, calculation, external calls, error handling, and result formatting are mixed into one block, changing one part may require understanding the entire flow. Separating related steps into named units can make the high-level flow easier to read.

For example, an order-confirmation process might be shown as steps such as validateOrder, calculateTotal, reserveInventory, and createPayment, which express the business flow. The purpose of separation is not to increase the number of functions, but to make each step’s responsibility and order easier to read. If an extracted function is only one line and its name is less clear than the original expression, it is difficult to conclude that extraction improves understanding.

Excessive splitting creates the opposite problem. Readers may need to keep moving among many files and thin functions to understand one action. Abstractions such as interfaces or types have the advantage of hiding implementation details, but they can also hide needed context. Abstraction should be used when it offers a clear benefit, not applied on the assumption that “more abstraction always means better design.” google.github.io

Whether to split can therefore be judged with questions such as these:

  • Does this part have a role that can be explained independently?
  • Does its name explain the intent better than reading the internal code?
  • Is the same rule repeated in multiple places, giving a reason to gather it in one place?
  • Does it create a boundary where only this part needs to be examined when making a change?
  • After separation, does following calls make the overall flow less clear instead?

These questions do not produce an answer automatically. But they focus attention on the actual cost for readers to understand the code rather than surface rules such as “short functions.”

Is simplicity the same as having fewer features?

In clean code, simplicity does not mean giving up functionality that is needed. It is closer to avoiding unnecessary structures, unused extension points, and hard-to-understand detours that are not required by current requirements. If you generalize based only on guesses about future needs, current readers must understand cases that do not yet exist.

For example, building a multilayer plug-in system in advance for a small feature with only one payment method may create room for future expansion. But it also increases the immediate code paths, configuration, and combinations that must be tested. Conversely, if adding payment methods is already confirmed and their rules differ substantially, creating a common boundary can reduce future changes. Neither choice is always better in advance.

Simplicity also does not mean “the fewest lines of code.” Compressing multiple conditions and transformations into one line may feel clever to the author, but the person modifying it must interpret precedence and exceptions. In contrast, using appropriately named intermediate values and separating conditions can increase line count while making the reasoning process simpler. Code review guidance also emphasizes that future developers should be able to read, understand, and modify the code. google.github.io

In practice, it is useful to consider two kinds of simplicity together. The first is simplicity of the implementation itself: whether there are few unnecessary states, branches, dependencies, and duplications. The second is simplicity of use and change: whether callers can use it correctly with ease and whether the place to modify when rules change is clear. A choice that makes external usage simple can sometimes be better even if the internals are somewhat more complex.

Why is a consistent style necessary, and why is it not enough?

When indentation, line breaks, file organization, and naming conventions all vary, readers have to interpret the format every time. Consistently using a style agreed upon by the team can reduce the attention spent on superficial differences in code. Tools that mechanically check rules, such as automatic formatters and linters, can be especially useful for this repetitive work.

However, following style alone does not make code clean. Even if every name follows the same convention, roles may still be ambiguous; even if line lengths are correct, the design may still be overly tangled. Code quality review takes the view that design, functionality, complexity, testing, and documentation should be considered in addition to style. google.github.io

When applying style rules, respecting the team’s existing conventions is generally practical. Trying a preferred notation in just one new file may seem minor, but it can weaken consistency across the project. Conversely, an existing convention can be discussed and changed if an improvement significantly increases clarity. What matters is not competing over which rule is more elegant, but whether the team can read and change the code consistently.

Code review also requires distinguishing minor preference differences from issues that affect maintainability. Demanding perfection in every change can slow improvement itself. If a change improves maintainability, readability, and understandability overall, accepting it incrementally may be more realistic. google.github.io

What is the relationship between tests and clean code?

Tests are executable means of verification for behavior the code promises. Here, a promise means observable behavior such as “only valid orders are paid,” “an order that has already been canceled is not canceled again,” or “the specified amount is deducted when discount conditions are met.” Tests provide a basis for checking whether critical behavior broke after a change.

If clean code is seen merely as code that looks good, tests can appear separate from it. But under a definition that includes safe modification, tests are central. During structural cleanup, you need to be able to confirm that external behavior was preserved, and when adding a new rule, you need to check that old rules were not accidentally broken. Maintainable code should have tests that verify core logic and promised behavior and help identify the cause of failures. google.github.io

Having many tests alone does not guarantee quality. Tests that are too tightly coupled to minor internal ordering can make even legitimate structural improvements difficult. Conversely, tests that omit important boundary conditions and business rules may not contribute enough to safety of change even if there are many of them. Test names and the arrange-act-assert structure should also be written clearly so readers know what is guaranteed.

For example, if logic calculates a refund eligibility period, it is more meaningful to test the boundaries of the actual rule—such as the deadline date itself, immediately after the deadline, and missing input—rather than checking only ordinary dates. Which cases to test depends on product requirements and risk. The key is to make tests communicate not merely that “code exists,” but “which behavior must continue to be preserved.”

When are comments and documentation necessary?

Comments are not bad. They are especially valuable when conveying background that code has difficulty expressing. For example, names alone may not adequately convey a workaround for abnormal behavior in an external service, legal or contractual constraints, a choice based on performance measurements, or the reason for temporary compatibility code that will be removed after a particular date. This information helps future maintainers understand why they should not replace it with a simpler approach. google.github.io

Conversely, comments that simply translate what the code already says can drift out of sync with the code over time. A comment saying “increment count by 1” next to count = count + 1 adds no new information. In that case, a better name or a more direct structure may take priority. The longer comments become, the more it is worth checking whether they signal unclear code intent.

The appropriate location for documentation can also differ. A local reason inside a function may suit a nearby comment. Usage rules, configuration methods, and compatibility conditions shared across multiple modules may be easier to find in separate documentation or interface descriptions. Wherever it is placed, the important thing is to give readers the context needed to make decisions and update it together when the code changes.

How are clean code, refactoring, and coding style different?

These three terms are often mentioned together, but they have different roles. Clean code is a quality state or perspective aimed at code that is easy to understand and change. Refactoring is the activity of improving internal structure while preserving externally observable behavior. Coding style is a convention for code expression, such as indentation, naming notation, and spacing.

CategoryKey questionScope
Clean codeCan this code be understood and changed safely?Names, structure, complexity, tests, documentation, consistency
RefactoringHow can the structure be improved while preserving behavior?An activity for structural improvement
Coding styleIn what format does the team express code?Conventions for notation and formatting

Refactoring is one way to create or maintain clean code. For example, duplicated price calculations can be gathered in one place, ambiguous names can be changed, and conditions can be organized into easier-to-understand units. But structural changes made without confirming that behavior is preserved can be risky, so tests and review are important.

Style reduces collaboration friction, but it does not automatically solve design problems. Conversely, clear code with a working structure is not automatically bad merely because its style differs slightly. Understanding this distinction reduces the mistake of treating formatting issues and genuine maintenance risks with equal weight in reviews. google.github.io

What should take priority when there are performance and security constraints?

Clean code’s emphasis on simplicity and clarity does not mean sacrificing performance, security, compatibility, or operational reliability. For example, a cache required for performance, validation steps required for security, or compatibility handling for an old external system can make code more complex. If that complexity is based on real requirements and measurement results, it may be more appropriate than an alternative that merely appears simpler.

The important attitude in this situation is not to hide complexity. The constraints, behavior that must be guaranteed, and reasons for not using a conventional implementation can be made visible through names, structure, tests, and necessary comments. The principle of prioritizing technical facts and data over personal preference applies to these decisions. google.github.io

For instance, if an easy-to-read implementation does not meet response requirements in the real production environment, there is a reason to choose a more complex implementation. However, making all code complex based only on the assumption that it is “for performance” is not desirable either. After measuring the problem and confirming requirements, both the costs and benefits of complexity should be compared.

The same applies to security. Steps such as input validation, authorization checks, and error handling can make the flow of code longer. That does not mean they can be omitted to make the code shorter. Good structure places these necessary steps where they are easy to recognize and helps prevent sensitive rules from being scattered arbitrarily across the codebase.

What are common misconceptions about clean code?

First is the misconception that “shorter is always better.” Short functions and concise expressions can help, but line count is not the criterion. Excessive splitting and abstraction can lengthen call paths and hide context. Rather than asking whether the code got shorter, ask whether readers can understand the main flow and its reasons more easily. google.github.io

Second is the misconception that “fewer comments are always better.” The idea of expressing content that code can explain for itself through names and structure does not mean removing useful background information. In particular, reasons for choices and external constraints may need to remain in comments or documentation. Good comments do not repeat the code; they provide context that is difficult to know from the code alone. google.github.io

Third is the misconception that “code is good only if it follows every rule.” Recommendations are tools for judgment, not a legal code that applies to every situation. Priorities vary according to language characteristics, existing project conventions, performance and security requirements, and team experience. It is more important to check whether applying a rule actually makes the code clearer.

Fourth is the misconception that “the design must be perfect from the beginning.” Requirements change, and some information cannot be known initially. Rather than delaying changes while pursuing perfection alone, it is more realistic to continue making small improvements that make the current system easier to read and maintain overall. google.github.io

How can you judge clean code in practice?

It is difficult to judge with an absolute checklist alone, but you can ask several questions when facing a change. First, consider whether someone seeing the code for the first time can explain its main purpose. Next, when changing one rule, check whether the location to modify is relatively clear or whether unrelated areas must also be changed. Finally, confirm whether there are tests or review methods to verify the core behavior after the change.

Here are practical questions to use when writing or reviewing a feature:

  • Can you roughly understand the role of a value, function, or module from its name alone?
  • Does one function unnecessarily mix different business rules or external operations?
  • Is the same important rule copied in multiple places?
  • Does it naturally fit the team’s conventions for naming, formatting, and file organization?
  • Have reasons for choices or constraints that the code cannot express been recorded as needed?
  • Is there a way to verify core behavior and risky boundary conditions?
  • Has simplification overlooked performance, security, or compatibility requirements?
  • Do the abstraction or separation actually reduce the cost of understanding, or do they only lengthen the path readers must follow?

You do not need to answer all of these immediately. Trying to solve every design problem in a small change can bring review to a halt. It is practical to fix high-impact issues first and move the rest in a better direction in subsequent changes. The goal of code review can also be continuous improvement of the system’s maintainability, readability, and understandability rather than producing perfect code. google.github.io

Conclusion: Clean code is quality for change, not a fixed format

Clean code does not mean only a list of rules from a particular book or tidy formatting. It is a quality perspective that makes code intent visible in names and structure, reduces unnecessary complexity, enables consistent reading within a team, and makes it possible to verify behavior after changes. Comments are used to convey background, tests support safety of change, and abstractions are used when they genuinely make understanding and change easier.

The shape of good code can differ from project to project. What matters is not whether it looks short or follows a famous rule, but whether the next developer can understand and change it correctly under current requirements and constraints. Continuously improving small names, conditions, tests, and structures from that perspective is the practical starting point of clean code. google.github.iogoogle.github.io

Frequently asked questions

Can clean code be evaluated with a fixed formula or score?

No. Clean code is not a single international standard or measurement formula; it is a practical quality perspective aimed at improving understandability, maintainability, consistency, and safety of change. The right choices can vary with the project language, team, and operational constraints.

Is code always clean if it is short?

No. Short code can sometimes make intent clearer, but excessive compression, splitting, or abstraction can hide context and execution flow, making code harder to read. The important criterion is not line count, but whether readers can understand the intent and change the code safely.

Does having many comments mean code is high quality?

Not necessarily. Behavior that can be expressed through names and structure is often better explained by the code itself. However, comments are valuable for background that is difficult to infer from code alone, such as reasons for a decision, external constraints, or unavoidable exceptions.

Are clean code and refactoring the same thing?

They are not the same. Clean code refers to a state in which code is understandable and easy to maintain, while refactoring is the activity of improving internal structure while preserving externally observable behavior. Refactoring can therefore be one way to move toward cleaner code.

Do you have to give up clean-code principles when complex code is needed for performance?

No. Complexity that is genuinely required by performance, security, compatibility, or operational conditions may be necessary. Rather than ignoring requirements because a simpler approach looks cleaner, choose complexity based on measurement and technical evidence, and make the reason for it visible.