These are the principles I try to keep in the back of my mind when writing software.

Be pragmatic

Having a perfectly engineered solution is great, but sometimes itā€™s okay to have a sub-optimal approach until it holds you back too much. Premature optimization is an obvious example of this.

Work incrementally

Donā€™t go straight for a full-blown solution that solves all problems. Start with the most basic case first to get a better understanding of the problem space.

Use abstractions

Abstractions are a great way to make code easier to reason about. You donā€™t have to worry about the implementation details and can just focus on the high-level concept. Itā€™s important to keep in mind that good abstractions are amazing but bad abstractions will make your life miserable. Use them wisely.

If itā€™s a bad abstraction, it wonā€™t represent the high-level concept well. This often means you need to be aware of many of the implementation details to use it. To make it even worse, bad abstractions often have a negative compounding effect on the codebase. Making everything that uses it needlessly more complicated.

Getting rid of bad abstractions in time is crucial, which naturally leads to the next principle.

Refactor ruthlessly

There are two reasons for why refactoring is valuable

  • Code can always be improved
  • Changing code can uncover bad design

Not refactoring enough can result in a vicious cycle:

  • Thereā€™s friction when making changes
  • Developers build around the code instead of changing the code
  • The code becomes more difficult to understand
  • Thereā€™s even more friction when making changes

šŸ§¹ You Should Refactor More Than You Think is an article that focuses on this particular principle.

Manage dependencies

Avoid unnecessary dependencies, but more importantly, make sure dependencies make sense. Circular dependencies will come back to haunt you.

Write stateless code

State is hard to reason about and much more difficult to test. Of course, there will almost always be at least some form of state, but splitting that from stateless code will make it much easier to understand and debug.

Automated Tools over Manual Guidelines

The simplest example of this is formatting. Itā€™s always better to have an automated formatting tool than expect developers to point out formatting issues in Code Reviews.

Folder structure

Keep test files next to code files. All related code files should live as closely together as possible.