Building maintainable code

Bad code good code

Bad code is rigid (every change affects many other parts of the code) fragile (breaks at unrelated parts of the code, bugs exposing up later down the line), and immobile (cannot be reused outside the context). Because we want to deliver a professional product at the end of the day we better write good code. In the assessment rubric, you find the following criteria for good code:

Code is written in short units and simple units. Code is written once, interfaces are small, single responsibility principle is applied, components are balanced, automated tests are supplied

In this section, the criteria are explained in more detail.

Write short units of code

Why: short methods are easy to analyze, test and reuse

What: max 15 lines of code. If you need more, you probably add functionality belonging to its own unit.

Refactoring techniques:

  • Vectorize code using comprehensions or numpy arrays

Write simple units of code

Why: makes it easier to modify and test

What: Limit the number of branch point (if, while, for etc.) to 4, avoid complex units

-> avoid deeply nested loops (more than 1-2)

-> avoid nested functions deeper than 1 level from global scope

-> use python idioms (ie. “in” on iterables, don’t use truth tests explicitly on True or False)

Refactoring techniques:

  • Use mapping

  • Identify distinct cases and use return to leave the method

Write code once

Why: when code is copied bug needs to be fixed multiple times, inefficient, error-prone

What: write reusable generic code

Refactoring technique:

  • use functions or superclasses

Keep interfaces small

Why: easier to understand, maintain, test and reuse

What: limit the number of parameters per unit to at most 4

Refactoring techniques:

  • combine related parameters into an object

  • split the unit into multiple units

Keep Architecture Components Balanced

Why: easy to locate code and allows isolation for maintenance, related code is grouped together

What: organize your code in a way that the numbers of components between 6- 12. Not too many components nor too few. Makes a logical grouping (f.i. fetching data, preprocess data, process data, logging data, configuration processes, visualize data, data storage) Components should be of comparable size

Automated test

Why: Makes development predictable and less risky. Writing tests makes you write better code. It leads to code that have less parameters, shorter units, simpler units. It saves time to (repeat) tests manually.

What: write unit tests using a test framework. Test both normal and special cases. Maintain test like normal code. Cover the critical parts at least. Use a linter like nblint

a good notebook that demonstrates use of linters, unit tests and performance test is the following notebook

https://github.com/JustinMatters/Testing-and-Linting-in-Jupyter/blob/master/Testing_Notebook.ipynb

Write clean code

  • Leave no code smells behind

  • Leave no bad comments behind

  • Leave no code in comments behind

  • Leave no bad comments behind

  • Leave no dead code behind

  • Leave no long identifier names behind (for example: ‘globalprocessingfileandstore’)

  • Leave no badly handled exceptions behind (catch specific exceptions)

References

https://www.softwareimprovementgroup.com/resources/ebook-building-maintainable-software/

Last updated