Chapter 23 of 24 | Python

A project ladder

Projects combine language features into useful software. Start with a small, finished program, then improve its design, reliability, testing, and packaging. Each stage adds a new engineering challenge.

Stage 1: command-line tools

  1. Unit converter: validate values, separate calculations from input/output, and test known conversions.
  2. Text analyzer: stream a file line by line, count words, and define how punctuation and Unicode are handled.
  3. Quiz game: load questions from JSON, shuffle using random, and report a final score.

Stage 2: applications with persistence

  1. Task tracker: model task states, validate transitions, and save data with an explicit schema.
  2. Expense tracker: use Decimal or integer minor units, categorize entries, and produce summaries.
  3. Contact book: search and sort records, handle duplicate data, and support import/export.

Stage 3: reusable libraries and services

  1. Package a parser: define a stable API, robust error types, property tests, and versioned input formats.
  2. Build a small web service: use a maintained framework, validate requests, handle timeouts, and test endpoints.
  3. Analyze a data set: describe data provenance, missing values, assumptions, and limitations alongside the results.

Plan and deliver in increments

Write a problem statement, user stories, invalid inputs, and non-goals. Implement one end-to-end feature, add tests, and keep the application runnable. Separate domain logic from user interaction and storage so the core rules can be tested without a terminal, network, or filesystem.

For each project, include a README with purpose, supported Python versions, dependencies, setup, run, and test instructions. Provide sample input and expected output. State known limitations; honesty about scope is part of professional quality.

Master-level capstone: investigate a question

Choose a measurable question, such as whether streaming reduces peak memory for a workload or which parsing strategy scales better. Define a baseline, input distribution, metrics, and environment before running experiments. Preserve source, data-generation code, tool versions, and raw measurements. Explain threats to validity, such as cache effects or input bias.

Do not claim a general performance improvement from one machine or one timing. Separate observations from explanations, test correctness independently, and invite another person to reproduce the result.

Worked design slice: task tracker

Begin with a small model and a pure transition function. The user interface can call this function after validating input, and tests can check transitions without reading from a terminal.

from dataclasses import dataclass

@dataclass(frozen=True)
class Task:
    title: str
    done: bool = False

def complete(task):
    if not task.title.strip():
        raise ValueError("task title cannot be empty")
    return Task(title=task.title, done=True)

task = complete(Task("Write tests"))
assert task.done

This design makes the task immutable and returns a new state. A larger application might choose a mutable model, but the transition should still have a clear contract. Persisting tasks adds another boundary: parse external data, validate it, and only then construct domain objects.

Project review and maintainability

Before adding a feature, ask whether the current public interface is clear, whether the change breaks saved data, and how failure is reported. Use tests to capture compatibility expectations. Avoid adding abstractions for hypothetical future needs; add them when a concrete second use or a measurable constraint justifies the complexity.

A mature project also needs maintenance decisions: supported Python versions, dependency update cadence, issue triage, and a deprecation policy. The technical design should make it possible to repair and operate the software, not merely demonstrate a language feature.

Project checklist

  1. Define the problem and accepted input precisely.
  2. Choose data models, ownership, and error behavior.
  3. Automate tests and run them from a clean environment.
  4. Measure before optimizing and retain a reproducible baseline.
  5. Document setup, limitations, and design trade-offs.

Progression from first project to mastery

0% read