09. Low-level Design

Best practices for organizing functionality.

Motivation

We make references to “writing code the right way”, but that is secondary to getting the correct answer. After all, how can you get a good grade if it doesn’t work?

In software engineering, everything needs to work, but doing it the right way is equally important. Why?

  • Because you are on a team, and someone else may have to understand and edit your code. Including your future self. We call this understandability.
  • Poorly-implemented solutions are more difficult to change without introducing bugs. We call this maintainability.
  • Poorly-implemented solutions may work with small data, but become intolerable with millions of records. We call this efficiency.
  • Overly-specific solutions that make assumptions about the data will break when encountering “the real world”. Avoiding this is called robustness.

The Rules

These characteristics are the result of your code design. The labs in these sections will go through code-level design principles that you, the developer, are responsible for when writing code.

The rules are:

  1. Avoid magic literals.
  2. Functions should have a single responsibility.
  3. DRY (Don’t Repeat Yourself) and the Rule of Three.
  4. Separate input/output logic from business logic.
  5. Handle errors at the lowest sensible level, and re-raise/re-throw them otherwise.
  6. Raise specific errors and define your own if needed.

Write these down! We will explore them in-depth in turn.

Example event-driven program using pygame

We’ll have some fun by creating a very simple game using the pygame library. Our example program comes from a very excellent YouTube tutorial called “The Ultimate introduction to Pygame” by Clear Code. I highly recommend his channel as his tutorials are clear and to the point.

We will implement the code as in his tutorial, but we will re-design the code by applying the rules above. His code works just fine, but our re-design will help improve the understandability, maintainability, efficiency, and robustness of the software.

Setup

  1. Download and unzip pygame-design.zip.
    • WSL users: Unzip the folder under Windows, not Linux. We will work on the native Windows side for this.
  2. Open PyCharm. Go through the menus: File -> Open. Find and open the pygame-design/ folder, then hit the Open button. You should see the following structure:
    It is essential that the root folder is pygame-design/
  3. Mac: Go to PyCharm -> Settings.Windows: Go to File -> Settings
  4. Select Project: pygame-design -> Project Interpreter. Then select Add Interpreter -> Add Local Interpreter. You should see something similar to the following:
  5. Make sure Generate New is selected. The pre-populated location should be fine. Then hit OK.
  6. Open the Integrated Terminal in PyCharm. Type the command pip install pygame to download the pygame library.

You should now be good to go.

Lesson 1: More pygame, Rules 1-2

Lesson 2: Rules 1-2 again

Grab the following files:

Code at the end of Lesson 2

Lesson 3: Rules 3-4 + refactoring an existing application

Do the following:

  • Make sure you are up-to-date and have all files from Lesson 2.
  • Download bank-accounts.zip. Unzip it alongside your other projects but not inside the pygame one. This is a separate project that we will open in PyCharm during the lesson. Unzipping the file will create a bank-accounts/ subdirectory containing several files.

Code at the end of Lesson 3

Lesson 4: Rules 5-6 on error handling

We pick up right where we left off in the previous example.

Code at the end of Lesson 4

Last modified March 27, 2025.