Skip to content

Refactoring and Clean Code

Glossary

105 terms from Refactoring and Clean Code, in plain words, each with the chapter that teaches it.

#

@pytest.mark.parametrize
A pytest decorator that runs one test function once for each row of inputs and expected values you give it.
__name__ guard
An `if __name__ == "__main__":` line that makes code run only when the file is run directly, not when another file imports it. It makes a script safe to import in tests.

A

alias (keeping an old name)
A second name that points to the same function, such as `run = print_statements`. Old callers keep working while you move to the new name.
argparse
A Python standard-library module for reading options and arguments from the command line. It can be used, for example, to let a statements script take the month as an option.
assert
A Python statement that checks something is true and fails the test if it isn't. A test with no assert can never catch a wrong result.

B

Behavior
Everything a program does that someone outside can see: what it prints, returns, writes to files and which errors it raises. Refactoring must leave it unchanged.
Blocking comment
A review comment about a problem that must be fixed before the change is merged. An example is the off-by-one `bags > 20` bug in the discount code.
boy scout rule
The habit of leaving code a little cleaner than you found it, for example by renaming one name each time you touch a file. It pays down technical debt in small steady steps.

C

C901
The ruff rule that reports functions whose cyclomatic complexity is above a limit. Its default limit is 10, and it can be lowered (for example to 5) to flag functions like Kettle & Crane's `run`.
capsys
A pytest fixture that captures what a test prints instead of showing it. `capsys.readouterr().out` returns the printed text as one string.
Change function declaration
Changing a function's name or parameters in safe steps. First add the new parameter with a default, then update the callers, then remove the default.
Characterization test
A test that records what old code does today, odd behavior and bugs included. It tells you if a refactoring changes anything.
Clean code
Code that is easy to read, change and test. It's the goal that refactoring works toward.
Code coverage
The share of lines that ran while the tests ran. It shows what isn't tested, but a covered line isn't necessarily checked by an assert.
Code review
Someone other than the author reads a change before it becomes part of the shared code. It catches bugs and spreads knowledge of the change.
Code smell
A sign in the code that something may be wrong underneath, such as vague names or duplication. It's a reason to look closer, not proof of a problem.
Coincidental duplication
Code that looks the same today but stands for different knowledge and changes for different reasons. An example is `PORTLAND_FEE = 15` and `BIG_ORDER_PERCENT = 15`, which should stay separate.
commented-out code
Old code switched off with `#`, such as `# print(data)`. Nobody knows if it still matters, so delete it and let version control remember it.
Common Python errors (NameError, KeyError, ImportError, AttributeError)
NameError: code uses a name that doesn't exist, e.g. after renaming `p` in only some places. KeyError: a dict key is missing (`DELIVERY["Eugene"]`, or an unknown SKU). ImportError: an import can't find a name, e.g. a removed public name like `run`. AttributeError: an object or module has no such name, e.g. a misspelled name given to `monkeypatch.setattr`.
context lines
The unchanged lines, starting with a space, that a diff shows around a change so you can see where you are.
CSV (comma-separated values)
A plain-text table where each line is a row and commas separate the columns, like `orders.csv`. Python's `csv.DictReader` turns each row into a dict keyed by column name, with every value as text.
Cyclomatic complexity
A count of the separate paths through a function: one, plus one for each `if`, `elif`, `for`, `while` and `except`. The old `run` function scores 12.

D

Data clumps
A smell where a few values always travel together, like `free_from` and `fee`. Fix it with Extract class or a parameter object.
dataclass
A Python decorator that writes `__init__` and other methods for a class that mostly holds values. With `frozen=True` the object is read-only, which suits a parameter object like `ReportOptions`.
Debt register
A short list of known technical debt kept where the team can see it. Each row gives the cost now, the cost to fix and when to do it.
Decimal
A Python number type for exact decimal arithmetic, which makes it a better fit for money than `float`. Switching to it changes how numbers are calculated, so it is best done as a separate change from a refactoring.
deep nesting
An `if` inside an `if` inside a loop, several levels deep, which is hard to follow. Guard clauses and extracted functions flatten it.
Deliberate / inadvertent debt
Technical debt taken on knowingly, such as shipping now and cleaning up later, versus debt created by accident because you didn't know better yet.
dependency injection
Passing in what code needs, such as a date or an email sender, instead of having the code create it. This lets a test supply fixed or fake versions.
DeprecationWarning
A Python warning category that tells callers a name or feature is outdated. Calling `warnings.warn(..., DeprecationWarning)` inside an old alias such as `run` makes its callers see the warning.
dict.get
A dict method that looks up a key and returns a default value if the key is missing. `DELIVERY.get(city, OTHER_CITIES)` avoids a KeyError for cities not in the table.
Diff
A view of the difference between two versions of a file. Lines starting with `-` were removed and lines starting with `+` were added.
Docstring
A string written as the first statement of a module, function or class. Python keeps it, so `help()` and editors can show it. PEP 257, Python's docstring guide, asks for triple double quotes and a one-line imperative summary.
doctest
A Python standard-library module that runs the examples written in docstrings and checks that they produce the shown results. It lets documentation examples double as tests.
DRY (Don't Repeat Yourself)
The principle that each piece of knowledge, such as a price or a rule, should live in exactly one place. It's about knowledge, not about lines that merely look alike; for example, three copies of a money-line layout become one `money_line` function.

E

early return
Leaving a function straight away with `return` when a special case applies. It's the mechanism behind guard clauses.
Edge case
An input at the boundary of a rule, such as exactly $300 or 20 bags, where bugs like `>` versus `>=` hide. Tests should pin the values just below and at each limit.
Extract class
Moving values that always travel together, and their behavior, into their own class. For example, `free_from` and `fee` moved into `Courier`.
Extract function / variable
Extract function moves lines that do one job into a new named function. Extract variable stores an expression in a named variable.

F

f-string
A Python string like `f"{label:<12}{amount:>11.2f}"` that inserts values. Codes after the colon set alignment and width: `<` left, `>` right, `.2f` two decimals.
F2 (Rename Symbol)
The VS Code command that renames a name everywhere it's used. Unlike find-and-replace, it understands which `t` is the variable.
feature envy
A smell where a function mostly uses another object's data, so it belongs on that object. Fix it with Move method.
fixture (pytest)
A helper that pytest hands to a test function by name, such as `capsys` or `monkeypatch`. It gives the test something it needs, like captured output.

G

Git
A version-control tool that records every change to your files and who made it. GitHub is a site that hosts Git projects and where pull requests happen.
global mutable state
Data that any code can change from anywhere, like the `data` list that `run` rewrites with the `global` keyword. It makes behavior hard to follow and test.
Golden master
One approved copy of a program's output saved in a file. A test checks that each run prints exactly that text, as with `golden_receipt.txt`.
Green / red
Green means all tests pass and red means at least one fails. In the safe loop you keep a change on green and undo it on red.
Guard clause
An `if` at the top of a function that handles a special case and returns at once. It flattens deep nesting, as in `shipping_cost`.

H

Hats
Kent Beck's idea that refactoring and adding a feature are different jobs. Wear one hat at a time so you know which change broke a test.
help()
A Python built-in that displays a function's or module's docstring and signature. Try `help(delivery_fee)` in the Python prompt.
Hotspot
Code that is both complex and changed often. That's where cleanup pays back fastest.
hunk header
The `@@ -1,5 +1,7 @@` line in a diff. It says which lines of the old and new file the changed area covers.

I

Inline variable / function
The opposite of extracting: replace a variable or function that adds no meaning with the expression or body it stands for.
Intention-revealing name
A name that says what a thing is for, so readers need no comment or search. `subtotal` reveals more than `t`.

J

journal comment
A comment that records who changed what and when, like `# 2021-03-02 JS: changed 0.1 to 0.15`. Version control already keeps that history, so delete it.

L

lambda
A short, unnamed Python function written on one line. In tests it is handy as a fake such as a `send_email` that records addresses in a list instead of sending.
Legacy code
Michael Feathers' definition: code without tests. Age doesn't matter, because without tests nobody can change it and know it still works.
legacy code change recipe
Five steps for changing untested code. Find the change point, find a test point, break the dependencies, pin the current behavior with tests, then change and refactor.
logging
Python's standard module for recording messages. `logging.basicConfig` makes `logging.info` messages show on screen, as in the wrap example.
long parameter list
A function with many parameters, which is easy to call in the wrong order. A parameter object is the usual fix.
Lookup table
A dict that maps each case to its settings, replacing branches that differ only in data. The `DELIVERY` dict holds each city's free-from amount and fee.
Lying comment
A comment that no longer matches the code, like `# 10% off` above tiers of 15% and 5%. Trust the code and fix or delete the comment.

M

magic numbers
Numbers (or strings) in code with no name, such as 20, 0.15 and 300. Naming them or moving them into tables shows what they mean.
Mixed jobs
A smell where one function reads data, does the sums and prints all together. Split phase separates these jobs into read, compute and print functions.
monkeypatch
A pytest fixture that replaces a name inside a module for one test, such as a fake `send_email`, and restores the original afterwards.
Move method
Moving a function onto the object whose data it mostly uses. `delivery_fee` moved onto `Courier` this way.
mutation testing
A technique that deliberately changes your code to see whether your tests notice. It's the automated version of breaking code on purpose to check a test goes red.

N

Naming habits
Common conventions readers rely on: nouns for values, verbs for functions, `is_`/`has_` questions for true/false values, plurals for collections, UPPER_CASE for constants and units in names. Python doesn't enforce them.
Nit
A small, optional review comment marked `nit:`, such as a naming preference. The author may ignore it.

P

Parameter object
One object, such as a frozen dataclass, that groups values passed around together. `ReportOptions` replaced a long parameter list.
Parameterize
Turn near-identical copies into one function by making the value that differs a parameter. `bags_of(rows, sku)` replaced separate espresso and decaf functions.
pathlib.Path
Python's way to work with file paths. `Path(__file__).parent` gives the folder of the current file, so tests find data files wherever pytest is started.
polymorphism (replace conditional with polymorphism)
Different objects answer the same method call in their own way, which replaces an `if` chain. `Courier` and `Pickup` each have their own `delivery_fee`.
property-based testing
A style of testing where a library such as Hypothesis generates many inputs to check that a rule always holds. It can find edge cases that hand-written examples miss.
Prudent / reckless debt
Technical debt taken on sensibly, like shipping for the fall sale and cleaning up next week, versus carelessly, like skipping tests with no plan.
Public name
A name other code may import or call, so renaming it can break callers you can't see. An internal name is used only inside its own file and is safe to rename.
Pull request
A request, on a site like GitHub, to merge your change into the main code once others have reviewed it.
Pure function
A function whose result depends only on its parameters and that changes nothing outside itself. `subtotal` and `shipping_cost` are pure, so they're easy to test.
pytest
A popular Python testing tool that finds and runs test functions and reports which pass or fail. `pytest -q` runs all tests with short output.
pytest-cov
A pytest plugin that measures code coverage. `--cov-report=term-missing` also lists the line numbers that never ran.

R

README
The file at the top of a project that people read first. It says what the project is, how to run it and how to test it.
Refactoring
Changing the structure of code without changing its behavior. Names, function sizes and where logic lives change, but the output stays the same.
Rewrite vs refactor
A full rewrite is tempting but usually runs late and brings back old bugs, because the old code holds many unwritten fixes. Prefer refactoring in steps, and replace a part only bit by bit.
ruff
A fast Python tool that finds likely mistakes (`ruff check`) and tidies layout (`ruff format`). `--select` picks rule groups and `--fix` applies safe fixes.
rule of three
A rule of thumb to wait for the third copy of some code before sharing it. This avoids merging things too early into the wrong abstraction.

S

safe loop
The refactoring routine: start green, make one small change, run the tests, then keep the change if green or undo it if red.
Safety net
A set of fast tests that checks the behavior you care about, so a mistake shows up seconds after you make it.
Seam
A place where you can change what code does without editing it there. Examples are a parameter with a default, like `today=None`, or `monkeypatch.setattr`.
Side effect
Anything a function does besides returning a value, such as printing, writing a file or changing a global. Keep these at the edge of the program.
SKU (stock keeping unit)
A product code such as `ESP-5LB` that identifies one item for sale. The statements script maps each SKU to its price.
SOLID
A set of five object-oriented design principles: single responsibility, open/closed, Liskov substitution, interface segregation and dependency inversion. They guide how to split code into classes that are easy to change.
Split loop / split phase
Split loop gives each job in a loop its own loop. Split phase breaks a function into stages, such as parse, compute and format, that each pass a result to the next.
Sprout function
Write new logic as a new, separately tested function and call it from the old code with a one-line change. `days_overdue` is an example.
Strangler fig
Replacing old code bit by bit. Build the new version beside the old one, move work over one piece at a time, and delete the old code when nothing uses it.
Strategy pattern
A design pattern where interchangeable objects each provide the same method, so behavior can be swapped without changing the code that uses them. Classes such as `Courier` and `Pickup`, which each offer the same method, work in a similar way.

T

Technical debt
Shortcuts in code that make every later change slower, like a loan. The interest is the extra time each change costs until you clean up.
TODO
A comment marking work left to do. Give it an owner and a reason, like `# TODO(Sam): add Eugene's rate once agreed`, or it will stay forever.
type hints
Notes in a function signature, such as `city: str` and `-> float`, that tell callers what to pass and get back. Tools can check them.

U

Unit test
A small, fast test that checks one function directly, such as the discount tiers or the Portland $300 delivery limit. Unit tests pin down individual rules and can sit beside a broader golden test of the whole output.
unsafe fix
A ruff fix that might change behavior, so ruff only applies it with `--fix --unsafe-fixes`. Check the result and run the tests.
UTF-8
A common way to store text as bytes that handles accented letters like the é in Café. Naming it when you open or read a file makes the text read the same on every system.

V

virtual environment
An isolated Python setup where you install tools like pytest and ruff with pip. It keeps one project's packages apart from another's.

W

with statement
A Python block, as in `with open(path) as file:`, that closes the file automatically when the block ends, even if an error occurs. The old script's `open(f)` without `with` never closes the file.
Wrap function
Rename the old function, then write a new one with the old name that adds behavior before or after calling it. Every caller gets the new behavior without being edited.
Wrong abstraction
A shared function that its callers have outgrown, kept alive with flag parameters. The fix is to inline it into its callers and share only what really is the same.