Clean Code

Philosophy and Culture for Devel Team

By José Roniérison

What is a clean code?

"I could list all of the qualities that I notice in clean code, but there is one overarching quality that leads to all of them. Clean code always looks like it was written by someone who cares. There is nothing obvious that you can do to make it better. All of those things were thought about by the code’s author, and if you try to imagine improvements, you’re led back to where you are, sitting in appreciation of the code someone left for you—code left by someone who cares deeply about the craf."

  • Michael Feathers

"Clean code can be read, and enhanced by a developer other than its original author. It has unit and acceptance tests. It has meaningful names. It provides one way rather than many ways for doing one thing. It has minimal dependencies, which are explicitly defined, and provides a clear and minimal API. Code should be literate since depending on the language, not all necessary information can be expressed clearly in code alone."

  • Dave Thomas

"Runs all the tests, contains no duplication, expresses all the design ideas that are in the system, minimizes the number of entities such as classes, methods, functions, and the like."

  • Ron Jeffries

"You know you are working on clean code when each routine you read turns out to be pretty much what you expected. You can call it beautiful code when the code also makes it look like the language was made for the problem."

  • Ward Cunningham

The importance of a Clean Code

What a dirty code can do with a project/company

  • Is more difficult to fix a bug
  • New implementations are more slow (ridgity)
  • Increases the code cost
  • Increases the infrastructure cost

Can a dirty code crash a company or a project?

  • Ariana5
  • Cost

The code tends to chaos

When no one organizes the code, each hotfix tends to increase the entropy of code ..

So we should refactor all the time

The worst enemy of clean code is duplication

  • Write the code in right place
  • Refactor your code to eliminate code duplication

But what is a developer who cares?

Variables

The name of variables, function, classes, folders and namespaces should answer all the big questions. It should tell you why it exists, what it does and how it is used. If name require a commnet, then the name does not reveal its intent.

Must describe what contains and reveal its intention

This allows you understand the code better

Must describe the data type

So you can use the data methods as map, gsub, lenght, and so on.

Must make sense in the context which is inserted

The understanding of a name depends of the class, namespace or folder where it is placed.

Avoid disinformation

Don't use variables with name as aux, opt, sys, xopt, and so on.

Make meaningful distinctions

Is a bad pratice create two variables with name UserInfo and UserData

Use pronunceable names

When you talk about code with others developers you should pronounce the variable names.

Use searchable names

Often you need to search for a variable to understand the code, fix a bug, and so on.

Don't care if you waste a few time choosing a good name

Good names avoid mistakes

Class

Class name should have noun or a noun phrase

Avoid verbs in class name.

S.O.L.I.D

Lets talk about it

SRP (Single-responsiblity principle)

You can have only one reason to change a class.

Class or method should have only one reason to change and a reason can viewed as someone who interacts with the system.


In [1]:
#bad
class FooController
  #...
  def show
    if foo_has_something?
      #do something
    end
    
    #do something else
  end
  
  private
  def foo_has_something?
    @foo.something.count > 0
  end
  #...
end


Out[1]:
:foo_has_something?

In [2]:
#good

#foor_controller.rb
class FooController
  #...
  def show
    if @foo.has_something?
      #do something
    end
    
    #do something else
  end
  #...
end

#foo.rb
class Foo
  def foo_has_something?
    something.count > 0
  end
end


Out[2]:
:foo_has_something?

In [3]:
#bad
class Notifier
  def send_message(message)
    # send message
  end
  
  def generate_reports
    # generate reports
  end
  
  def authenticate
    # do authentication
  end
end


Out[3]:
:authenticate

In [4]:
#good
class Notifier
  def send_message(message)
    # send message
  end
end

class NotifierReport
  def generate
    # generate reports
  end
end

class NotifierAuthentication
  def authenticate
    # do authentication
  end
end


Out[4]:
:authenticate

OCP (Open Close Principle)

You can have only one reason to change a class.


In [5]:
#bad
class GraphicEditor
  def draw_shape(shape) 
   if s.m_type==1
     draw_circle(shape);
   elsif s.m_type==2
     draw_rectangle(shape);
   end
  end
  
  def draw_circle(circle) end #draw the circle
  def draw_rectangle(rectangle) end #draw the rectangle
end
 
class Shape
  attr_accessor :m_type
end
 
class Rectangle < Shape 
  def initialize
    m_type=1
  end
end
 
class Circle < Shape 
  def initialize
    m_type=2
  end
end


Out[5]:
:initialize

In [ ]:
#good
class GraphicEditor
  def draw_shape(shape) 
    shape.draw
  end
end

class Shape
  #default behavior for shapes
end
 
class Rectangle < Shape 
  def draw
    #draw the rectangle
  end
end
 
class Circle < Shape 
  def draw
    #draw the circle
  end
end

In [ ]:
#bad
class Canvas
  def draw(component)
    case component.type
    when 'popup'
      generate_popup_html(component)
    when 'section'
      generate_section_html(component)
    end
  end
  
  def generate_popup_html(popup_component)
    #build html without html, head and body tags
  end
  
  def generate_section_html(section_component)
    #build complete HTML
  end
end

In [ ]:
#bad
class Component
  def type=(type)
    @type=type
  end
  
  def type
    @type
  end
end

class PopupComponent
  def initializer
    type='popup'
  end  
  #...
end

class SectionComponent
  def initializer
    type='section'
  end
  #..
end

In [ ]:
#good
class Canvas
  def draw(component)
    component.html
  end
end

class Component
  #some default behavior
end

class PopupComponent < Component
  def html
    #build html without html, head and body tags
  end
end

class SectionComponent < Component  
  def html
    #build complete HTML
  end
end

LSP (Liskov Substitution Principle)

Is a must you can exchange a derived object for her base class.


In [ ]:
#bad
class Canvas
  def draw component
    component.draw
  end
end

class Component
  #doesn't implement draw
end

class PopupComponent < Component
  def draw
  end
end

In [ ]:
#good
class Canvas
  def draw component
    component.draw
  end
end

class Component
  def draw
  end
end

class PopupComponent < Component
  def draw
  end
end

ISP (Interface Segregation Principle)

Clients can't be forced to implement what will not use.


In [ ]:
#bad
class Component
  def draw_section_header
  end
  
  def draw_content
  end
end

class PopupComponent < Component
  # ..
end

class SectionComponent < Component
  # ..
end

In [ ]:
#good
class Component  
  def draw_content
  end
end

class PopupComponent < Component
  # ..
end

class SectionComponent < Component
  def draw_section_header
  end
end

DIP (Dependency Inversion Principle)

High Level Classes --> Abstraction Layer --> Low Level Classes.


In [ ]:
#bad
class MyController
  def index
    connection = DB.open 
    connection.execute("<query here>")
    @pages = #..treat query
  end
end

In [ ]:
#good
class MyController
  def index
    @pages = Page.all
  end
end

class Page < Database::AbstractModel
end

class Database::AbstractModel
  def self.all
    #connect and select from database
  end
end

Avoid if inside if, blocks inside blocks and so on

Methods

Is a must methods with only one responsability, small, good names and low complexity

Methods name should have verb or a verb phrase

If method do somenthing, it should have the verb in name

Methods must do only one thing (SRP)

And do it well

Methods should be small

If a method does only one thing, it should be small too

Methods does what its name says that will do

Avoid side effect

Avoid Comments

  • If you need to use a comment to tell something about your code, the code insn't clear
  • Comments increases the amount of code to maintain
  • Bad comments add noise in code
  • And if you don't maintain the comments, it can trick you
  • Automatic documentation and private codes..

Indent

What happens if a team don't follow a style guide?

Thus follow the style guide of your team

Tests

  • Tests should be clear as the production code, as SRP, methods quality and so on
  • Cover all error cases
  • Follow best pratices of you languages and frameworks

Logs

  • Log what is provided to your application and what the application returns
  • Avoid make the log file verbose using correct levels as info, error, debug and so on
  • Log the limit of your application, like a connection to external application or messages send to an external service
  • Improve the search for a log using tags or patterns
  • Make sure you logs doesn't breake your application

Application Limit

References: