Code Conventions and Personal Hints

My Perspective on Multiple Examples of Coding — October 2, 2017

No, this isn’t a post saying that you shouldn’t create variables called aux1, aux2 and aux3 (you shouldn’t, though).

Conventions are created by people. They encapsulate, inherently, these same people’s preconceptions and opinions. Bottom line is: they are not necessary logical (although, in many cases, much study is done before adopting a convention). Therefore, it is okay to disagree with conventions and what they state.

That doesn’t mean they were created to be broken. They represent an important feature that allows people that don’t even speak the same language to create, understand, modified and improved by each other’s code.

A fun drawing of PEP8 compliant code.
A fun drawing of PEP8 complaint code. Available at: realpython/python-pep8

So what do we do? I’ve come to the conclusion that it’s very important to follow SOME convention. Ideally, the conventions that most people follow in your current programming language. However, you should never stop looking up for new styles that might interest you and why not.

Some useful references

The most popular conventions are very easily found on the internet. You can get many hints of good programming standards from these files:

Doing your part

Conventions aren’t everything. It’s possible to write unreadable code that follows conventions. Bellow is a list of a few elements that can improve considerably one’s code (in my opinion, that is; feel free to disagree).

Use simple and pleasant names

If your scope is a method with 30 lines, there’s no need for huge variable names. Stick with the simple:

# Wrong!
active_users_in_admin_role = User.objects(roles='admin', active=True)
ids_of_active_users_in_admin_role = [user.id for user in active_users_in_admin_role]
repositories_of_active_admin_users = Repository.objects(author__in=ids_of_active_users_in_admin_role)

# Right.
users = User.objects(roles='admin', active=True)
ids_of_users = [u.id for u in users]
repositories = Repository.objects(author__in=ids_of_users)

Be consistent

Don’t keep jumping between styles:

boolean canDrive;

if (age >= 16) {
    canDrive = True;
} else {
    canDrive = False;
}

boolean canDrink = age >= 21;
if (canDrive && canDrink)
{
    return true;
}
else
{
    return false;
}

Choose one and stick with it:

boolean canDrive = age >= 16;
boolean canDrink = age >= 21;

return canDrive && canDrink;

Give similar names to what’s similar and distant names to what’s different.

It’s a good practice to aggregate similar components and separate different ones. An interesting instance of this is the Onion Architecture.

If you’re overriding a method multiple times, try to reuse the code that you’ve already created, maintaining a logical order:

public MusicStreamProvider(Music music) : this(music, DEFAULT_BUFFER_SIZE) { }

public MusicStreamProvider(Music music, int bufferSize = DEFAULT_BUFFER_SIZE)
: this(music.Id, music.Name, music.ChannelId, bufferSize) { }

public MusicStreamProvider(int musicId, string musicName, int channelId, int bufferSize = DEFAULT_BUFFER_SIZE)
{
    LocalFileName = ContextualizedPath(
        DEFAULT_DIRECTORY,
        channelId.ToString(),
        Path.ChangeExtension(
            musicId.ToString(),
            Path.GetExtension(musicName)));
    BufferSize = bufferSize;
}

If two methods don’t have a similar behavior, they most likely shouldn’t have similar names:

#Wrong!
def read_music(music, file):
...

def read_music_s(music, string):
...

#Right.
def read_music_from_file(music):
...
def read_music_from_string(music):
...

Optimize comments

Don’t comment code that could be self explanatory:

# Wrong!
int a = 10; # skip @a books.
int b = 50; # take @a books.
var page = books.paginate(a, b);

# Right.
int skip = 10;
int take = 50;
var page = books.paginate(skip, take);

Be direct, making them as short as possible:

if not_found_in_cache:
# What hasn't been found in cache must be queried from the database.
results_from_database = self.model.objects(id__in=not_found_in_cache)

Consider chaining methods instead of writing multiple statements

As an example of method chaining, WordsInABook! I’m interested in find out which are the most frequent words in a book:

public virtual ICollection MostFrequentWords(int count)
{
    return Book
        .Split()
        .GroupBy(word => word)
        .OrderByDescending(group => group.Count())
        .Take(count)
        .Select(group => group.Key)
        .ToList();
}

Of course, this is not the most efficient way to deal with this problem, but in terms of simplicity, I’d say it’s good, right? The whole problem was solved in “one” line! In my opinion, chaining methods with simple and meaningful names produce a pleasant effect on reading.

Another solution, by @cenouro:

def self.most_frequent_words(str, top_n = 10)
    str.split.sort { |a,b| str.count(b) <=> str.count(a) }.uniq.take(top_n)
end

Don’t repeat words.

I really hate verbosity, so I want to take some time on this topic…

Suppose that you’re reading a book. The authors can be extremely vague, producing really confusing material, or they could be awfully thoroughly, boring you to death. There is a tiny space between these two extremes that will create an enjoyable story.

Code is writing. As the author, you must be able to get to that enjoyable region. Achieving that will make your code less stressful and more pleasant to read, hence increasing quality.

We tend to repeat ourselves very often.

UsersRepository usersRepository = new UsersRepository(Context);
usersRepository.AddUser(user);

After four times, I THINK I GOT that this is a repository of users!

  • Why repeat the name so many times?
  • Why do we have to call it usersRepository? Doesn’t the plural form users already indicates a collection to us? Besides, we’ll have to write down a huge name every time.
  • Why is there a method called .AddUser()? This is a repository of users! The parameter of the method is User user! Did someone think we would implement the method .AddHorse(Horse horse) someday?

What if it were like this:

var users = new UsersRepository(Context);
users.Add(user);

#1 Never go full retard and implement a method called .AddHorse(Horse horse) on a class named UsersRepository.

Another example:

public class EnrollmentService
{
    public async Task OfStudent(string id)
    {
        return await Db.Enrollments
            .Where(e => e.StudentId == id)
            .ToListAsync();
    }
}

Do you think EnrollmentsService.OfStudent() is a confusing name? Look at the signature of the method, not only the name. Clearly, it returns a collection of Enrollments. That can only mean that I’m returning a list of enrollments of a given student id. This is how I’d use it:

var student = ...
var enrollments = EnrollmentService();
return await enrollments.OfStudent(student.Id);

Look at the third line. That’s almost natural! Are we seriously still considering that await enrollmentsRepository.GetEnrollmentsOfStudent(student.Id) might be better?

Switch languages often, until you find one that you like.

Java

Sometimes, your ideas don’t really match a programming language standards. Personally, I’m not a big fan of Java. Consider the example:

@Document
public class Course {
    private ObjectId id;
    private String name;
    private Professor professor;

    public getId() { return this.id; }
    public setId(ObjectId id) { this.id = id; }
    public getName() { return this.name; }
    public setName(String name) { this.name = name; }
    public getName() { return this.name; }
    public getProfessor() { return this.professor; }
    public setProfessor(Professor professor) { this.id = id; }
}

String name = ...
Professor professor = ...

Course course = new Course();
course.setName(name);
course.setProfessor(professor);

How many times did we repeat the word course on the initializing? And why do I have to say .setProfessor, given that the name of the parameter in the method’s signature is already professor? Isn’t obvious that I’m setting a professor?

#2 If Java were a sentence, it would be “Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo”. It’s grammatically correct, but it’s fucking dumb. It assumes that all programmers are retarded people that can’t read, and so it must repeat the same words over and over. This is called verbosity.

C# slightly different approach

C# is similar to Java in many ways. Repetition is not one of them:

public class Course
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Professor Professor { get; set; }
}

name = ...
professor = ...

var course = new Course
{
    Name = name,
    Professor = professor
};

Nice, right? We’ve eliminated one usage of the word course with var and the sequential calls for methods set because of the way C# allows us to instantiate classes.

C# also allows us to substitute the get and set keywords by {} and write any pre-assingment validation that we might eventually need. In other words, they work as mutator methods.

The Pythonic way.

In Python, nothing is private, only members declared with an underscore, which means “be careful, use it only if you deeply understands the class functioning”. It’s a little bit more rational, as it grants full modification freedom to future programmers, if they eventually come up with a better way to do things.

class Course(mongoengine.Document):
name = mongoengine.StringField()
professor = mongoengine.EmbeddedDocument(Professor)

name = ...
professor = ...

course = Course(name=name, professor=professor)

Conclusion

This post was very biased, I know. Let’s be honest, though: we all have our preferences when it comes to languages, styles, environments etc. I believe that programming freedom yields better results than restriction and “protection”, and that’s why Java isn’t the best language for me. In the other hand, I’m pretty positive there is a huge community that have very good reasons to like Java.

We’re all different. “Our world isn’t perfect, and that’s what makes it so beautiful” - Mustang.

#3 Finally, I’ll leave this reference: PEP 20 – The Zen of Python. You might find it very reasonable. :-)