Skip to main content
Every Python project starts as a single file. That is completely normal. But as your script grows past a few dozen lines, a single file becomes hard to read, impossible to test, and painful to reuse. The fix is straightforward: identify the natural groups of responsibility in your code and move each group into its own file. This page shows you exactly how to do that, step by step.

Why split your code?

A well-organised codebase gives you three concrete advantages:
  • Readability — each file has one clear job, so a new reader (including future-you) can find relevant code instantly
  • Reusability — a calculate_totals function in its own module can be imported by multiple scripts without copy-pasting
  • Testability — small, focused functions with no side effects are trivial to unit-test; one giant script is nearly impossible to test reliably

Before: a single tangled script

Here is a realistic sales analysis script that does everything in one file. It works, but it is hard to maintain.
The problem: if you want to reuse the loading logic in a second script, you have to copy it. If a test fails, you have to wade through 80 lines to find the bug. If a colleague needs to change the output format, they risk breaking the data-processing logic by accident.

Identify the logical groups

Read through your script and label every line with its responsibility. In the example above, four groups emerge naturally:

After: four focused modules

Here is the project layout after refactoring:

data_loader.py — reads and parses raw data

data_processor.py — all calculation logic

reporter.py — formatting and output

main.py — the orchestrator

Notice how main.py reads like plain English — it tells you what happens without burying you in how. The implementation details live in the appropriate modules.

Making your folder a package with __init__.py

Add an __init__.py to sales_analysis/ to make it a proper Python package. You can leave it empty, or use it to expose a clean public API:
With this in place, other scripts can import directly from the package:

Importing between modules

Use absolute imports within your package. They always resolve correctly, regardless of where you run the code from.
Run the project from its root directory using the -m flag:

Writing a simple test after refactoring

Once your logic lives in focused functions, writing tests becomes trivial. Create tests/test_data_processor.py:
Run the tests:
Try testing the monolithic analyzer.py and you will immediately understand why modular code is worth the refactoring effort.
When you are not sure how to split a script, start with a single rule: one function does one thing, and one module groups related functions. If you cannot summarise a module’s job in one short sentence, it probably needs to be split further.

Project Workflow

Learn the day-to-day workflow for managing Python projects end to end