EASYTUTORGUIDE

Practical tutorials, tools, courses, digital skills, and business promotion.

Free Learning

JavaScript – Chapter 2: Development Environment

Learn what JavaScript is, where it runs, why it is important, and how beginners can start using it with simple examples and clear output.

Beginner Friendly JavaScript Basics Web Development Code Examples
JavaScript Lesson 2 Chapter 2 Topics Day / Night Mode

Main reading content

python-course-chapter-36

Chapter 36: Code Quality and Professional Python Style

A complete beginner-friendly guide to clean Python code, PEP standards, formatting tools, linters, documentation, refactoring, code smells, complexity, automation, and professional project standards.

Goal: Learn how to write Python programs that are readable, consistent, maintainable, testable, documented, and suitable for professional software projects.

Chapter 36 Topics

``` ```

36.1 Clean Code Principles

```

Clean code is code that another programmer can read, understand, test, change, and extend without unnecessary difficulty. A clean program uses meaningful names, small focused functions, clear control flow, consistent formatting, and limited duplication.

Clean code is not only about making code look attractive. It reduces misunderstandings, prevents mistakes, improves teamwork, and makes future changes safer. Code is usually read many more times than it is written, so readability is extremely important.

Poor Example

def x(a, b, c):
t = a * b
if c:
    t = t - t * .1
return t

The function may work, but the names do not explain what the values represent. A future programmer must inspect every line to understand its purpose.

Improved Example

def calculate_order_total(
unit_price: float,
quantity: int,
has_discount: bool
```

) -> float:
subtotal = unit_price * quantity

```
if has_discount:
    discount = subtotal * 0.10
    subtotal -= discount

return subtotal
```

print(
calculate_order_total(
unit_price=20,
quantity=3,
has_discount=True
)
)
```

Output

54.0

Clean Code Guidelines

  • Use names that explain purpose.
  • Keep functions focused on one responsibility.
  • Avoid deeply nested conditions.
  • Remove unused code and variables.
  • Prefer clear code over clever code.
  • Write tests before major refactoring.
  • Keep formatting consistent.
  • Document important decisions and public interfaces.
```

36.2 PEP 8

```

PEP 8 is the main style guide for Python code. It recommends conventions for indentation, spacing, line breaks, imports, naming, blank lines, and code organization.

PEP 8 does not change how Python executes a program. Its purpose is to make Python projects more consistent and readable. Teams may adjust some rules, but consistency within a project should remain the main goal.

Poor Formatting

def calculate(a,b):
```

return a+b
result=calculate(10,20)
print(result)
```

PEP 8 Style

def calculate(
first_number: int,
second_number: int
```

) -> int:
return first_number + second_number

result = calculate(10, 20)

print(result)
```

Output

30

Common PEP 8 Recommendations

  • Use four spaces for each indentation level.
  • Use lowercase words with underscores for functions and variables.
  • Use capitalized words for class names.
  • Place imports near the top of the file.
  • Use blank lines to separate major sections.
  • Avoid unnecessary whitespace.
  • Keep lines reasonably short.
  • Place spaces around most operators.

Spacing Example

# Poor style
```

total=price*quantity+tax

# Improved style

total = price * quantity + tax

36.3 PEP 257

```

PEP 257 provides conventions for Python docstrings. A docstring is a string placed at the beginning of a module, class, function, or method to explain its purpose.

A short docstring may fit on one line. Longer docstrings usually begin with a summary, followed by a blank line and additional details. The summary should describe the action using clear language.

One-Line Docstring

def add(first: int, second: int) -> int:
"""Return the sum of two integers."""
return first + second

Multi-Line Docstring

def calculate_average(
scores: list[float]
```

) -> float:
"""Calculate the average score.

```
Return zero when the supplied list is empty.
Otherwise, divide the total by the number of scores.
"""
if not scores:
    return 0.0

return sum(scores) / len(scores)
```

print(calculate_average([80, 90, 100]))
```

Output

90.0

Module Docstring

"""Utilities for calculating student grades.
```

This module contains functions for averages,
letter grades, and pass-or-fail decisions.
"""

36.4 Naming Conventions

```

Good names communicate what data represents and what a function does. Names should be specific enough to remove confusion but not unnecessarily long.

Python commonly uses different naming styles for variables, functions, classes, constants, modules, and internal attributes.

Common Naming Styles

# Variables
```

student_name = "Sara"
total_price = 49.99

# Functions

def calculate_total():
pass

# Classes

class ShoppingCart:
pass

# Constants

TAX_RATE = 0.13
MAXIMUM_ATTEMPTS = 3

# Internal-use name

_cached_result = None
```

Poor Names

a = 20
```

b = 3
c = a * b
```

Improved Names

unit_price = 20
```

quantity = 3
subtotal = unit_price * quantity

print(subtotal)
```

Output

60

Naming Advice

  • Use nouns for variables and classes.
  • Use verbs for functions and methods.
  • Avoid unclear abbreviations.
  • Do not reuse names for unrelated meanings.
  • Avoid names that differ only by capitalization.
  • Use Boolean names such as is_active or has_access.
```

36.5 Code Formatting

```

Code formatting controls indentation, line breaks, spacing, quotation style, and layout. Consistent formatting makes code easier to scan and reduces arguments about appearance during teamwork.

Manual formatting is possible, but automated formatters can apply a consistent style across an entire project.

Poorly Formatted Code

def create_user(name,email,active=True):
return {"name":name,"email":email,"active":active}

Improved Formatting

def create_user(
name: str,
email: str,
active: bool = True
```

) -> dict[str, str | bool]:
return {
"name": name,
"email": email,
"active": active
}
```

Long Expression Formatting

total = (
subtotal
+ tax
+ delivery_charge
- discount
```

)
```

Formatting Principles

  • Use consistent indentation.
  • Break long expressions logically.
  • Separate unrelated sections with blank lines.
  • Avoid placing many statements on one line.
  • Use automated tools consistently across the team.
```

36.6 Black

```

Black is an automatic Python code formatter. It reformats source files into a consistent style with very little configuration.

Black does not usually change program behavior. It changes layout, spacing, line wrapping, and quotation formatting so developers do not need to format every detail manually.

Install Black

python -m pip install black

Format One File

python -m black app.py

Format a Folder

python -m black .

Check Without Changing Files

python -m black --check .

Before Black

def calculate(a,b,c=0):
```

return a+b+c
```

After Black

def calculate(a, b, c=0):
return a + b + c

Typical Output

reformatted app.py
```

All done!
1 file reformatted.

36.7 Ruff

```

Ruff is a fast Python linter and code-quality tool. It can detect unused imports, undefined names, style problems, suspicious expressions, unnecessary code, and many other issues.

Ruff can also automatically correct many findings. It is often configured through a pyproject.toml file.

Install Ruff

python -m pip install ruff

Check a Project

python -m ruff check .

Automatically Fix Safe Problems

python -m ruff check . --fix

Format with Ruff

python -m ruff format .

Problem Example

import os
```

import math

def add(first, second):
unused_value = 100
return first + second
```

Possible Ruff Findings

F401 `os` imported but unused
```

F401 `math` imported but unused
F841 Local variable `unused_value` is assigned but never used
```

Corrected Version

def add(first, second):
return first + second

Basic Configuration

[tool.ruff]
```

line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I", "B"]

36.8 Flake8

```

Flake8 is a widely used Python linting tool. It combines style checking, error detection, and complexity-related plugins.

Flake8 reports problems but normally does not reformat code automatically. Developers review its messages and correct the source.

Install Flake8

python -m pip install flake8

Check One File

python -m flake8 app.py

Check the Current Project

python -m flake8 .

Problem Example

import os
```

def greet(name):
message="Hello "+name
return message
```

Possible Output

app.py:1:1: F401 'os' imported but unused
```

app.py:5:12: E225 missing whitespace around operator
```

Corrected Code

def greet(name):
message = "Hello " + name
return message

Configuration Example

[flake8]
```

max-line-length = 88
exclude =
.git,
.venv,
**pycache**

36.9 Pylint

```

Pylint performs detailed static analysis of Python code. It checks style, possible errors, naming, design problems, duplicated code, unused values, and other quality concerns.

Pylint may report more messages than simpler tools. Not every warning requires a change, but each message should be understood before it is disabled.

Install Pylint

python -m pip install pylint

Check a File

python -m pylint app.py

Problem Example

def F(x):
y = 10
return x + 1

Possible Findings

Missing function or method docstring
```

Function name "F" doesn't conform to snake_case naming style
Unused variable "y"
```

Improved Code

def increment(number: int) -> int:
"""Return the supplied number increased by one."""
return number + 1

Generate a Configuration File

python -m pylint --generate-rcfile > .pylintrc
```

36.10 Isort

```

Isort automatically organizes Python imports. It groups standard-library imports, third-party imports, and local project imports into consistent sections.

Organized imports make dependencies easier to understand and prevent developers from manually rearranging long import lists.

Install Isort

python -m pip install isort

Sort Imports

python -m isort .

Check Without Changing

python -m isort . --check-only

Before Isort

from shop.cart import ShoppingCart
```

import os
from pathlib import Path
import requests
import json
```

After Isort

import json
```

import os
from pathlib import Path

import requests

from shop.cart import ShoppingCart
```

Black-Compatible Configuration

[tool.isort]
```

profile = "black"

36.11 Docstrings

```

Docstrings document modules, classes, functions, and methods. They can be viewed with help(), development tools, and documentation generators.

A useful function docstring explains what the function does, important parameters, the return value, and expected exceptions when these are not already obvious.

Function Docstring

def divide(
dividend: float,
divisor: float
```

) -> float:
"""Divide one number by another.

```
Args:
    dividend: Number that will be divided.
    divisor: Number used as the divisor.

Returns:
    The division result.

Raises:
    ValueError: If the divisor is zero.
"""
if divisor == 0:
    raise ValueError(
        "Divisor cannot be zero."
    )

return dividend / divisor

Class Docstring

class ShoppingCart:
"""Store products and calculate the cart subtotal."""

def __init__(self) -> None:
    """Create an empty shopping cart."""
    self.items: list[float] = []

View Documentation

help(divide)

Output

Help on function divide:
```

divide(dividend: float, divisor: float) -> float
Divide one number by another.

36.12 Comments

```

Comments explain information that is not clear from the code itself. Good comments describe reasons, important decisions, unusual limitations, or business rules.

Comments should not repeat obvious code. They must also be updated when the code changes, or they can become misleading.

Poor Comment

# Add one to count
```

count += 1
```

Useful Comment

# The external service uses one-based page numbers.
```

page_number = internal_index + 1
```

Business Rule Comment

# Ontario tax rate used for this sample calculation.
```

TAX_RATE = 0.13
```

TODO Comment

# TODO: Replace the in-memory repository with database storage.

Comment Guidelines

  • Explain why, not only what.
  • Keep comments accurate.
  • Do not use comments to excuse confusing code.
  • Prefer meaningful names when they can remove the need for a comment.
  • Do not leave large blocks of old commented-out code.
```

36.13 Documentation

```

Documentation explains how to install, configure, use, test, and maintain a project. It may include a README file, API documentation, examples, setup instructions, architecture notes, and contribution rules.

Good documentation should answer the questions a new user or developer is likely to ask. It should be updated whenever important behavior changes.

README Structure

# Student Manager
```

## Description

A Python application for storing students and scores.

## Requirements

* Python 3.11 or newer

## Installation

python -m pip install -r requirements.txt

## Run

python main.py

## Test

python -m pytest

## Format

python -m black .

## Lint

python -m ruff check .
```

Useful Project Documentation

  • Project purpose
  • Installation requirements
  • Environment configuration
  • Usage examples
  • Testing commands
  • Project structure
  • API or function references
  • Known limitations
  • Contribution instructions
  • Licence information
```

36.14 Refactoring

```

Refactoring means improving the internal structure of code without intentionally changing its external behavior. Common refactorings include renaming variables, extracting functions, simplifying conditions, and moving responsibilities into suitable classes.

Tests should be run before and after refactoring. They provide evidence that behavior has remained correct.

Before Refactoring

def print_receipt(items):
subtotal = 0

for item in items:
    subtotal += (
        item["price"]
        * item["quantity"]
    )

tax = subtotal * 0.13
total = subtotal + tax

print("Subtotal:", subtotal)
print("Tax:", tax)
print("Total:", total)

After Refactoring

TAX_RATE = 0.13
```

def calculate_subtotal(
items: list[dict[str, float | int]]
) -> float:
return sum(
item["price"] * item["quantity"]
for item in items
)

def calculate_tax(
subtotal: float
) -> float:
return subtotal * TAX_RATE

def print_receipt(
items: list[dict[str, float | int]]
) -> None:
subtotal = calculate_subtotal(items)
tax = calculate_tax(subtotal)
total = subtotal + tax

```
print("Subtotal:", subtotal)
print("Tax:", tax)
print("Total:", total)

Benefits

  • Calculations can be tested separately.
  • Functions have clearer responsibilities.
  • The tax rate has a meaningful constant name.
  • Future changes are easier to make.
```

36.15 Reducing Code Duplication

```

Duplication occurs when the same or nearly identical logic appears in several places. Repeated code increases maintenance work because every copy may need to be updated.

Duplication can often be reduced by extracting a shared function, using a loop, creating a reusable class, or storing changing values in data structures.

Duplicated Code

pizza_subtotal = 14.99 * 2
```

pizza_tax = pizza_subtotal * 0.13
pizza_total = pizza_subtotal + pizza_tax

burger_subtotal = 9.99 * 3
burger_tax = burger_subtotal * 0.13
burger_total = burger_subtotal + burger_tax
```

Improved Code

TAX_RATE = 0.13
```

def calculate_item_total(
price: float,
quantity: int
) -> float:
subtotal = price * quantity
tax = subtotal * TAX_RATE

```
return subtotal + tax
```

pizza_total = calculate_item_total(
14.99,
2
)

burger_total = calculate_item_total(
9.99,
3
)

print(pizza_total)
print(burger_total)
```

Output

33.8774
```

33.8661

36.16 DRY Principle

```

DRY means “Do Not Repeat Yourself.” The principle encourages developers to keep one reliable source for each important rule or piece of knowledge.

DRY does not mean that every similar-looking line must be combined. Creating a complicated abstraction too early can make code harder to understand. Remove meaningful duplication when a shared rule is clear.

Repeated Business Rule

checkout_tax = subtotal * 0.13
```

refund_tax = refund_amount * 0.13
report_tax = monthly_sales * 0.13
```

Single Source of Truth

TAX_RATE = 0.13
```

def calculate_tax(
amount: float
) -> float:
return amount * TAX_RATE

checkout_tax = calculate_tax(subtotal)
refund_tax = calculate_tax(refund_amount)
report_tax = calculate_tax(monthly_sales)
```

Benefits

  • The rule is defined once.
  • A future tax-rate change occurs in one place.
  • Tests can focus on one shared function.
  • Different parts of the application remain consistent.
```

36.17 KISS Principle

```

KISS means “Keep It Simple.” A straightforward solution is usually easier to read, test, debug, and maintain than an unnecessarily complicated solution.

Simplicity does not mean ignoring requirements. It means satisfying the real requirements using the clearest reasonable design.

Unnecessarily Complicated

def is_adult(age):
result = {
    True: "yes",
    False: "no"
}[age >= 18]

return result == "yes"

Simple Version

def is_adult(age: int) -> bool:
return age >= 18
```

print(is_adult(20))
print(is_adult(15))
```

Output

True
```

False
```

KISS Guidelines

  • Use direct expressions when they are clear.
  • Avoid unnecessary classes and layers.
  • Do not optimize before performance is a real problem.
  • Use familiar language features when possible.
  • Prefer readable conditions over clever tricks.
```

36.18 YAGNI Principle

```

YAGNI means “You Are Not Going to Need It.” It advises developers not to build features, abstractions, or configuration options before a real requirement exists.

Future needs are difficult to predict. Unused features increase code size, testing requirements, documentation work, and maintenance cost.

Overbuilt Design

class ReportEngine:
def generate(
    self,
    format_name,
    language,
    theme,
    encryption,
    cloud_provider,
    compression
):
    pass

If the current requirement is only to print a basic text report, most of these options are unnecessary.

Current Requirement Only

def create_text_report(
title: str,
body: str
```

) -> str:
return f"{title}\n{'=' * len(title)}\n{body}"

print(
create_text_report(
"Sales Report",
"Total sales: $500"
)
)
```

Output

Sales Report
```

============
Total sales: $500
```

New formats can be added when there is a confirmed need and enough information to design them properly.

```

36.19 Code Smells

```

A code smell is a sign that code may be difficult to maintain or may contain a deeper design problem. A smell is not always a bug, but it deserves investigation.

Common Code Smells

  • Very long functions
  • Large classes with many responsibilities
  • Deeply nested conditions
  • Duplicated logic
  • Too many function parameters
  • Unclear names
  • Global mutable variables
  • Repeated Boolean flags
  • Large blocks of commented-out code
  • Functions that change unrelated data

Long Function Smell

def process_order(order):
# Validate customer
# Validate products
# Calculate subtotal
# Apply discount
# Calculate tax
# Calculate delivery
# Save order
# Send email
# Print receipt
pass

Improved Organization

def process_order(order):
validate_order(order)

totals = calculate_totals(order)

saved_order = save_order(
    order,
    totals
)

send_receipt(saved_order)

return saved_order

Each helper function can now focus on one responsibility and be tested separately.

```

36.20 Cyclomatic Complexity

```

Cyclomatic complexity estimates the number of independent paths through a function. Conditions, loops, and exception branches increase complexity.

A highly complex function is harder to understand and requires more tests. Complexity can often be reduced by extracting functions, returning early, and replacing long conditional chains with data-driven logic.

Complex Example

def shipping_cost(
country,
subtotal,
is_member,
is_express
```

):
if country == "Canada":
if subtotal >= 50:
if is_express:
return 10
return 0

```
    if is_member:
        return 5

    return 8

if country == "USA":
    if is_express:
        return 20

    return 12

return 30

Simplified Version

def canadian_shipping(
subtotal: float,
is_member: bool,
is_express: bool
```

) -> float:
if subtotal >= 50:
return 10 if is_express else 0

```
if is_member:
    return 5

return 8
```

def shipping_cost(
country: str,
subtotal: float,
is_member: bool,
is_express: bool
) -> float:
if country == "Canada":
return canadian_shipping(
subtotal,
is_member,
is_express
)

```
if country == "USA":
    return 20 if is_express else 12

return 30

Measuring Complexity

python -m pip install radon
```

python -m radon cc app.py -a
```

Example Output

app.py
F 1:0 shipping_cost - B
```

Average complexity: B

36.21 Pre-Commit Hooks

```

Pre-commit hooks automatically run checks before a Git commit is created. They can format code, sort imports, detect lint problems, and prevent common mistakes from entering the repository.

Hooks create consistent checks for every contributor. They do not replace continuous integration, but they catch many problems earlier.

Install Pre-Commit

python -m pip install pre-commit

Configuration File

# .pre-commit-config.yaml
```

repos:

* repo: https://github.com/psf/black
  rev: 24.10.0
  hooks:

  * id: black

* repo: https://github.com/astral-sh/ruff-pre-commit
  rev: v0.8.0
  hooks:

  * id: ruff
    args: [--fix]
  * id: ruff-format

Install the Git Hook

python -m pre_commit install

Run All Hooks Manually

python -m pre_commit run --all-files

Example Output

black................................................Passed

ruff.................................................Passed
ruff-format..........................................Passed
```

Tool versions in real projects should be reviewed and updated regularly rather than copied permanently without maintenance.

```

36.22 Professional Project Standards

```

Professional Python projects use an organized structure, automated tests, dependency management, documentation, code-quality checks, version control, and clear configuration.

Standards vary by team, but developers should be able to install, run, test, format, and understand the project using documented commands.

Example Project Structure

professional_project/
```

│
├── src/
│   └── store/
│       ├── **init**.py
│       ├── models.py
│       ├── pricing.py
│       └── service.py
│
├── tests/
│   ├── test_pricing.py
│   └── test_service.py
│
├── README.md
├── pyproject.toml
├── .gitignore
├── .pre-commit-config.yaml
└── requirements-dev.txt
```

Professional Standards Checklist

  • Clear folder structure
  • Meaningful names
  • Consistent formatting
  • Automated linting
  • Static type checking
  • Unit and integration tests
  • Documented setup commands
  • Dependency version management
  • No passwords or secrets in source code
  • Useful error handling
  • Version control with meaningful commits
  • Automated continuous integration
  • Code review before merging important changes

Example Development Commands

python -m black .
```

python -m ruff check .
python -m mypy src
python -m pytest
python -m pytest --cov=src

36.23 Chapter Practice Exercises

```

Complete these exercises to practise naming, formatting, linting, documentation, refactoring, duplication removal, complexity reduction, and professional project organization.

  1. Rename five unclear variables in a small program.
  2. Rewrite a function using PEP 8 spacing.
  3. Correct inconsistent indentation.
  4. Add a module docstring.
  5. Add a one-line function docstring.
  6. Add a detailed class docstring.
  7. Replace an unnecessary comment with a meaningful name.
  8. Remove commented-out code.
  9. Install and run Black.
  10. Use Black check mode.
  11. Install and run Ruff.
  12. Use Ruff automatic fixes.
  13. Install and run Flake8.
  14. Install and run Pylint.
  15. Sort imports with Isort.
  16. Create a pyproject.toml file.
  17. Extract a long calculation into a function.
  18. Split a function with multiple responsibilities.
  19. Remove duplicated tax calculations.
  20. Create a shared validation function.
  21. Apply the DRY principle to repeated business rules.
  22. Simplify an overcomplicated Boolean function.
  23. Remove an unused future feature using YAGNI.
  24. Identify five code smells.
  25. Replace deep nesting with early returns.
  26. Measure complexity with Radon.
  27. Reduce the complexity of a grade function.
  28. Create a pre-commit configuration.
  29. Add Black and Ruff hooks.
  30. Create a professional README.
  31. Create a .gitignore file.
  32. Create separate source and test folders.
  33. Add type hints to public functions.
  34. Add tests before refactoring.
  35. Run formatter, linter, type checker, and tests together.
  36. Create a professional project-quality checklist.

Practice Refactoring Example

# Before refactoring
```

def p(a, b, c):
x = a * b

```
if c == True:
    x = x - x * .1

print("total:", x)

return x

Improved Version

DISCOUNT_RATE = 0.10
```

def calculate_total(
unit_price: float,
quantity: int,
has_discount: bool
) -> float:
"""Calculate an order total with an optional discount."""
subtotal = unit_price * quantity

```
if has_discount:
    subtotal -= subtotal * DISCOUNT_RATE

return round(subtotal, 2)
```

def display_total(total: float) -> None:
"""Display a formatted order total."""
print(f"Total: ${total:.2f}")

order_total = calculate_total(
unit_price=20,
quantity=3,
has_discount=True
)

display_total(order_total)
```

Output

Total: $54.00
```

36.24 Chapter Refactoring Project

```

This project begins with a poorly designed order-processing script. The program works for basic data, but it contains unclear names, duplicated rules, deeply mixed responsibilities, weak validation, global state, and poor formatting.

Unrefactored Program

orders=[]
```

def p(n,e,items,member=False):
t=0
for i in items:
if i["q"]>0:
t=t+i["p"]*i["q"]
d=0
if member==True:
d=t*.1
t=t-d
tax=t*.13
if t>40:
shipping=0
else:
shipping=5
total=t+tax+shipping
o={"name":n,"email":e,"items":items,"subtotal":t,"tax":tax,"shipping":shipping,"total":total}
orders.append(o)
print("customer",n)
print("subtotal",t)
print("tax",tax)
print("shipping",shipping)
print("total",total)
return o

p("Sara","[sara@example.com](mailto:sara@example.com)",[{"n":"Pizza","p":14.99,"q":2},{"n":"Drink","p":2.5,"q":2}],True)
```

Problems in the Original Code

  • Names such as p, t, d, and o are unclear.
  • Formatting is inconsistent.
  • Business rules are hidden as unexplained numbers.
  • Validation, calculation, storage, and display are mixed together.
  • A mutable global order list stores application data.
  • Dictionary keys are abbreviated.
  • Money values are not consistently rounded.
  • The function has too many responsibilities.
  • There are no type hints or docstrings.
  • The design is difficult to test.

Refactored Project Structure

order_project/
```

│
├── src/
│   └── orders/
│       ├── **init**.py
│       ├── models.py
│       ├── pricing.py
│       ├── repository.py
│       ├── service.py
│       └── presentation.py
│
├── tests/
│   ├── test_models.py
│   ├── test_pricing.py
│   └── test_service.py
│
├── main.py
├── pyproject.toml
├── requirements-dev.txt
├── README.md
└── .pre-commit-config.yaml
```

File 1: src/orders/models.py

"""Domain models for the order application."""
```

from dataclasses import dataclass, field

@dataclass(frozen=True)
class Product:
"""Represent a product available for purchase."""

```
name: str
unit_price: float

def __post_init__(self) -> None:
    if not self.name.strip():
        raise ValueError(
            "Product name cannot be empty."
        )

    if self.unit_price < 0:
        raise ValueError(
            "Product price cannot be negative."
        )
```

@dataclass(frozen=True)
class OrderItem:
"""Represent a product and its ordered quantity."""

```
product: Product
quantity: int

def __post_init__(self) -> None:
    if self.quantity <= 0:
        raise ValueError(
            "Quantity must be greater than zero."
        )

def subtotal(self) -> float:
    """Return the subtotal for this order item."""
    return round(
        self.product.unit_price
        * self.quantity,
        2
    )
```

@dataclass(frozen=True)
class Customer:
"""Represent a customer placing an order."""

```
name: str
email: str
is_member: bool = False

def __post_init__(self) -> None:
    if not self.name.strip():
        raise ValueError(
            "Customer name cannot be empty."
        )

    if "@" not in self.email:
        raise ValueError(
            "Customer email is invalid."
        )
```

@dataclass
class Order:
"""Represent a completed customer order."""

```
order_id: int
customer: Customer
items: list[OrderItem]
subtotal: float
discount: float
tax: float
shipping: float
total: float

File 2: src/orders/pricing.py

"""Pricing rules for orders."""
```

from orders.models import OrderItem

TAX_RATE = 0.13
MEMBER_DISCOUNT_RATE = 0.10
FREE_SHIPPING_MINIMUM = 40.00
STANDARD_SHIPPING_CHARGE = 5.00

def calculate_items_subtotal(
items: list[OrderItem]
) -> float:
"""Return the total before discounts, tax, and shipping."""
return round(
sum(
item.subtotal()
for item in items
),
2
)

def calculate_discount(
subtotal: float,
is_member: bool
) -> float:
"""Return the membership discount."""
if not is_member:
return 0.0

```
return round(
    subtotal
    * MEMBER_DISCOUNT_RATE,
    2
)
```

def calculate_tax(
discounted_subtotal: float
) -> float:
"""Return the tax for the discounted subtotal."""
if discounted_subtotal < 0:
raise ValueError(
"Subtotal cannot be negative."
)

```
return round(
    discounted_subtotal * TAX_RATE,
    2
)
```

def calculate_shipping(
discounted_subtotal: float
) -> float:
"""Return the shipping charge."""
if discounted_subtotal >= FREE_SHIPPING_MINIMUM:
return 0.0

```
return STANDARD_SHIPPING_CHARGE

File 3: src/orders/repository.py

"""Order storage implementations."""
```

from orders.models import Order

class OrderRepository:
"""Store orders in memory for this sample project."""

```
def __init__(self) -> None:
    self._orders: dict[int, Order] = {}

def save(self, order: Order) -> None:
    """Save or replace an order."""
    self._orders[order.order_id] = order

def find(self, order_id: int) -> Order | None:
    """Return an order by identifier."""
    return self._orders.get(order_id)

def all_orders(self) -> list[Order]:
    """Return a copy of all stored orders."""
    return list(self._orders.values())

def count(self) -> int:
    """Return the number of stored orders."""
    return len(self._orders)

File 4: src/orders/service.py

"""Application services for creating orders."""
```

from orders.models import (
Customer,
Order,
OrderItem
)

from orders.pricing import (
calculate_discount,
calculate_items_subtotal,
calculate_shipping,
calculate_tax
)

from orders.repository import (
OrderRepository
)

class OrderService:
"""Create and store customer orders."""

```
def __init__(
    self,
    repository: OrderRepository
) -> None:
    self._repository = repository

def create_order(
    self,
    order_id: int,
    customer: Customer,
    items: list[OrderItem]
) -> Order:
    """Create, calculate, and save a new order."""
    if not items:
        raise ValueError(
            "An order must contain at least one item."
        )

    if self._repository.find(order_id) is not None:
        raise ValueError(
            "Order identifier already exists."
        )

    items_subtotal = calculate_items_subtotal(
        items
    )

    discount = calculate_discount(
        items_subtotal,
        customer.is_member
    )

    discounted_subtotal = round(
        items_subtotal - discount,
        2
    )

    tax = calculate_tax(
        discounted_subtotal
    )

    shipping = calculate_shipping(
        discounted_subtotal
    )

    total = round(
        discounted_subtotal
        + tax
        + shipping,
        2
    )

    order = Order(
        order_id=order_id,
        customer=customer,
        items=items,
        subtotal=discounted_subtotal,
        discount=discount,
        tax=tax,
        shipping=shipping,
        total=total
    )

    self._repository.save(order)

    return order

File 5: src/orders/presentation.py

"""Functions for displaying orders."""
```

from orders.models import Order

def format_money(amount: float) -> str:
"""Return a currency-formatted value."""
return f"${amount:.2f}"

def create_receipt(order: Order) -> str:
"""Create a plain-text receipt for an order."""
lines = [
"ORDER RECEIPT",
"=" * 50,
f"Order ID: {order.order_id}",
f"Customer: {order.customer.name}",
f"Email: {order.customer.email}",
"-" * 50
]

```
for item in order.items:
    lines.append(
        f"{item.product.name:<20} "
        f"{item.quantity:>3} x "
        f"{format_money(item.product.unit_price):>8} "
        f"= {format_money(item.subtotal()):>8}"
    )

lines.extend(
    [
        "-" * 50,
        f"{'Discount':<35}{format_money(order.discount):>15}",
        f"{'Subtotal after discount':<35}{format_money(order.subtotal):>15}",
        f"{'Tax':<35}{format_money(order.tax):>15}",
        f"{'Shipping':<35}{format_money(order.shipping):>15}",
        f"{'Total':<35}{format_money(order.total):>15}"
    ]
)

return "\n".join(lines)

File 6: main.py

"""Run the sample order application."""
```

from orders.models import (
Customer,
OrderItem,
Product
)

from orders.presentation import create_receipt
from orders.repository import OrderRepository
from orders.service import OrderService

def main() -> None:
"""Create and display a sample order."""
pizza = Product(
name="Pizza",
unit_price=14.99
)

```
drink = Product(
    name="Drink",
    unit_price=2.50
)

items = [
    OrderItem(
        product=pizza,
        quantity=2
    ),
    OrderItem(
        product=drink,
        quantity=2
    )
]

customer = Customer(
    name="Sara",
    email="sara@example.com",
    is_member=True
)

repository = OrderRepository()
service = OrderService(repository)

order = service.create_order(
    order_id=1001,
    customer=customer,
    items=items
)

print(create_receipt(order))
```

if **name** == "**main**":
main()
```

Program Output

ORDER RECEIPT
```

==================================================
Order ID: 1001
Customer: Sara
Email: [sara@example.com](mailto:sara@example.com)
--------------------------------------------------

Pizza                  2 x   $14.99 =   $29.98
Drink                  2 x    $2.50 =    $5.00
----------------------------------------------

Discount                                  $3.50
Subtotal after discount                  $31.48
Tax                                       $4.09
Shipping                                  $5.00
Total                                     $40.57
```

File 7: tests/test_pricing.py

"""Tests for order pricing functions."""
```

import pytest

from orders.models import (
OrderItem,
Product
)

from orders.pricing import (
calculate_discount,
calculate_items_subtotal,
calculate_shipping,
calculate_tax
)

def test_items_subtotal() -> None:
items = [
OrderItem(
Product("Pizza", 14.99),
2
),
OrderItem(
Product("Drink", 2.50),
2
)
]

```
assert calculate_items_subtotal(items) == 34.98
```

@pytest.mark.parametrize(
"subtotal, is_member, expected",
[
(100, True, 10),
(100, False, 0),
(0, True, 0)
]
)
def test_discount(
subtotal: float,
is_member: bool,
expected: float
) -> None:
assert (
calculate_discount(
subtotal,
is_member
)
== expected
)

def test_tax() -> None:
assert calculate_tax(100) == 13

@pytest.mark.parametrize(
"subtotal, expected",
[
(39.99, 5),
(40, 0),
(100, 0)
]
)
def test_shipping(
subtotal: float,
expected: float
) -> None:
assert (
calculate_shipping(subtotal)
== expected
)
```

File 8: tests/test_service.py

"""Tests for the order service."""
```

import pytest

from orders.models import (
Customer,
OrderItem,
Product
)

from orders.repository import OrderRepository
from orders.service import OrderService

def create_service() -> OrderService:
"""Create a service with an empty repository."""
return OrderService(
OrderRepository()
)

def test_create_member_order() -> None:
service = create_service()

```
customer = Customer(
    "Sara",
    "sara@example.com",
    True
)

items = [
    OrderItem(
        Product("Pizza", 20),
        2
    )
]

order = service.create_order(
    1001,
    customer,
    items
)

assert order.discount == 4
assert order.subtotal == 36
assert order.tax == 4.68
assert order.shipping == 5
assert order.total == 45.68
```

def test_empty_order_is_rejected() -> None:
service = create_service()

```
customer = Customer(
    "Sara",
    "sara@example.com"
)

with pytest.raises(
    ValueError,
    match="at least one item"
):
    service.create_order(
        1001,
        customer,
        []
    )

File 9: pyproject.toml

[project]
```

name = "professional-order-project"
version = "1.0.0"
requires-python = ">=3.11"

[tool.black]
line-length = 88

[tool.ruff]
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I", "B"]

[tool.isort]
profile = "black"

[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]
```

File 10: requirements-dev.txt

black
```

isort
mypy
pre-commit
pytest
pytest-cov
ruff
```

File 11: .pre-commit-config.yaml

repos:
```

* repo: https://github.com/psf/black
  rev: 24.10.0
  hooks:

  * id: black

* repo: https://github.com/astral-sh/ruff-pre-commit
  rev: v0.8.0
  hooks:

  * id: ruff
    args: [--fix]
  * id: ruff-format

File 12: README.md

# Professional Order Project


## Description

A small Python order application demonstrating clean code,
professional project structure, testing, type hints, formatting,
linting, documentation, and refactoring.

## Install

python -m pip install -r requirements-dev.txt

## Run

python main.py

## Test

python -m pytest -v

## Coverage

python -m pytest --cov=orders --cov-report=term-missing

## Format

python -m black .
python -m isort .

## Lint

python -m ruff check .

## Type Check

python -m mypy src
```

How to Run the Project

  1. Create the complete folder structure.
  2. Create an empty __init__.py file inside src/orders.
  3. Copy each code section into its matching file.
  4. Open a terminal in the project root.
  5. Create a virtual environment with python -m venv .venv.
  6. Activate the virtual environment.
  7. Install tools with python -m pip install -r requirements-dev.txt.
  8. Run the application with python main.py.
  9. Run tests with python -m pytest -v.
  10. Format with python -m black ..
  11. Sort imports with python -m isort ..
  12. Check code with python -m ruff check ..
  13. Run type checking with python -m mypy src.
  14. Measure coverage with python -m pytest --cov=orders.
  15. Install hooks with python -m pre_commit install.

Refactoring Improvements

  • Unclear names were replaced with meaningful names.
  • Formatting was standardized.
  • Business rules became named constants.
  • Validation moved into domain models.
  • Pricing rules moved into focused functions.
  • Storage moved into a repository.
  • Application workflow moved into a service.
  • Receipt formatting moved into presentation code.
  • Global mutable state was removed.
  • Tests were added for important behavior.
  • Development tools were configured.
  • Project commands were documented.

Project Challenges

  • Add percentage discount codes.
  • Add fixed-value discount codes.
  • Add product categories.
  • Add customer loyalty points.
  • Add a JSON repository implementation.
  • Add CSV receipt export.
  • Add structured logging.
  • Add a protocol for repository implementations.
  • Add a payment-service abstraction.
  • Mock payment processing in tests.
  • Add complete type checking in strict mode.
  • Increase test coverage.
  • Reduce any new complex functions.
  • Add continuous integration configuration.
  • Create professional contribution guidelines.
```
Chapter 2: Development EnvironmentPrepare Your JavaScript Workspace

A modern course built to help learners study step by step with clarity, comfort, and confidence.