Skip to content

Instantly share code, notes, and snippets.

@DimaD
Last active December 14, 2015 02:08
Show Gist options
  • Save DimaD/5011177 to your computer and use it in GitHub Desktop.
Save DimaD/5011177 to your computer and use it in GitHub Desktop.
For the past year I dealt a lot with novice programmers so I summarized some of my thoughts on how to help them to become better team-players and better programmers. I plan to review and expand it in the future

How to write better code

Q: How do you eat an elephant?
A: One bite at a time.

Purpose of this guide is to help you write code which is easier to use, easier to test and easier to understand by your fellows. All code examples in this guide are written in Ruby but the language does not matter because they illustrate generic problems, not Ruby-specific constructions.

The guide itself is written in English because mastering English is a very important step on a way to improve as an IT specialist.

Learn English

I do mean it. What natural language does you favorite programming language use as a base to draw all the names from? I bet it’s English unless you write in ДРАКОН, Рапира or BrainFuck. So improving your english skill will help you to find better names for classes, methods, variables and everything you write as a programmer. In the following sections you will see why it is important to name things properly.

Learning english will also open the doors for you to international community of programmers which is large and already documented a lot of knowledge. From blogs to official APIs and books which will never be translated into russian or will loose their applicability (BTW, do you know this word?) by the time they are translated into russian.

Here are some dictionaries which are useful to translate terminology and find synonyms:

  1. Мультитран
  2. Oxford Advanced Learner’s Dictionary
  3. Lingvo
  4. Lingvo on yandex

Be careful with Lingvo, often it gives you variants of translation which nobody uses, Мультитран is much better and professional source of information.

Use BDD or TDD to drive design of your code

Tests are good, right? Right, if you know how to write them. And if you ever tried doing it you know testing is very hard. What can you do about it? Write more tests and ask all your colleagues to write them too so they can understand how to create testable code.

If you have troubles testing your code because it is too complex try TDD (test driven development) which states what you should write your tests before you write your code. This is right, before you wrote first line of code you need to have tests for them. Do not try to fit everything in this tests. Leave them small and let them test the simplest things possible. For example what you can instantiate a class which you do not have yet or call a method you have not written yet.

After you did your tests you can write empty class/method implementation and see your simple tests pass. After this you can iterate and start to introduce more complicated tests and implement your code to make them pass. Do it in iterations and make tests drive your code. After each iteration refactor your code and your tests to remove duplication.

How will it help you to write better code? Well, when you write tests you specify how you would like to use your methods, you see how the code using them will look like and how you want them to behave. By doing this you make API which is more comfortable to use. This approach when you write the code which the not-yet-existing API to solve the problem in the simplest possible way first and then write the API is called wishful thinking. It is introduced and described in a book called SICP1. It is a very simple but powerful idea. Try it next time and you will see the benefits immediately.

Write tests

Stop, what? I thought you already told about it in the previous section.

Abstractions

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

It’s all about abstraction and encapsulation.

Methods are abstractions too

To quote The Rspec Book2:

You may think of classes and interfaces when we use the word abstraction, but here’s another way to look at it: names are abstractions. That applies to names of systems, components, packages, namespaces, classes, methods, and even variable names.

So if you write code like this

class Issue
  def css_classes
    if due_date.present? && !status.is_closed
      if due_date == ::User.current.today
        "issue_warning"
      elsif due_date < ::User.current.today
        "issue_alarming"
      end
    end
    s
  end
end

stop for a minute and think: what does due_date.present? && !status.is_closed && (due_date == ::User.current.today) mean? It has something to do with dates and current day. Ah, it checks if issue’s deadline is today. It is a good idea to extract this logic in the method. Why? Because it helps you to write self-documented and understandable code. Just have a look at this code:

class Issue
  def css_classes
    if self.have_deadline_today?
      "issue-warning"
    elsif self.deadline_passed?
      "issue-alarm"
    else
      ""
    end
  end
end

Is it easier to understand? Definitely yes. You can read it almost like plain English and there is no need to dive into details what is deadline if you only need to fix class name.

By extracting code like this into method you create more methods with domain-specific-language so you can express more code in domain-specific-names. This is what DSLs (Domain Specific Languages) are all about, not magical metaprogramming.

As a bonus you can easily test/spec this methods.

Make meaningful error messages

Always describe why error happened and how to solve it. The worse thing you can do is not to show user an error and the second worse thing is to show meaningless error message like “Error happened, ooops”.

And remember: developers are users too and we do need helpful error messages to diagnose them faster.

Let’s have a look at a short maintenance script which updates issues in redmine:

for issue in issues
  issue.priority = max_priority
  if issue.save
    puts "Success: priority of #{issue} was changed to #{max_priority.name}"
  else
    puts "Error: priority of #{issue} was not changed"
  end
end

What error message does it generate?

Error: priority of Feature #1: Visualize issues connections was not changed

As a developer who runs this script I really want to know what is wrong with the system:

  • is issue invalid?
  • is max_priority invalid?
  • did we lost connection to DB?
  • maybe something else happened what I can not even think about?

Additional information would help me to understand the issue and solve it faster.
For example:

Did not change priority of "Feature #1: Visualize issues connections":
    PGError: not connected
Did not change priority of "Feature #1:":
    Issue validation errors: Tracker can't be blank, and Tracker is not included in the list

1 Structure and Interpretation of Computer Programs — Probably one of the best books about programming you can ever read. It improves your coding skills no matter what programming language you use and it helps you to form a foundation on which you can easily learn new languages, libraries and technologies. This book is the text supplementary of the course taught at MIT. You can get old but good videos of this course too.

2 The RSpec Book: Behaviour-Driven Development with RSpec, Cucumber, and Friends

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment