Chapter 02: Code Quality in Practice

From Working Code to Professional Software

Software Engineering - Winter Semester 2025/26

Your code runs perfectly. Your CI fails. What happened?

Software Engineering | WiSe 2025 | Code Quality

When Working Code Isn't Enough

The Scenario:

$ git push origin feature/road-profile-viewer

Running automated checks...
❌ Ruff check: 14 errors found
❌ Format check: 8 files need formatting
❌ Type check: 6 type errors detected

Pull request blocked - fix issues before merging

Your code works... but it doesn't meet professional standards.

Software Engineering | WiSe 2025 | Code Quality

Learning Objectives

By the end of this lecture, you will:

  1. ✅ Understand the difference between working code and professional code
  2. ✅ Recognize common code quality issues through real examples
  3. ✅ Learn Python's style guide (PEP 8) and why it matters
  4. ✅ Master modern tools: Ruff (linter/formatter) and Pyright (type checker)
  5. ✅ Apply automated checks to catch issues before human review
  6. ✅ Build quality into your workflow from day one
Software Engineering | WiSe 2025 | Code Quality

The Reality Check

Let's look at "working" code from our Road Profile Viewer:

import numpy as np
from dash import Dash, html, dcc, Input, Output
import sys,os  # Multiple imports on one line

def generate_road_profile(num_points=100,x_max=80):
    y=0.015 * x_norm**3 * x_max
    return x,y

def HelperFunction(val):  # Never used
    result=val*2
    return result

Looks reasonable, right? Let's see what the tools say...

Software Engineering | WiSe 2025 | Code Quality

Running Automated Checks

$ uv run ruff check .

main.py:16:1: E401 Multiple imports on one line
main.py:16:8: F401 `sys` imported but unused
main.py:29:5: E225 Missing whitespace around operator
main.py:36:1: N802 Function name should be lowercase
main.py:36:1: F841 Local variable is assigned but never used
main.py:47:1: E501 Line too long (149 > 88 characters)

Found 14 errors.

14 errors in code that works perfectly!

Software Engineering | WiSe 2025 | Code Quality

🤔 Discussion: Why Do These Rules Exist?

Take 2 minutes with your neighbor:

Look at these errors: E401 (multiple imports), F401 (unused import), E225 (missing whitespace), N802 (wrong naming)

Discuss:

  1. Does fixing these make the code run faster?
  2. Who benefits from these rules?
  3. What happens in a team if everyone ignores them?
Software Engineering | WiSe 2025 | Code Quality

Issue 1: Import Violations

❌ Problem:

import sys,os  # Multiple + unused

Ruff reports:

  • E401: Multiple imports on one line
  • F401: 'sys' imported but unused
  • F401: 'os' imported but unused

✅ Solution:

# Removed - not needed

Why it matters:

  • Unused imports waste memory
  • Confuse future readers
  • Accumulate as technical debt
Software Engineering | WiSe 2025 | Code Quality

Issue 2: Missing Whitespace

❌ Hard to read:

marker=dict(size=12,color='red')
y=0.015*x_norm**3
camera_x,camera_y = 0,2.0

✅ Easy to read:

marker = dict(size=12, color='red')
y = 0.015 * x_norm**3
camera_x, camera_y = 0, 2.0

Research shows: Developers parse well-spaced code ~30% faster.

Your brain chunks information at whitespace boundaries.

Software Engineering | WiSe 2025 | Code Quality

Issue 3: Naming Conventions

Python naming signals meaning:

Style Means Example
PascalCase Class RoadProfile, CameraRay
snake_case Function/variable calculate_total, camera_x
UPPER_CASE Constant MAX_SPEED, PI

❌ Problem: HelperFunction() → Looks like a class!

✅ Solution: helper_function() → Clearly a function

Software Engineering | WiSe 2025 | Code Quality

Issue 4: Dead Code

def HelperFunction(val):  # Never called anywhere!
    result = val * 2
    return result

Dead code costs:

  • 🔧 Must update for API/Python changes
  • 🧠 Readers must understand unused code
  • ⏰ May use outdated logic if revived later

Solution: Delete it! Git remembers everything.

Software Engineering | WiSe 2025 | Code Quality

Issue 5: Line Length

❌ Hard to review in split view:

marker=dict(size=12,color='red',symbol='circle',shape='diamond',opacity=0.8)

✅ Easy to spot changes in diffs:

marker = dict(
    size=12,
    color='red',
    symbol='circle',
    shape='diamond',
)

Rule: Limit lines to 88 characters (Ruff/Black default)

Software Engineering | WiSe 2025 | Code Quality

The Bigger Picture: Why Quality Matters

Four core principles:

  1. 📖 Code is read 10x more than written
  2. 🧠 Consistency reduces cognitive load
  3. 💰 Early detection prevents expensive bugs
  4. 🚀 Automation scales, humans don't

Let's explore each...

Software Engineering | WiSe 2025 | Code Quality

Principle 1: The 10:1 Rule

For every 1 hour writing code, 10 hours are spent reading it:

Activity Time
You write a feature 1 hour
You debug it 30 min
Colleague reviews it 20 min
Future you adds a feature 1 hour
New team member learns codebase 2 hours
Someone fixes a bug 1 hour

Total: 5.5 hours reading, 1 hour writing

→ Code quality optimizes for reading, not writing!

Software Engineering | WiSe 2025 | Code Quality

Principle 3: The Cost of Bugs

When Found Relative Cost Example
While writing 1x "Oops, typo!" (5 seconds)
Code review 10x "Let me push a fix..." (5 min)
QA testing 100x "Debug, fix, retest" (1 hour)
Production 1000x+ "Emergency hotfix, reputation damage"

Automated checks catch issues at 1x cost, not 1000x!

Software Engineering | WiSe 2025 | Code Quality

Principle 4: Automation Scales

The Math (10 developers, 50 PRs/week):

Without automation:

  • 30 min per review
  • 25 hours/week
  • Need extra hire!

With automation:

  • 2 min automated + 10 min human
  • 10.3 hours/week
  • Saves ~$87,000/year

Plus: Happier developers, faster delivery, fewer bugs!

Software Engineering | WiSe 2025 | Code Quality

PEP 8: The Foundation

PEP 8 is Python's official style guide.

  • Written by: Guido van Rossum (Python's creator)
  • First published: 2001
  • Status: Active, community-agreed standard

Why follow it?

  • 🗣️ It's a common language all Python devs understand
  • 🔧 All tools expect it (IDEs, linters, formatters)
  • 💼 Hiring expects it (interviews, portfolios, OSS contributions)
Software Engineering | WiSe 2025 | Code Quality

Key PEP 8 Rules

Indentation:

def example():
    if True:
        print("4 spaces")

Naming:

def calculate_average():  # snake
class RoadProfile:        # Pascal
MAX_SPEED = 100           # UPPER

Imports (grouped, ordered):

import os        # stdlib
import sys

import numpy     # third-party
from dash import Dash

from myapp import utils  # local
Software Engineering | WiSe 2025 | Code Quality

Modern Tools: The Ruff Revolution

Traditional (Pre-2023):

  • Flake8 (linting) + Black (formatting) + isort (imports) + mypy (types)
  • Four tools, four configs, slow

Modern (2024+):

  • Ruff (linting + formatting + imports)
  • Pyright (type checking)
  • Two tools, one config, blazingly fast! 🚀

Ruff is 10-100x faster than Python-based tools (written in Rust)

Software Engineering | WiSe 2025 | Code Quality

Installing Ruff - Dependency Management

Where do development tools go?

Type Command Example
Production deps uv add <pkg> dash, numpy, plotly
Dev deps uv add --dev <pkg> ruff, pytest, pyright
Global tools uv tool install <pkg> Available everywhere

Ruff is a DEV dependency - needed for development, not to run the app!

uv add --dev ruff
Software Engineering | WiSe 2025 | Code Quality

pyproject.toml After Adding Dev Deps

[project]
dependencies = ["dash", "plotly", "numpy"]  # Production

[dependency-groups]
dev = ["ruff", "pyright"]  # Development tools

Install everything: uv sync --group dev

Key insight: Production deps run the app, dev deps help you develop it.

Software Engineering | WiSe 2025 | Code Quality

Ruff Check - The Linter

Commands:

uv run ruff check .        # Check all files
uv run ruff check --fix .  # Auto-fix issues

Example output:

main.py:16:1: E401 [*] Multiple imports on one line
main.py:29:5: E225 [*] Missing whitespace around operator
Found 5 errors. [*] 3 fixable with --fix

The [*] indicates auto-fixable issues.

Software Engineering | WiSe 2025 | Code Quality

Ruff Format - The Formatter

Automatically reformats code to follow consistent style:

uv run ruff format .           # Format all files
uv run ruff format --check .   # Check only (CI mode)
uv run ruff format --diff .    # Show what would change

Before:

def generate(n=100,x=80):
    y=0.015*x_norm**3
    return x,y

After:

def generate(n=100, x=80):
    y = 0.015 * x_norm**3
    return x, y
Software Engineering | WiSe 2025 | Code Quality

Configuring Ruff

Add to pyproject.toml:

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = [
    "E", "W",   # pycodestyle (errors, warnings)
    "F",        # Pyflakes (bugs)
    "I",        # isort (imports)
    "N",        # pep8-naming
    "B",        # flake8-bugbear
    "UP",       # pyupgrade (modern syntax)
]
fixable = ["ALL"]
Software Engineering | WiSe 2025 | Code Quality

Ruff in VS Code

Install:

  1. Ctrl+Shift+X (Extensions)
  2. Search "Ruff"
  3. Install (by Astral)

Features:

  • 🔴 Red lines = errors
  • 🟡 Yellow lines = warnings
  • Ctrl+. → Quick fix
  • Shift+Alt+F → Format
Software Engineering | WiSe 2025 | Code Quality

Format on Save - Zero Effort Quality

Add to .vscode/settings.json:

{
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    }
  }
}

Result: Press Ctrl+S → Code is instantly formatted! 🎉

Software Engineering | WiSe 2025 | Code Quality

🛠️ Hands-On: Let's Fix Some Code!

Open your Road Profile Viewer in VS Code:

  1. Run uv run ruff check . - How many errors?
  2. Run uv run ruff check --fix . - How many auto-fixed?
  3. Run uv run ruff format . - Watch the spacing fix itself
  4. Check remaining issues - these need manual fixes

Remaining issues might include:

  • Function naming (change HelperFunctionhelper_function)
  • Dead code (delete unused functions)
  • Missing type hints (we'll cover this next!)
Software Engineering | WiSe 2025 | Code Quality

Type Checking with Pyright

Python is dynamically typed - flexible but dangerous:

def calculate_discount(price, discount_percent):
    return price * (discount_percent / 100)

result = calculate_discount("50", "10")  # 💥 Crash at runtime!

Type hints catch this BEFORE runtime:

def calculate_discount(price: float, discount_percent: float) -> float:
    return price * (discount_percent / 100)

result = calculate_discount("50", "10")
# ❌ Pyright: Expected float, got str
Software Engineering | WiSe 2025 | Code Quality

Installing Pyright

# Add as dev dependency
uv add --dev pyright

# Sync dependencies
uv sync --group dev

# Test it works
uv run pyright --version

⚠️ Windows users: May need Developer Mode enabled for symlinks.

# Run PowerShell as Admin:
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
Software Engineering | WiSe 2025 | Code Quality

Configuring Pyright - Strict Mode

Add to pyproject.toml:

[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.12"
include = ["src"]
exclude = [".venv", "__pycache__", "build", "dist"]

reportMissingTypeStubs = true
reportUnknownMemberType = "warning"
reportUnknownParameterType = "warning"

Modes: offbasic (default) → standardstrict

For learning: Use strict to catch everything!

Software Engineering | WiSe 2025 | Code Quality

Using Type Hints

Basic Types:

name: str = "Alice"
age: int = 25
height: float = 1.75
is_student: bool = True

Functions:

def greet(name: str) -> str:
    return f"Hello, {name}"

Optional (None handling):

def find(id: int) -> User | None:
    ...

user = find(123)
# user.name  ❌ Might be None!
if user is not None:
    print(user.name)  # ✅
Software Engineering | WiSe 2025 | Code Quality

Type Hints for Road Profile Viewer

import numpy as np
from numpy.typing import NDArray

def generate_road_profile(
    num_points: int = 100,
    x_max: float = 80
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
    """Generate a road profile using clothoid approximation."""
    x = np.linspace(0, x_max, num_points)
    x_norm = x / x_max
    y = 0.015 * x_norm**3 * x_max + 0.3 * np.sin(2 * np.pi * x_norm)
    y = y - y[0]
    return x, y
Software Engineering | WiSe 2025 | Code Quality

🤔 Quick Poll: Understanding Quality Concepts

Match the concept to its tool:

Concept Tool
1. Checking style + finding bugs A. pyright
2. Auto-fixing spacing/indentation B. ruff check
3. Validating type annotations C. ruff format

Answers: 1-B, 2-C, 3-A

Software Engineering | WiSe 2025 | Code Quality

Understanding the Quality Landscape

┌─────────────────────────────────────────┐
│            PEP 8 (Style Guide)          │
│  ┌─────────────────────────────────┐    │
│  │     Linting (ruff check)        │    │
│  │  • Style rules • Bug detection  │    │
│  │    ┌─────────────────────┐      │    │
│  │    │ Formatting (ruff format)   │    │
│  │    └─────────────────────┘      │    │
│  └─────────────────────────────────┘    │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Type System: Hints → Pyright → Safety   │
└─────────────────────────────────────────┘

Key: Ruff = style + bugs, Pyright = types (complementary!)

Software Engineering | WiSe 2025 | Code Quality

Ruff vs Pyright: Complementary Tools

Ruff handles:

  • ✅ Style violations (PEP 8)
  • ✅ Unused imports/variables
  • ✅ Common bug patterns
  • ✅ Code formatting
  • ✅ Import sorting

Pyright handles:

  • ✅ Type mismatches
  • ✅ None handling errors
  • ✅ Function signature validation
  • ✅ Missing type annotations
  • ✅ Type inference

You need BOTH! Ruff won't catch greet(42) when signature is greet(name: str).

Software Engineering | WiSe 2025 | Code Quality

Your Quality Workflow

Step-by-step:

# 1. Write code (with type hints)
def calculate_total(prices: list[float]) -> float:
    x=sum(prices)  # Oops, missing spaces
    return x

# 2. Format automatically
$ uv run ruff format .     # Fixes spacing

# 3. Check for issues
$ uv run ruff check .      # Style + bugs ✓

# 4. Validate types
$ uv run pyright           # Type safety ✓
Software Engineering | WiSe 2025 | Code Quality

Common Pitfalls

Pitfall Solution
"I'll add quality checks later" Start from day one!
"Formatters make my code ugly" Consistency > preference
"Type hints are too much work" They save 10x debugging time
"My code works, why style?" Code is read 10x more than written
Software Engineering | WiSe 2025 | Code Quality

The Complete Quality Stack

Developer writes code (with type hints)
            ↓
    ┌───────┴───────┐
    ▼               ▼
┌────────┐    ┌──────────┐
│  Ruff  │    │ Pyright  │
│ style  │    │  types   │
│ format │    │  safety  │
└────────┘    └──────────┘
    └───────┬───────┘
            ▼
    Quality Code ✓

Result: PEP 8 compliant, type-safe, maintainable code!

Software Engineering | WiSe 2025 | Code Quality

Summary: Key Takeaways

  1. Working code ≠ Quality code

    • Professional software must be readable, maintainable, type-safe
  2. Use the right tools

    • Ruff: Linting + formatting in one fast tool
    • Pyright: Type checking integrated with VS Code
  3. Follow PEP 8 - It's the Python community standard

  4. Type hints save time - Catch bugs before runtime

  5. Automate everything - Quality from day one, not bolted on later

Software Engineering | WiSe 2025 | Code Quality

What's Next?

You now know:

  • ✅ What to check (PEP 8, types, bugs)
  • ✅ How to check locally (Ruff, Pyright)

Next lecture (Chapter 02 (Feature Development)):

  • 🔄 CI/CD - Running checks automatically on every push
  • 🤖 GitHub Actions - Automation platform
  • 🔒 Branch Protection - Enforcing quality before merge
  • 🪝 Pre-commit Hooks - Local quality gates

Transform local checks → Automated pipeline!

Software Engineering | WiSe 2025 | Code Quality

🎯 Practice Time: Fix Your Code!

Steps:

  1. uv add --dev ruff pyright → Add dev dependencies
  2. uv run ruff check --fix . → Fix linting issues
  3. uv run ruff format . → Format code
  4. Add type hints to main functions
  5. uv run pyright → Fix type errors

Goal: All checks pass!

uv run ruff check . && uv run pyright  # Zero errors ✅
Software Engineering | WiSe 2025 | Code Quality

Resources

Official Documentation:

VS Code Extensions:

  • Ruff: Search "Ruff" by Astral
  • Pylance: Includes Pyright (default with Python extension)