5.1 · Clean code & design patterns (PEP 8, SOLID)

Level 5 · Pro level & industrialization

5.1Clean code & design patterns (PEP 8, SOLID)

Goal: Write readable, structured and maintainable code.
Estimated time: 14 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • Automated PEP 8: predictable naming and formatting — black/ruff decide, no more style debates in review.
  • Pragmatic SOLID: one responsibility per unit, depend on abstractions — no dogma for small scripts.
  • Named patterns: Factory, Strategy, Adapter, Context — one name is worth ten sentences of explanation.

Running mini-case — Nabil tidies up his scripts in Algiers

Nabil, junior data analyst in Algiers, juggles analysis scripts that “work” but nobody rereads without wincing: cryptic names, a giant function that loads, cleans, computes and displays, a different style in every file. He runs black/ruff, renames everything explicitly and splits the giant function into single-responsibility units. This section tells his move to pro code — and the thread continues in 5.2, 5.3 and 5.4.

This same case returns in the lesson, the diagram, the script and the 48h challenge — and continues in 5.2, 5.3 and 5.4.

1. PEP 8: the shared grammar

At pro level, code is reread far more than it is written. PEP 8 is the official Python style guide: snake_case for functions, PascalCase for classes, UPPER for constants, contained lines, two blank lines between functions. Following it makes any code predictable: a reader knows where to look. Nabil stops reinventing his style in every script — he adopts the shared grammar.

The decisive point: style is automated. black reformats, ruff (or flake8/pylint) flags deviations — style is no longer debated in review, the tool decides. Install it, run it before every commit, fix what is flagged.

2. SOLID in practice (no dogma)

SOLID guides the split: a single responsibility per function or class, open for extension (parameters, plugins) but closed for modification (add without breaking), depend on abstractions rather than concrete details — in Python, duck typing helps (“if it quacks, it is a duck”). Nabil splits his giant function: one function loads, another cleans, a third computes — each one testable.

But pragmatism comes first: a small throwaway script needs no abstract factory. Structure what grows and lasts, keep simple what is one-off. The god-object that does everything and the factory farm for fifty lines are the two edges to avoid.

3. The patterns that pay off

Design patterns are proven solutions to recurring design problems. The ones that pay off fast in Python: the Factory (creating the object from config), the Strategy (interchangeable algorithms — the dictionary of functions in the example), the Adapter (unifying different APIs behind one interface), the Context (a home-made with via contextlib to manage resources cleanly). Nabil replaces his cascade of elif with a named strategy: adding a mode becomes adding an entry.

Team bonus: a named pattern in a review is worth ten sentences of explanation — “it is a strategy” says it all. Discover them as needs arise, never by heart all at once.

Common misinterpretation: “To be pro, you must apply SOLID to the letter and patterns everywhere.” No. With no design problem, the pattern is dead weight; with no code growth, architecture is paperwork. The right reflex: start from the pain (untestable function, duplication, an elif chain that keeps growing), then pick the minimal tool — never the reverse.
Code that rereads well Predictable and testable PEP 8 (auto)Shared style, black/ruff decide+SOLID (pragmatic)One responsibility per unit+Patterns (named)Strategy, Factory, Adapter Style is no longer debated in review: the tool decides before the commit.
Predictable and testable
Text version of the diagram (SEO / screen readers / translation): 1) PEP 8: Nabil runs his analysis scripts in Algiers through black/ruff — explicit naming, uniform formatting. 2) SOLID: he splits his giant function (one responsibility per unit) and depends on abstractions. 3) Patterns: he replaces the elif cascade with a named strategy — adding a mode becomes adding an entry. Lesson: predictable and testable, with measure.

🔎 Concrete example — Nabil replaces his elif chain with a strategy

Before/after black + named strategy.

# BEFORE: rigid
def calculer(mode, x):
    if mode == "tva":
        return x * 1.2
    elif mode == "remise":
        return x * 0.9

# AFTER: named strategy
STRATEGIES = {"tva": lambda x: x * 1.2, "remise": lambda x: x * 0.9}


def calculer(mode, x):
    return STRATEGIES[mode](x)

Reading: adding a mode = one entry in the dictionary, not one more elif.

Lesson: black . + ruff check . before every commit.

Section vocabulary

PEP 8
The official Python style guide, which makes any code predictable.

Simple example: Nabil renames his variables with explicit snake_case.

What it is not: a tool that speeds up execution.

Reflex: could a stranger read my code effortlessly?

Clean code
Readable code: explicit names, short functions, zero duplication.

Simple example: Nabil splits his giant function into three testable units.

What it is not: code that works but nobody dares reread.

Reflex: does each function have a single responsibility?

black / ruff
Tools that automatically format (black) and check style (ruff).

Simple example: Nabil runs black then ruff before every commit.

What it is not: a hand-made style debate in review.

Reflex: did the tool decide before the review?

SOLID
Splitting principles towards decoupled, maintainable code — to apply without dogma.

Simple example: Nabil opens for extension via parameters instead of modifying.

What it is not: a check-list to tick blindly.

Reflex: does this structure pay for its cost?

Strategy
Pattern: interchangeable algorithms behind one interface.

Simple example: Nabil's computation modes become dictionary entries.

What it is not: a growing cascade of elif.

Reflex: can I add a case without touching the existing code?

In practice — Making your code clean and compliant

  1. Take an old script and rename its variables/functions explicitly (snake_case, meaningful names).
  2. Install black (pip install black) and run it on the file: observe the automatic, uniform formatting.
  3. Install flake8 and fix the style warnings it reports.
  4. Split one overlong function into several short single-responsibility functions: measure the readability gain.
Your code follows PEP 8, is reformatted by black and validated by flake8: it becomes readable, consistent and professional.

Ready-to-say script — proposing automatic style

“In Algiers, our analysis scripts work but reread badly: different styles, cryptic names, giant functions. I suggest we freeze PEP 8 with black and ruff before every commit, and split the most painful function into single-responsibility units. We measure the rereading gain, then we extend.”

Say in review to get automatic formatting adopted without promising a full rewrite.

Copy-paste template — clean-reread check-list

CLEAN CODE REREAD (1 file)
NAMING: snake_case functions / PascalCase classes / UPPER constants? ___
SPLIT: does each function have ONE single responsibility? -> To split: ___
DUPLICATION: same logic in 2 places? -> To factor out: ___
TOOLS: does black pass? ___ · clean ruff check? ___
PATTERN: an elif chain growing -> strategy worth considering? ___

Solo / tight budget

Nabil alone: black + explicit renaming on the most-reread script is enough — no full rewrite.

Small business / small team

A shared black/ruff pre-commit: the whole team ships the same style without discussing it.

SME in Algiers

Nabil schedules a monthly reread in Algiers: one giant function split each time, patterns named in review.

Starting team

Pair up: one codes, the other rereads with the check-list; a pattern is added only on real pain.

✓ Best practices

  • black/ruff as pre-commit: the tool settles style.
  • One responsibility per unit: split the most painful function first.
  • Name patterns in review: one shared name is worth ten sentences.

✗ Common pitfalls

  • Debating style by hand instead of automating it.
  • Building a factory farm for fifty lines.
  • Leaving a god-object that does everything (loading, cleaning, computing, displaying).

Key takeaways

  • PEP 8 is the official Python style: following it makes code familiar to everyone.
  • Clean code = explicit names + short functions + zero duplication.
  • black reformats automatically; flake8/pylint catch defects.
  • Design patterns are proven solutions to recurring design problems.

48h challenge — apply tomorrow

Run 1 script of Nabil's through black + ruff, rename 5 cryptic names and split 1 giant function: is rereading faster?

Section exit checklist
  • I can explain PEP 8 and the role of black/ruff in one sentence.
  • My script is reformatted with no blocking warning.
  • I know when NOT to add a pattern.
Self-rating 1-5: would a colleague understand my code with no explanation?did I structure only where it relieves pain?

Frequently asked questions

Does code style really matter if it works?

Yes, hugely. Code is read far more often than it is written. A consistent style reduces bugs, speeds up rereading and eases teamwork. It is a major corporate criterion: clean code is professional code. And with black/ruff, that style costs almost nothing: the tool guarantees it before every commit.

Should I learn all the design patterns?

No, never by heart. First understand clean code principles. Discover patterns as needs arise: when a design problem keeps coming back, a pattern often solves it elegantly. Knowing them by name helps recognise those situations — Factory, Strategy, Adapter and Context already cover everyday Python essentials.

Check your understanding

What does the PEP 8 convention define?

Additional resources (optional, to go further)

Level 5 examFresh, harder questions to test what you've learned