Python & Data Science

Why Programmers Freeze Before Writing Any Code An

Have you ever sat down to write code, opened your editor, and just… stared at the blank screen? You know exactly what the final program should do. You’ve solved similar problems before. But the first line of code feels impossible. Your cursor blinks at you, and your mind goes blank.

If that sounds familiar, here’s some good news: you’re not broken. This isn’t a lack of skill or intelligence. It’s a missing strategy. Most of us were never taught how to start — we were just told to “start coding.” And for anything non-trivial, that advice fails spectacularly.

This article is the first step in a series that will teach you a systematic way to break that paralysis. By the end of this piece, you’ll have a concrete, repeatable method to go from a blank page to your first line of code — without the panic.

The Blank-Page Freeze: A Story You Already Know

Let’s name the feeling. You have a task: “Clean this messy CSV and compute monthly revenue per customer.” You’ve done this before. You know pandas. You know how to group data. But somehow, opening a new file and typing the first import pandas as pd feels like a monumental step.

You’re not alone. On Reddit and Quora, programmers regularly ask: “Why do I freeze up when I go to write code?” The answers are telling. One user described it as “trying to hold the entire ocean in my head at once.” Another said, “I can explain the solution in plain English, but translating that into code feels like a different skill entirely.”

Jeff Atwood, in his famous Coding Horror post “Why Can’t Programmers… Program?”, observed that many programmers struggle with basic decomposition. They can’t break a simple problem — like “write a program that prints the numbers from 1 to 100, but for multiples of 3 print ‘Fizz’ instead” — into its logical steps. The issue isn’t syntax; it’s strategy.

So what’s actually going on here? The problem isn’t that you can’t code. The problem is that you’re trying to solve the entire problem at once, in your head, before writing a single line. That’s like trying to build a house by visualizing every nail and every board simultaneously. It’s overwhelming.

The fix? Stop trying to hold the whole problem in your head. Start with one sentence.

What Is Decomposition? (And Why You Already Do It)

Let’s step away from code for a moment. Think about planning a birthday party. You don’t just “do the party.” That’s too big. Instead, you break it down:

  • Pick a venue
  • Make a guest list
  • Plan the food
  • Buy decorations
  • Send invitations

Each of those is still a task, but it’s manageable. You can focus on the guest list without worrying about the cake flavor. That’s decomposition: breaking a big, scary task into smaller, manageable pieces.

In programming, it’s the same. You don’t “build the app.” You break it into “handle user input,” “process data,” “display results.” Each piece is small enough to think about clearly.

This is the hardest part for beginners: they think coding is about syntax — memorizing functions and keywords. But really, coding is about problem-solving strategy. Syntax is just the tool you use to express the solution you’ve already figured out.

In plain English: Decomposition is just a fancy word for ‘chunking’ a big problem into bite-sized pieces. You already do this in everyday life. You just need to do it deliberately when you code.

Now, here’s the interesting part: this idea has been around for over 50 years. In 1971, computer scientist Niklaus Wirth published a paper called “Program Development by Stepwise Refinement.” It’s one of the most influential papers in software engineering, and it contains the exact technique you need to cure blank-page paralysis.

The Blank-Page Fix: Level 0 of Stepwise Refinement

Wirth’s key insight is simple: every program starts as a single sentence. That sentence is Level 0. It’s not code. It’s not even pseudo-code. It’s just a plain English description of what the program does.

Let’s see what happens with our data-science task. Here’s Level 0:

Level 0: Read a CSV file and compute the average of a column.

That’s it. You’ve written something. The blank page is no longer blank. And because it’s not code yet, there’s no pressure to get syntax right. You’re just describing the goal.

Now you refine. Break that sentence into 2-3 sub-sentences. This is Level 1:

Level 1:

  1. Open the file
  2. Read the data
  3. Compute the average
  4. Print the result

Each of those is still a sentence, but they’re more specific. You can see the shape of the program emerging.

Keep going. Level 2 refines each Level 1 piece further:

Level 2:

  1. Open the file
    • Check if the file exists
    • Open it with the correct encoding
  2. Read the data
    • Parse the header row
    • Parse each data row into a list
  3. Compute the average
    • Sum all values in the target column
    • Divide by the number of rows
  4. Print the result
    • Format the output as a string
    • Print to console

Now each piece is small enough to code in one sitting. You’re not writing code yet — you’re writing a recipe. The code comes last.

Here’s what that looks like in Python, with comments that map back to the decomposition:

# Level 2, Step 1: Open the file
import os

file_path = "data.csv"
if not os.path.exists(file_path):
    print(f"Error: {file_path} not found")
    exit()

# Level 2, Step 2: Read the data
with open(file_path, "r") as f:
    lines = f.readlines()

# Parse header and data
header = lines[0].strip().split(",")
data_rows = [line.strip().split(",") for line in lines[1:]]

# Level 2, Step 3: Compute the average
# Assume the target column is the second column (index 1)
target_column_index = 1
values = [float(row[target_column_index]) for row in data_rows]
average = sum(values) / len(values)

# Level 2, Step 4: Print the result
print(f"The average of column '{header[target_column_index]}' is: {average:.2f}")

What this actually means: you didn’t start by writing import os. You started with a sentence. The code came naturally after you’d figured out the structure. The code is the last step, not the first.

Why ‘Just Start Coding’ Fails: The Cognitive Load Trap

Now let’s talk about why “just start coding” is such bad advice. It comes down to cognitive load — how much your brain can hold at once.

Your working memory can only hold about 4-7 things at a time. A non-trivial program has dozens of moving parts: the problem domain, the algorithm, the syntax, the edge cases, the data structures, the libraries you’re using. Trying to juggle all of that at once is like trying to keep a dozen plates spinning.

When you “just start coding,” you’re asking your brain to do all of this simultaneously:

  • Problem domain: What exactly are we trying to accomplish?
  • Algorithm: What steps will get us there?
  • Syntax: How do I write this in Python?
  • Edge cases: What if the file is empty? What if the column has missing values?
  • Data structures: Should I use a list? A dictionary? A DataFrame?
  • Libraries: Do I need pandas? Or can I do this with built-in functions?

That’s way more than 7 things. Your brain overloads, and you freeze.

Decomposition offloads that burden. Instead of holding everything at once, you focus on one small piece at a time. When you’re working on “open the file,” you don’t think about “compute the average.” You just think about file paths and error handling. Your cognitive load drops from “impossible” to “manageable.”

This is why experienced programmers seem to “just know” where to start. They’ve internalized decomposition patterns. They don’t think about it consciously anymore — but they’re still doing it. They start with one sentence, refine it, and only then write code.

In plain English: Your brain isn’t broken. You’re just asking it to do too much at once.

A Concrete Example: Decomposing a Data-Science Task

Let’s put this into practice with a real data-science problem. Imagine you have a CSV file of customer purchase data. It’s messy — some rows have missing values, and the dates are in inconsistent formats. Your task: clean the data and compute monthly revenue per customer.

Here’s the stepwise refinement:

Level 0: Clean the data and compute monthly revenue.

Level 1:

  1. Load the data
  2. Handle missing values
  3. Standardize date formats
  4. Group by customer and month
  5. Sum revenue

Level 2: For “Handle missing values”:

  • Drop rows with missing customer ID
  • Fill missing revenue with 0
  • Flag rows with missing dates for review

Level 3: For “Standardize date formats”:

  • Detect the current format (e.g., “MM/DD/YYYY” or “YYYY-MM-DD”)
  • Convert all to “YYYY-MM-DD”
  • Handle invalid dates by setting to NaT (Not a Time)

Now let’s write the code. Each function corresponds to a Level 2 or Level 3 sub-task:

import pandas as pd
from datetime import datetime

# Level 1, Step 1: Load the data
def load_data(file_path):
    """Load CSV data into a DataFrame."""
    df = pd.read_csv(file_path)
    print(f"Loaded {len(df)} rows from {file_path}")
    return df

# Level 2, Step 2: Handle missing values
def handle_missing_values(df):
    """Clean missing values in the dataset."""
    # Drop rows with missing customer ID
    initial_count = len(df)
    df = df.dropna(subset=["customer_id"])
    dropped_count = initial_count - len(df)
    print(f"Dropped {dropped_count} rows with missing customer ID")
    
    # Fill missing revenue with 0
    missing_revenue = df["revenue"].isna().sum()
    df["revenue"] = df["revenue"].fillna(0)
    print(f"Filled {missing_revenue} missing revenue values with 0")
    
    # Flag rows with missing dates
    missing_dates = df["purchase_date"].isna().sum()
    if missing_dates > 0:
        print(f"WARNING: {missing_dates} rows have missing dates — flagged for review")
        df["date_flag"] = df["purchase_date"].isna()
    
    return df

# Level 3, Step 3: Standardize date formats
def standardize_dates(df):
    """Convert all dates to YYYY-MM-DD format."""
    def parse_date(date_str):
        if pd.isna(date_str):
            return pd.NaT
        # Try common formats
        for fmt in ["%m/%d/%Y", "%Y-%m-%d", "%d-%m-%Y"]:
            try:
                return datetime.strptime(date_str, fmt)
            except ValueError:
                continue
        # If none work, return NaT
        print(f"Could not parse date: {date_str}")
        return pd.NaT
    
    df["purchase_date"] = df["purchase_date"].apply(parse_date)
    print(f"Standardized dates. {df['purchase_date'].isna().sum()} invalid dates set to NaT")
    return df

# Level 1, Steps 4 & 5: Group and compute
def compute_monthly_revenue(df):
    """Group by customer and month, sum revenue."""
    # Extract month from date
    df["month"] = df["purchase_date"].dt.to_period("M")
    
    # Group and sum
    monthly_revenue = df.groupby(["customer_id", "month"])["revenue"].sum().reset_index()
    print(f"Computed monthly revenue for {monthly_revenue['customer_id'].nunique()} customers")
    return monthly_revenue

# Main execution — this is Level 0 refined into code
def main():
    # Level 1, Step 1
    df = load_data("customer_purchases.csv")
    
    # Level 1, Step 2
    df = handle_missing_values(df)
    
    # Level 1, Step 3
    df = standardize_dates(df)
    
    # Level 1, Steps 4 & 5
    result = compute_monthly_revenue(df)
    
    # Display results
    print("\nTop 5 customers by monthly revenue:")
    print(result.head())
    print(f"\nTotal monthly revenue across all customers: ${result['revenue'].sum():,.2f}")

if __name__ == "__main__":
    main()

Let’s interpret the output. When you run this, you’ll see something like:

Loaded 1000 rows from customer_purchases.csv
Dropped 15 rows with missing customer ID
Filled 23 missing revenue values with 0
WARNING: 8 rows have missing dates — flagged for review
Standardized dates. 3 invalid dates set to NaT
Computed monthly revenue for 150 customers

Top 5 customers by monthly revenue:
   customer_id    month  revenue
0        10001  2023-01   450.50
1        10001  2023-02   320.00
2        10002  2023-01   890.75
3        10002  2023-02   210.30
4        10003  2023-01   675.00

Total monthly revenue across all customers: $45,230.80

What this means in plain English: Out of 1,000 rows, 15 had no customer ID (we dropped them), 23 had no revenue (we assumed 0),and8hadnodates(weflaggedthemforhumanreview).Afterstandardizingdates,3wereunparseable.Weendedupwithcleandatafor150customers,andthetopcustomer(10002)spentover0), and 8 had no dates (we flagged them for human review). After standardizing dates, 3 were unparseable. We ended up with clean data for 150 customers, and the top customer (10002) spent over 1,100 across two months. The total monthly revenue is about $$45,000.

Notice how each function maps directly to a Level 2 or Level 3 sub-task. The code is organized exactly like the recipe we wrote earlier. Decomposition made the code write itself.

What We’ve Learned and Where We’re Going

Let’s recap what you now know:

  • Decomposition is breaking a big problem into small, manageable pieces. You already do this in everyday life.
  • Stepwise refinement is the specific technique: start with one plain English sentence (Level 0), then refine it into sub-sentences (Level 1, Level 2…), until each piece is small enough to code.
  • Cognitive load explains why “just start coding” fails — your brain can’t hold the whole problem at once. Decomposition reduces that load.
  • The code comes last. You write the recipe first, then translate it into Python.

You now have a repeatable method: Level 0 sentence → Level 1 sub-tasks → Level 2 sub-sub-tasks → code. Next time you face a blank page, start with one sentence. Just one. The rest will follow.

This is Part 1 of a series. In Part 2, we’ll explore the Noun/Verb framework — a different way to decompose problems by identifying objects (nouns) and actions (verbs). Future parts will cover:

  • CRC Cards for object-oriented design
  • Test-First for test-driven development
  • Data-Flow diagrams for pipeline thinking
  • A special extension for converting mathematical formulas into code

Ready to never freeze at a blank page again? Let’s keep going — Part 2 is waiting.

Check Your Understanding

Remember: What is decomposition, in one sentence?

Understand: Explain why “just start coding” is bad advice for complex problems, using the concept of cognitive load.

Apply: Take the following task and write its Level 0 and Level 1 decomposition: “Download a weather API, parse the JSON response, and display the 5-day forecast.”

Analyze: Look at the code example in this article. How does the main() function reflect the Level 0 sentence? How do the helper functions reflect Level 1 and Level 2?

Evaluate: Compare the stepwise refinement approach to “just starting coding.” What are the strengths and weaknesses of each? When might “just start coding” actually be the better approach?

Create: Choose a programming task you’ve been avoiding (or one you recently struggled with). Write its decomposition from Level 0 down to Level 3, then implement it in code.

  • “Why Can’t Programmers… Program?” by Jeff Atwood — A classic post that highlights how many programmers struggle with decomposition, which this article directly addresses.
  • “Program Development by Stepwise Refinement” by Niklaus Wirth — The original 1971 paper that introduced the technique this article is built on.
  • “An Introduction to Decomposition” (arXiv:2204.09117) — A modern academic perspective on decomposition that complements the practical approach here.

Apply What You Learned is for Supporter and Insider subscribers.

Subscribe to unlock the exercises on this post.

See plans

Looking for something else?

Search every article by title, summary or topic.