Go to content
The Codest
  • About Us
    • Staff Augmentation
    • Project Development
    • Cloud Engineering
    • Quality Assurance
    • Web Development
  • Our Team
  • Case studies
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
  • About Us
    • Staff Augmentation
    • Project Development
    • Cloud Engineering
    • Quality Assurance
    • Web Development
  • Our Team
  • Case studies
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
2020-11-13
Software Development

How to write a good and quality code?

Justyna Mianowska

How to write a good and quality code? - Image

Writing a nice, well designed and good looking code is not as hard as it seems to be. It requires a little effort to get to know the main rules and just use them in your code. And it doesn’t have to be like everything at once, but as you feel comfortable with one thing try to think about another, and so on.

Build solid, not stupid code

Let’s start by introducing the most elementary rules that are called SOLID. It is a term describing a collection of design principles for good code that was invented by Robert C. Martin and how it goes:

S means Single Responsibility Principle

It states that a class should have one and only one reason to change. In other words, a class should have only one job to do, so we should avoid writing big classes with many responsibilities. If our class has to do a lot of things then each of them should be delegated into separate ones.

class Notification
  def initialize(params)
    @params = params
  end

  def call
    EmailSender.new(message).call
  end

  private

  def message
    # some implementation
  end
end

O means Open/Closed Principle

It states that you should be able to extend a classes behavior, without modifying it. In other words, it should be easy to extend a class without making any modifications to it. We can achieve this e.g by using strategy pattern or decorators.

class Notification
  def initialize(params, sender)
    @params = params
    @sender = sender
  end

  def call
    sender.call message
  end

  private

  def message
    # some implementation
  end
end

class EmailSender
  def call(message)
    # some implementation
  end
end

class SmsSender
  def call(message)
    # some implementation
  end
end

With this design, it is possible to add new senders without changing any code.

L means Liskov Substitution Principle

It states that derived classes must be substitutable for their base classes. In other words, usage of classes which come from the same ancestor should be easy to replace by other descendant.

class Logger {
  info (message) {
    console.info(this._prefixFor('info') + message)
  }

  error (message, err) {
    console.error(this._prefixFor('error') + message)
    if (err) {
      console.error(err)
    }
  }

  _prefixFor (type) {
    // some implementation
  }
}

class ScepticLogger extends Logger {
  info (message) {
    super.info(message)
    console.info(this._prefixFor('info') + 'And that is all I had to say.')
  }

  error (message, err) {
    super.error(message, err)
    console.error(this._prefixFor('error') + 'Big deal!')
  }
}

We can easily replace the name of the class, because both has exactly the same usage interface.

I mean Interface Segregation Principle

It states that you should make fine grained interfaces that are client specific. What is an interface? It’s a provided way of usage of some part of the code. So a violation of this rule could be e.g a class with too many methods as well as a method with too many argument options. A good example to visualize this principle is a repository pattern, not only because we often put a lot of methods into a single class but also those methods are exposed to a risk to accept too many arguments.

# not the best example this time
class NotificationRepository
  def find_all_by_ids(ids:, info:)
    notifications = Notification.where(id: ids)
    info ? notifications.where(type: :info) : notifications
  end
end

# and a better one
class NotificationRepository
  def find_all_by_ids(ids:)
    Notification.where(id: ids)
  end

  def find_all_by_ids_info(ids:)
    find_all_by_ids(ids).where(type: :info)
  end
end

D means Dependency Inversion Principle

It states that you should depend on abstractions, not on concretions. In other words, a class that uses another one should not depend on its implementation details, all what is important is the user interface.

class Notification
  def initialize(params, sender)
    @params = params
    @sender = sender
  end

  def call
    sender.call message
  end

  private

  def message
    # some implementation
  end
end

All we need to know about sender object is that it exposes `call` method which expects the message as an argument.

Not the best code ever

It is also very important to know the things which should be strictly avoided while writing code, so here goes another collection with STUPID principles which makes code not maintainable, hard for test, and reuse.

S means Singleton

Singletons are often considered as anti-patterns and generally should be avoided. But the main problem with this pattern is that it is a kind of excuse for global variables/methods and could be quickly overused by developers.

T means Tight Coupling

It is preserved when a change in one module requires also changes in other parts of the application.

U means Untestability

If your code is good, then writing tests should sound like fun, not a nightmare.

P means Premature Optimization

The word premature is the key here, if you don’t need it now then it’s a waste of time. It is better to focus on a good, clean code than in some micro-optimizations - which generally causes more complex code.

I mean Indescriptive Naming

It is the hardest thing in writing good code, but remember that it’s is not only for the rest of your team but also for future you, so treat you right :) It is better to write a long name for a method but it says everything, than short and enigmatic one.

D means Duplication

The main reason for duplication in code is following the tight coupling principle. If your code is tightly coupled, you just can’t reuse it and duplicated code appears, so follow DRY and don’t repeat yourself.

It’s not really important right now

I would like to also mention two very important things which are often left out. You should have heard about the first one - it’s YAGNI which means: you aren’t gonna need it. From time to time I observe this problem while doing code review or even writing my own code, but we should switch our thinking about implementing a feature. We should write exactly the code that we need at this very moment, not more or less. We should have in mind that everything changes very quickly (especially application requirements) so there is no point to think that something someday will come in handy. Don’t waste your time.

I don’t understand

And the last thing, not really obvious I suppose, and may be quite controversial to some, it’s a descriptive code. I don’t mean by that only using the right names for classes, variables or methods. It is really, really good when the whole code is readable from the first sight. What is the purpose of the very short code whereas it is as enigmatic as it can be, and no one knows what it does, except the person who wrote it? In my opinion, it is better to write some chars_condition statements_something else more than one word and then yesterday sitting and wondering: wait what is the result, how it happened, and so on.

const params = [
  {
    movies: [
      { title: 'The Shawshank Redemption' },
      { title: 'One Flew Over the Cuckoo\'s Nest' }
    ]
  },
  {
    movies: [
      { title: 'Saving Private Ryan' },
      { title: 'Pulp Fiction' },
      { title: 'The Shawshank Redemption' },
    ]
  }
]

// first proposition
function uniqueMovieTitlesFrom (params) {
  const titles = params
    .map(param => param.movies)
    .reduce((prev, nex) => prev.concat(next))
    .map(movie => movie.title)

  return [...new Set(titles)]
}

// second proposition
function uniqueMovieTitlesFrom (params) {
  const titles = {}
  params.forEach(param => {
    param.movies.forEach(movie => titles[movie.title] = true)
  })

  return Object.keys(titles)
}

Both methods above do the same thing: fetch unique movie titles from quite complicated parameters. And there is a question: which one says clearer about how params argument is built and which one says clearer what we want to receive as a result? I leave the answer to your own reflection.

To sum up

As you can see, there are a lot of rules to remember, but as I mentioned at the beginning writing a nice code is a matter of time. If you start thinking about one improvement to your coding habits then you will see that another good rule will follow, because all good things arise from themselves just like the bad ones.

Read more:

What is Ruby on Jets and how to build an app using it?

Vuelendar. A new Codest’s project based on Vue.js

Codest’s weekly report of best tech articles. Building software for 50M concurrent sockets (10)

Related articles

Software Development

3 Useful HTML Tags You Might Not Know Even Existed

Nowadays, accessibility (A11y) is crucial on all stages of building custom software products. Starting from the UX/UI design part, it trespasses into advanced levels of building features in code. It provides tons of benefits for...

Jacek Ludzik
Software Development

5 examples of Ruby’s best usage

Have you ever wondered what we can do with Ruby? Well, the sky is probably the limit, but we are happy to talk about some more or less known cases where we can use this powerful language. Let me give you some examples.

Pawel Muszynski
Software Development

Maintaining a Project in PHP: 5 Mistakes to Avoid

More than one article has been written about the mistakes made during the process of running a project, but rarely does one look at the project requirements and manage the risks given the technology chosen.

Sebastian Luczak
Software Development

Why you will find qualified Ruby developers in Poland?

Real Ruby professionals are rare birds on the market. Ruby is not the most popular technology, so companies often struggle with the problem of finding developers who have both high-level skills and deep experience; oh, and by the...

Jakub
Software Development

9 Mistakes to Avoid While Programming in Java

What mistakes should be avoided while programming in Java? In the following piece we answers this question.

Rafal Sawicki
Software Development

A quick dive into Ruby 2.6. What is new?

Released quite recently, Ruby 2.6 brings a bunch of conveniences that may be worth taking a glimpse of.  What is new? Let’s give it a shot!

Patrycja Slabosz

Subscribe to our knowledge base and stay up to date on the expertise from industry.

About us

The Codest – International Tech Software Company with tech hubs in Poland.

    United Kingdom - Headquarters

  • Office 303B, 182-184 High Street North E6 2JA London, England

    Poland - Local Tech Hubs

  • Business Link High5ive, Pawia 9, 31-154 Kraków, Poland
  • Brain Embassy, Konstruktorska 11, 02-673 Warsaw, Poland

    The Codest

  • Home
  • About us
  • Services
  • Case studies
  • Know how
  • Careers

    Services

  • PHP development
  • Java development
  • Python development
  • Ruby on Rails development
  • React Developers
  • Vue Developers
  • TypeScript Developers
  • DevOps
  • QA Engineers

    Resources

  • What are top CTOs and CIOs Challenges? [2022 updated]
  • Facts and Myths about Cooperating with External Software Development Partner
  • From the USA to Europe: Why do American startups decide to relocate to Europe
  • Privacy policy
  • Website terms of use

Copyright © 2022 by The Codest. All rights reserved.

We use cookies on the site for marketing, analytical and statistical purposes. By continuing to use, without changing your privacy settings, our site, you consent to the storage of cookies in your browser. You can always change the cookie settings in your browser. You can find more information in our Privacy Policy.