Software Development Principles

2021-07-18

Recently a subset of our dev team sat down and had an open discussion about what challenges we faced while developing, what areas of our codebase were hard to work in, and how we could mitigate them. We came up with a list of existing software development principles that we felt were important to us, and they are listed below:

We organized them by defining two Universal Principles, which are the overarching ideas that lead to better code - which are supported by the principles below them.

Universal Principle - Minimize Complexity

This is the most important principle while developing software, and most other software development principles are to simply ways achieve this. The most common mistake I see developers making is applying principles (e.g., DRY) without considering if they are actually reducing complexity.

DRY

Don’t repeat yourself unless it makes sense to. It’s important to note that there are times when your code does become less complex by repeating yourself.

YAGNI / KISS

A common mistake I see software developers making is pre-baking (prematurely optimizing) their solutions to reduce complexity without considering the age of the feature. In product terms, if a feature is early in its development (and therefore is subject to change), it doesn’t always make sense to over-architect the solution.

There are patterns that ‘bake’ solutions more than others, and it’s important to consider that when deciding which patterns to implement.

Minimize leakiness

All abstractions are leaky to an extent, but we should try to be aware and minimize the leakiness as much as possible. Abstractions lose a lot of their value and can increase instead of decrease complexity when they are too leaky.

Universal Principle - Code should be easy and readable

Code should be optimized for reading and understanding. It generally gets written once and read many times by other people. If your code relies on a contextual understanding that others may not have or you may forget, you’re shooting yourself in the foot.

Be Explicit

It often isn’t valuable to take shortcuts when naming classes, functions, or variables. It also doesn’t often pay to write a clever, shorter solution over a longer more explicit one.

Provide context and prevent ambiguity when naming variables

y = calc(x1, x2)

Is less effective than:

area = calculate_area(width, height)

Put conditionals into variables

Oftentimes even when not reusing them, it can make code more readable when you place conditional statements into a variable.

if (height > 10 && width < 7 && (depth < 10 || depth > 10))  
# ...
elsif (height > 10 && width < 7) 
# ...
end

# vs

has_valid_area = height > 10 && width < 7
has_valid_depth = depth < 10 || depth > 10

if (has_valid_area && has_valid_depth)  
# ...
elsif has_valid_area  
# ...
end

One method should solve one problem

Use methods as a single conceptual unit. Instead of saying that methods should be limited N lines, it’s better to break logic up into conceptual blocks.

Maintain the same level of abstraction in a method

Methods usually involve calling several other methods, and they become easier to read if they only call methods one level of abstraction lower. Think of it like a pyramid.