Skip to content
LarsTheGlidingSquirrel edited this page Jul 6, 2023 · 20 revisions

On this wiki page we can collect and share what we consider "best practices" or guidelines for software development at Serlo. Feel free to modify, discuss and improve this page.

General information

Clean code

Git Messages

Try to write code and structure projects in a way that ... (A) is recommended by documentation of tools we use (B) resembles other project on the internet.

We use a lot of tools to save time and effort. However, a lot of unforeseeable issues can arise if we use them in an unintended / unconventional way.

✔ Try to make your code look like the code you find in the documentation examples.

Other open source projects & people might have invested a lot of time to find a good solution to a problem that is similar to ours.

✔ When working on a solution, try to find other projects/people that already have invested time into finding a good solution.

Benefits:

  • Tool documentation doubles as documentation for our own code
  • Easier for new developers to understand the codebase / project
  • Many developers probably ran into the same issues we will. Probably some help can be found online.
  • Less nasty issues that take a long time to fix

✔ Only build custom solutions when you see a large enough benefit in doing so.

Some anecdotal examples (feel free to add yours):

  • In the editor block-level math elements were considered by slate as being inline. This caused the slate normalization to delete nodes. Slate as a tool was not used how it was intended to be used and lead to hard to debug issues. (Lars)
  • A custom built Dockerfile caused some nasty dependency issues. For optimization reasons there was a yarn add some-dependency in the Dockerfile. This however caused two different versions of the dependency to be installed. An unconventional solution that caused issues and was hard to debug. (Lars)

KISS principle - Keep It Short and Simple

✔ Try to implement the shortest and simplest solution that works.

When your are deeply involved in a question or a problem it might be tempting to implement a complex solution. However, what is managable for you right now might be too complex for others or even yourself in the future. Furthermore, complexity can add up if you need to keep multiple interacting parts in your working memory at the same time.

Preference: Order source files from abstract to concrete

In the last years the best practice developed at Serlo that we order code in source files from abstract to concrete, from general concepts to more specialized concepts (the general concepts depend on). Here concepts mean software structures like functions, classes, structures, etc. So always start with the most general concept and then go to its dependent concepts. Thus we find the source code more readable and understandable. Example:

/* ✅ Good */
function main() {
  do_stuff()
}

function do_stuff() {
  do_more_concrete_stuff()
}

function do_more_concrete_stuff() {
  partey()
}

What we do not want to do:

/* ❌ Bad */
function do_more_concrete_stuff() {
  partey()
}

function do_stuff() {
  do_more_concrete_stuff()
}

function main() {
  do_stuff()
}

Reason:

  • The most abstract / general constructs tend to also be the most important ones. In the above example main() would describe what the script does and thus tells a high level story for new developers about why this is needed.
  • The most abstract / general constructs are also the one which are exported by a module and thus needed to be read more often.
  • The more concrete / specialized constructs can only be understood when you have the overall / high level overview of the code (which is given by the more general constructs)

TypeScript / JavaScript

Testing

Rust

Take care that in assert_eq() the left value is the expected one

In order for us to better understand the code we follow the convention that the left value in assert_eq and assert_neq is the actual and the right value is the expected one.

Clone this wiki locally