Python Comments — Why Deleting One Cost 4% Revenue
A deleted comment hid a tax calculation rule, causing 2 months of wrong discounts.
20+ years shipping production Python across data and backend systems. Drawn from code that ran under real load.
- ✓Basic programming fundamentals
- ✓A computer with internet access
- ✓Willingness to follow along with examples
- Comments start with # and are ignored by Python
- Use block comments on their own line, inline comments sparingly
- Python has no multi-line comment syntax — stack # lines
- Triple-quoted strings are not comments; they become docstrings at function/module start
- Only comment WHY, never WHAT — let clean code speak for itself
Imagine you're assembling flat-pack furniture and you scribble 'this screw goes in LAST or the drawer won't open' on the instruction sheet for the next person. That note doesn't change the furniture — it just helps whoever reads the instructions later. Python comments work exactly the same way: they're notes you leave inside your code that Python completely ignores when running the program, but that humans (including future-you) read to understand what's going on.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
Every line of code you write today is a mystery you'll need to solve tomorrow. That sounds dramatic, but ask any developer who's opened a file they wrote six months ago — without comments, even your own code can look like a foreign language. Comments are the single cheapest investment you can make to keep your code readable, maintainable, and friendly to every person who touches it after you.
Python runs fast. It processes your instructions line by line, but it skips comments entirely — they exist purely for people, not machines. This solves a real problem: code tells the computer WHAT to do, but it rarely explains WHY a decision was made, what a tricky block is trying to achieve, or what a number like 0.0875 actually represents (is that a tax rate? a discount? who knows). Comments fill that gap.
By the end of this article you'll know exactly how to write single-line comments, how to fake multi-line comments the Pythonic way, how to use docstrings to document functions properly, and — most importantly — when to write a comment versus when to just write cleaner code. You'll also see the mistakes that trip up beginners and the answers that impress interviewers.
Why a Comment Cost 4% Revenue — The Real Cost of Comments in Python
A Python comment is any line or string literal prefixed with # or enclosed in triple quotes (''' or """) that the interpreter ignores. Comments exist solely for human readers — they have zero runtime effect. But that doesn't mean they're free. Every comment is a maintenance liability: it must be updated when the code changes, or it becomes misinformation.
In practice, comments are parsed out during lexical analysis before the AST is built, so they add no overhead to execution. However, they do affect readability, code review time, and — critically — developer trust. A stale comment is worse than no comment: it actively misleads. The Python community follows PEP 8: inline comments are for clarification, block comments explain intent, and docstrings ("""...""") serve as module/class/function documentation accessible via help().
Use comments only when the code cannot be made self-documenting — i.e., when the why is non-obvious but the what is clear. In production systems, over-commenting is a red flag: it often masks unclear logic. The real cost is not runtime but cognitive load and technical debt. One misaligned comment in a payment pipeline once caused a team to misread a rounding rule, costing 4% revenue for three weeks.
round() (banker's rounding). New hire trusted comment, implemented downstream logic expecting truncation — caused 4% revenue loss over 3 weeks.Single-Line Comments — The Workhorse You'll Use Every Day
A single-line comment in Python starts with the hash symbol #. Everything after that # on the same line is ignored by Python. That's the whole rule. There's nothing to install, no special mode to activate — just type # and write your note.
You can put a comment on its own line above the code it describes, which is called a 'block comment'. Or you can put it at the end of a line of code, which is called an 'inline comment'. Both are valid, but block comments are usually easier to read because they don't crowd the code.
Here's the key habit to build early: comment the WHY, not the WHAT. If you write total_price = price + tax and then comment # adds price and tax, that comment adds zero value — anyone can see that. But if you comment # tax is added here because EU law requires it to appear in the final total, now you've explained something the code can't explain itself. That's what comments are for.
Multi-Line Comments — What Python Actually Does (and Doesn't Have)
Here's something that surprises beginners: Python has no official multi-line comment syntax. Languages like Java and JavaScript have / ... / block comments, but Python never added an equivalent. So how do Python developers write longer notes that span multiple lines?
The answer is simple — you just start each line with its own #. Stack as many lines as you need, each prefixed with a #. Python treats each one as a separate single-line comment, but visually they read as a block. This is the officially recommended approach in Python's own style guide (PEP 8).
You'll also see beginners use triple-quoted strings (''' or """) as a workaround for multi-line comments. A string that isn't assigned to a variable and isn't attached to a function is technically just a value Python evaluates and immediately throws away — so it behaves like a comment in practice. However, this is not a true comment, and there's one important exception: when a triple-quoted string appears as the very first statement inside a function, class, or module, it becomes a docstring — a documented description that tools and IDEs can actually read. More on that in the next section.
__doc__. If you use triple quotes as a 'comment' inside a function body, Python still evaluates that string — it's not truly ignored like a # comment is. Stick to # for comments and reserve """ for docstrings.Docstrings — Comments That Your Code Can Actually Read
A docstring is a special triple-quoted string placed as the very first statement in a function, class, or module. It's Python's official way of documenting what a piece of code does — and unlike a regular comment, Python stores the docstring and makes it accessible at runtime via the __doc__ attribute and the built-in function.help()
Think of a regular comment as a sticky note on your desk — helpful to you, invisible to everyone else. A docstring is like a label on a product — it's built into the thing itself and anyone can read it just by inspecting the object.
Docstrings follow a convention: the first line is a short, one-sentence summary of what the function does. If you need more detail, add a blank line, then a longer description, then document the parameters and return value. You don't have to memorise a rigid format right now — just know that the first line matters most and should be a clear, complete sentence.
For beginners, the most important takeaway is this: every function you write should have a docstring. It takes ten seconds, and it pays back tenfold.
# comment and a docstring. The answer that impresses: a comment is stripped from execution entirely and only exists for human readers, while a docstring is stored as the __doc__ attribute of the object and can be read programmatically at runtime by tools like help(), IDEs, and documentation generators like Sphinx.help() is your friend; docstrings make help() actually useful.Best Practices — When to Comment and When to Clean Code
Great developers don't write more comments — they write code that needs fewer comments. The best comment is a well-named variable, a clear function, or a small, focused method. But even the cleanest code can't express business context, historical decisions, or reasoning behind a non-obvious fix.
Here's the practical rule I use on every team: write a comment when the answer to 'Why is this here?' isn't obvious from reading the code alone. If you're explaining a workaround for a third-party bug, add a comment. If you're applying a formula that looks wrong but is correct for the business, add a comment. If you're adding a sleep() to avoid a race condition, for heaven's sake comment that — or it'll be removed in the next 'cleanup' PR.
Also: treat comments like code. They rot. A stale comment is worse than no comment because it actively misleads. During code review, ask the author if each comment is still accurate. Delete comments that describe what the code already says. Update comments when the logic changes.
- Every comment is a maintenance burden. When code changes, comments must change too.
- If a comment is missing, you waste time. If a comment is wrong, you waste even more.
- Invest in readable code first — the comment is the last resort.
- Team rule: during code review, challenge every comment. Is it still accurate? Is it needed?
Style and Conventions — PEP 8 and Team Standards
PEP 8, Python's official style guide, has clear rules for comments. Follow them and your team will read each other's code faster:
- Comments should be complete sentences. Start with a capital letter, end with a period.
- Use a space after
#. It's# commentnot#comment. - Block comments apply to the following code. Indent them to the same level.
- Inline comments use at least two spaces between the code and the
#. - Docstrings use triple double quotes
"""— triple single quotes'''are acceptable but less common.
Most importantly: keep comments relevant. A comment that describes how code works is noise when the code is clear. A comment that describes why the code exists is gold.
Teams that enforce these standards through linters (flake8, pylint) catch comment style violations automatically. It's a small win, but it keeps the codebase consistent.
When Your Own Code Betrays You — The Six-Month Rule
Every senior engineer has this scar. You write something smart at 2 AM, ship it, move on. Six months later, a bug report lands on your desk. You open the file and stare at a function called . What data? What processing? Why does it flip a boolean called process_data()_x? You wrote it, but you might as well be reading assembly. This isn't a memory problem. It's a commenting failure. Your brain optimizes for understanding the code you just wrote. It discards context aggressively. Comments are the lockbox for that context. Write them when you write the code, not after. Future you is not smarter than current you — current you is just closer to the problem. The rule: if you had to think for more than ten seconds to figure out why a line exists, annotate it. Your future self will thank you with fewer debugging sessions and fewer deployment rollbacks.
The Collaborative Codebase — Comments as Contracts
In production, your code is read by ten people for every one person who writes it. Reviewers, testers, on-call engineers, the guy who inherits your module after you quit. Every time you omit a comment, you force each of those people to reverse-engineer your intent. That's a tax on the entire team, compounded every time someone touches that file. Comments are not a courtesy. They are a contract. They tell the next developer: 'I knew this was weird, and I left this note so you don't make the same mistake I almost made.' That contract breaks when you write W.E.T. comments — We Enjoy Typing — that just repeat the code. A comment like # increment counter by 1 next to counter += 1 is noise. It trains readers to ignore comments. Write comments that capture the business rule, the edge case you dodged, or the reason a third-party library is being used in a non-obvious way. That's the signal. Everything else is just duplication.
Docstrings: Google, NumPy, and Sphinx Formats
Docstrings are the primary way to document Python code that is accessible at runtime. While the article covers basic docstrings, real-world projects often adopt standardized formats for consistency and automatic documentation generation. The three most popular formats are Google, NumPy, and Sphinx.
Google Style uses a simple, readable format with sections like Args, Returns, and Raises. It's widely adopted in modern Python projects.
```python def calculate_compound_interest(principal: float, rate: float, years: int) -> float: """Calculate compound interest.
Args: principal: Initial investment amount. rate: Annual interest rate (as decimal, e.g., 0.05 for 5%). years: Number of years the money is invested.
Returns: The final amount after compound interest. """ return principal (1 + rate) * years ```
NumPy Style is common in scientific computing and data science. It uses a more structured format with Parameters and Returns sections.
```python def calculate_compound_interest(principal: float, rate: float, years: int) -> float: """Calculate compound interest.
Parameters ---------- principal : float Initial investment amount. rate : float Annual interest rate (as decimal). years : int Number of years.
Returns ------- float Final amount after compound interest. """ return principal (1 + rate) * years ```
Sphinx Style uses reStructuredText and is the traditional format for Python projects. It's more verbose but integrates with Sphinx documentation generator.
```python def calculate_compound_interest(principal, rate, years): """Calculate compound interest.
:param principal: Initial investment amount. :type principal: float :param rate: Annual interest rate (as decimal). :type rate: float :param years: Number of years. :type years: int :returns: Final amount after compound interest. :rtype: float """ return principal (1 + rate) * years ```
Choosing a format depends on your team's preferences and tooling. Google style is often preferred for its readability, while NumPy is standard in scientific Python. Sphinx is legacy but still used in many established projects. The key is consistency across your codebase.
pydocstyle with a configuration file to enforce docstring conventions. This prevents documentation drift and ensures all public APIs are documented consistently.Type Comments vs Type Annotations: Migration Guide
Python 3.5 introduced type hints via annotations, but before that, type comments (e.g., # type: (int) -> str) were used to indicate types in a way compatible with Python 2 and older 3.x versions. While type annotations are now the standard, many legacy codebases still use type comments. Migrating to annotations improves readability and tooling support.
Type Comments (Legacy) ``python def add(a, b): # type: (int, int) -> int return a + b ``
Type Annotations (Modern) ``python def add(a: int, b: int) -> int: return a + b ``
Migration Steps: 1. Ensure your codebase targets Python 3.6+ (annotations are fully supported). 2. Use pyupgrade to automatically convert type comments to annotations. 3. For complex types (e.g., List[int]), replace # type: (List[int]) -> None with def func(items: List[int]) -> None:. 4. Update any type-checking configuration (e.g., mypy) to use annotations. 5. Remove # type: ignore comments that were workarounds for type comment limitations.
Handling Optional and Union Types ```python # Type comment from typing import Optional def get_name(user_id): # type: (int) -> Optional[str] ...
# Annotation def get_name(user_id: int) -> Optional[str]: ... ```
Migration Tools - pyupgrade: Automatically converts type comments to annotations. - mypy with --python-version 3.6+: Ensures annotations are used. - flake8-annotations: Lints for missing annotations.
After migration, you can benefit from better IDE support, static analysis, and reduced syntax clutter.
pyupgrade in your CI pipeline. This ensures new code uses annotations and legacy type comments are gradually phased out. Combine with mypy strict mode to enforce type safety.Self-Documenting Code: When Comments Are Redundant
Self-documenting code is code that is so clear and well-structured that it doesn't need comments to explain what it does. This is achieved through meaningful variable names, small functions, and clear logic. Comments should only explain why something is done, not what is done.
Bad Example (Comment Explaining What) ``python # Check if user is admin if user.role == 'admin': ``grant_access()
Good Example (Self-Documenting) ``python if user.is_admin: ``grant_access()
Principles of Self-Documenting Code: 1. Use descriptive names: is better than calculate_total_price(). 2. Small functions: Each function should do one thing. Name it accordingly. 3. Avoid magic numbers: Use constants like calc()MAX_RETRIES = 3 instead of if retries < 3. 4. Use type hints: They serve as documentation for expected types. 5. Write clear control flow: Avoid deeply nested conditions; use early returns.
When Comments Are Still Needed: - Complex business logic that isn't obvious from code. - Workarounds for external bugs or limitations. - Legal or regulatory requirements. - Performance optimizations that obscure intent.
Example of a Comment That Adds Value: ``python # We use a manual retry loop instead of a library because # the external API has a known race condition that the library doesn't handle. for attempt in range(MAX_RETRIES): try: data = ``fetch_data() break except TemporaryError: if attempt == MAX_RETRIES - 1: raise time.sleep(2 ** attempt)
Strive for self-documenting code as the default. Comments should be the exception, not the rule. This reduces maintenance burden and keeps the codebase clean.
pylint with naming conventions to encourage readability.The Silent Bug: Deleted Sales Due to a Removed Comment
- Never delete a comment unless you fully understand the business context behind it.
- If a comment explains a non-obvious rule, convert it into a test case first.
git log -S 'your_search_term' --all --onelinegit annotate <file> | grep <line>| File | Command / Code | Purpose |
|---|---|---|
| single_line_comments.py | item_price = 49.99 # Price of the item in US dollars | Single-Line Comments |
| multi_line_comments.py | def celsius_to_fahrenheit(celsius_temp): | Multi-Line Comments |
| docstrings_demo.py | def calculate_discount_price(original_price, discount_percent): | Docstrings |
| best_practices.py | active_users = get_users(order_by='created_at') | Best Practices |
| comment_style.py | def total_cost(items): | Style and Conventions |
| data_pipeline.py | ACTIVE_FLAG = not source_response.get('is_inactive', True) | When Your Own Code Betrays You |
| payment_gateway.py | def charge_customer(order_id: str, retry_count: int) -> dict: | The Collaborative Codebase |
| docstring_formats.py | def calculate_compound_interest(principal: float, rate: float, years: int) -> fl... | Docstrings |
| type_migration.py | def add(a, b): | Type Comments vs Type Annotations |
| self_documenting.py | if user.role == 'admin': | Self-Documenting Code |
Key takeaways
help(), IDEs, and documentation tools.Interview Questions on This Topic
What is the difference between a comment and a docstring in Python, and how does Python treat each one at runtime?
__doc__ attribute and can be accessed programmatically via help() or by reading __doc__ directly. Comments document individual lines; docstrings document the purpose and interface of an entire function or class.Frequently Asked Questions
20+ years shipping production Python across data and backend systems. Drawn from code that ran under real load.
That's Python Basics. Mark it forged?
9 min read · try the examples if you haven't