Basic principles and practices of clean code development

Cult developer Kent Beck formulated principles for writing software, which, loosely stated, sound something like this:

1. Make the code work.
2. Make it clear.
3. Optimize for better performance.

These basics are decades old, but juniors (and sometimes even older ones, let’s be honest:) stumble already at the second step. The result is working but unreadable code that is not only difficult to understand, it cannot be expanded or corrected without breaking the program.

The reason is simple: in project work it is important to get to the market and get money as quickly as possible. Junes rush to figure out the problem without wasting time on refactoring and comments.

Usually things never get to the point of optimization: important sections of code are more likely to be “frozen” and prohibited from making changes to them than to be rewritten. It will be a disaster if it is precisely in them that the economic advantage of the system lies.

Does clean code exist?

Lyrical digression

Programming exam question: “What is Clean Code?”

The student suffers, puffs: “Well, there are methods without… uh… names… Sorry, professor, I knew it, but I forgot!”

The professor stands up and solemnly announces to the audience: “Friends, a tragedy! The only person knew about Clean Code – and he forgot!”

There is no clear-cut theory of clean code: the authors of topical books rely on subjective experience and sometimes contradict each other. In the end, it all depends on the tasks the code performs. However, we can identify a few effective principles that apply in most cases.

Principles of writing clean code

1. Use meaningful names. The purpose of variables, functions, classes and other identifiers should be clear from their names.

Example (Java):

public void assign(Author author, Book book)
{
  book.setAuthor(author);
  book.save();
}

It is impossible to determine what happens in it by the name of the method. This is bad. Let's specify the names, and the code will explain itself:

public void assignAuthorForBook(Author author, Book book) 
{
  book.setAuthor(author);
  book.save();
}

2. Leave comments in difficult areas. Let them be infrequent, but meaningful. Ideal code can be understood without additional documentation.

Example (Dart):

Comments can connect the code to reality and provide an explanation for why we decided to code the way we did.

///! category && product открывают одинаковый экран
/// Это нужно для обработки диплинков
/// Так как на вебе в url может быть либо products, либо category
GoRoute(
  path: CatalogueRoutes.productsItem.path,
  name: CatalogueRoutes.productsItem.path,
  builder: (context, state) => CategoryPage(
    key: state.pageKey,
    slug: state.pathParameters['slug']!,
    args: state.extra as CategoryArgsModel?,
  ),
),
GoRoute(
  path: CatalogueRoutes.categoryItem.path,
    name: CatalogueRoutes.categoryItem.path,
    builder: (context, state) => CategoryPage(
      key: state.pageKey,
      slug: state.pathParameters['slug']!,
      args: state.extra as CategoryArgsModel?,
  ),
),

3. Bring beauty to empty spaces. Spaces, tabs, and indents help you divide your code into logical sections and make it easier to read.

Example (Javascript):

function filterPermissions(permissions, searchQuery) {
const q = searchQuery.trim().toLowerCase()
if (!q) return permissions
const searchDeep = (permission) => filterTree(permission, ({title}) => title.toLowerCase().includes(q))
return permissions.map(searchDeep).filter((v) => v)
}

You can only use these bricks to build walls. Let's add indents, build a ladder, and our weighty block turns into beautiful, readable code:

function filterPermissions(permissions, searchQuery) {
    const q = searchQuery.trim().toLowerCase()

    if (!q) {
        return permissions
    }

    const searchDeep = (permission) => filterTree(
      permission,
      ({title}) => title.toLowerCase().includes(q)
    )

    return permissions
        .map(searchDeep)
        .filter((v) => v)
}

4. Throw away the unnecessary. After refactoring, immediately remove areas that are no longer of practical value. Don’t leave it in case it “suddenly comes in handy” – a deleted piece can always be found in the Git history.

Example (go):

//test_banner := model.Banner{ID: 1, FeatureId: 45, TagIds: []int32{5, 12, 244}}
  if !banner.IsActive {
    log.Warn("attempt to get inactive banner")
    //return model.Banner{ID: -1, FeatureId: -1, TagIds: []int32{}, content {"forbidden! - empty"}}
    return nil, fmt.Errorf("banner {id:%d, featureId:%d, tagIds:%v} is inactive",
    banner.ID, banner.FeatureId, banner.TagIds)
  }

Test lines in comments clog up the code and interfere with normal reading. They must be demolished mercilessly.

Let's sum it up

Clean code isn't just “easy to read.” First of all, we are talking about competent system design, which gives us much greater advantages:

— The code is easy to expand without technical costs. The entities are not too closely related.

— The code is stable and predictable. If a problem arises, we already know where to look.

PS

In practice, code cleaning should be approached wisely. Remember that every rule has its exceptions. Principles can be a good guide, but they are not the basis for the success of your product.

Our sources

For this article we selected advice from books Robert Cecil Martin, Steve McConnell And Kenta Beckconsulted with practicing programmers and drew inspiration from the channel ExtremeCode. To consolidate the information, read a summary of Uncle Bob's writings and similar material on clean code principleswhere you can see even more examples.

By the way, how do you like the material? If you want more of these, write in the comments, we will be pleased 🙂

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *