Clean Code

“The code will be read more than it is written”

- Robert C. Martin aka “Uncle Bob”… probably

Code

Below are some suggestions for cleaning up the Bessy code base. Simple cleanups of the code will make it much easier to see underlying design issues. For a deep dive into making clean code, please check out Uncle Bob’s book Clean Code

Note - The examples and proposed solutions exist to illustrate a point, they are in no way a criticism of the original author.

Add meaning with temporary variables

Array indexing is tough to parse at a glance.

if self.marker_data[self.marker_count][0] == "Trial Started":
   ...
elif self.marker_data[self.marker_count][0] == "Trial Ends":
   ...

Use a temporary variable to describe what was pulled out of the array.

marker_type = self.marker_data[self.marker_count][0]
if marker_type == "Trial Started":
   ...
elif marker_type == "Trial Ends":
   ...

Document code with helper functions

A wall of code is hard to follow. Keep functions shorter than a screenful.

if training:
   ... 100 lines and 4-5 indent levels ...
else:
   ... 100 more lines and 2 indent levels ...

Use helper functions to describe what’s being done.

if training:
   process_training_step()
else:
   training_completed()

Reduce duplication with helper functions

Duplicate code tends to obfuscate the key details. Can you spot the difference?

if unity_train:
    if print_training:
        print(
            "adding decision block {} to the classifier with label {}".format(
                self.decision_count, unity_label
            )
        )
    self.classifier.add_to_train(
        self.decision_blocks_processed[
            self.decision_count,
            : self.num_options,
            :,
            :,
        ],
        unity_label,
        print_training=print_training,
    )

    ## plot what was added
    # decision_vis(self.decision_blocks[self.decision_count,:,:,:], self.fsample, unity_label, self.channel_labels)
else:
    if print_training:
        print(
            "adding decision block {} to the classifier with label {}".format(
                self.decision_count,
                self.labels[self.decision_count],
            )
        )
    self.classifier.add_to_train(
        self.decision_blocks_processed[
            self.decision_count,
            : self.num_options,
            :,
            :,
        ],
        self.labels[self.decision_count],
        print_train=print_training,
    )

Factor out duplication with helper functions.

if unity_train:
    add_labelled_data(unity_label)
else:
    add_labelled_data(unity_label[self.decision_count])

def add_labelled_data(label)
   ## optionally print stuff
   ## add stuff to classifier

Make helpers private

Users of a class need to know what functions they should call.

Helper functions are typically meant only for the class to use. To signal to other users that they functions should not be called, we make them private. In Python, this is done by adding a double underscore to the beginning of a function.

Note - In OO languages, accessing a private member outside of a class is an error. With Python, private is just a convention. If the member starts with underscores, you’re not supposed to use it outside of the class, but you technically can. More info here.

def __add_labelled_data(label)
   ## optionally print stuff
   ## add stuff to classifier

Design

Prefer polymorphism to if/else or switch/case

Any time you need a class to behave differently based on a type property, consider using polymorphism instead. This turns one complicated class into smaller focused classes. The result is easier to understand code.

Generic_classifier is an example of doing this.

Apply SOLID principles

Learn the SOLID principles, this will demystify what all the “design patterns” are attempting to achieve. Bob Martin’s book is a great intro to software design. The latter half of the book is about library/package design principles and can be skipped, although it’s really good too.

Separate concerns

Learn about Clean Architecture. Many variants of this idea exist, but essentially, if you follow SOLID principles, you end up with Clean Architecture.