---
# System prepended metadata

title: Jam 03 - Exercise 3 - Scientific Debugging & Pattern Analysis
tags: [jam03, "3 \U0001F9EA in testing", scientific method, debugging, pattern analysis, "4 \U0001F973 done"]

---

---
title: "Jam 03 - Exercise 3 - Scientific Debugging & Pattern Analysis"
tags: 
  - 3 🧪 in testing
  - 4 🥳 done
  - jam03
  - debugging
  - pattern analysis
  - scientific method
---
<!-- markdownlint-disable line-length single-h1 no-inline-html -->
<!-- markdownlint-configure-file { "ul-indent": { "indent": 4 }, "link-fragments": {"ignore_case": true} } -->

{%hackmd dJZ5TulxSDKme-3fSY4Lbw %}

# Exercise 3: Scientific Debugging & Pattern Analysis

## Overview - Exercise 3

In this exercise, you'll learn how to apply scientific debugging principles to understand complex pattern matching behavior. You'll use the debugger to analyze how regex patterns work with different inputs and learn to choose the right debugging approach for different scenarios.

## Required Steps - Variable Analysis

### 1. Conditional Breakpoints

Sometimes you only want to pause execution when certain conditions are met. IntelliJ's conditional breakpoints are perfect for this:

1. Set a breakpoint by clicking in the gutter
2. Right-click the breakpoint (red circle)
3. In the dialog:
   - Add a condition (e.g., `input.contains("@bucknell.edu")`)
   - The breakpoint will only pause when this condition is true
4. For more advanced conditionals, select "More" or press `⌘⇧F8` (Mac) / `Ctrl+Shift+F8` (Windows) after right-clicking the breakpoint

This is especially useful when:

- Debugging specific test cases
- Looking for particular pattern matches
- Investigating edge cases

For example, to debug email patterns, you might set a condition like:
`testType.contains("email")` to pause only during email test cases.

![Conditional Breakpoints](https://hackmd.io/_uploads/HyVALaZYkg.png)

### 2. Watching Variables Change

If you have an active debugger session, let's stop it and set up for email analysis:

1. Click the red square Stop button to stop any current debug session if necessary
2. Set a conditional breakpoint at `String input = test[0];` with the condition: `testType.contains("email")`
3. Start debugging - the program will now pause only during email test cases
4. When the program stops, Step Over (F8) and watch the variables panel until you reach the line with `matches()`
5. Watch in the Variables window as you step over the line with `matches()`:
   - How does the Matcher object change?
   - What happens when you find a match?
   - Look at the `matcher` object in the Variables window - you can expand it to see all its properties
   - The `text` field shows the entire input string
   - The `groups` array (in the matcher object) shows the start and end positions of matches:
        - `groups[0]` and `groups[1]` show the start and end indices of the entire match
        - `groups[2]` and `groups[3]` show the start and end indices of the first capture group (if any)
        - If there were more capture groups, their indices would follow the same pattern

>[!Important] **Debug Task 1**
>Add to `answers.txt`:
>
>(Q3.1) When testing "<abc123@bucknell.edu>", expand the `matcher` object in the Variables window and find:
>
> - The entire input string (look at the `text` field, should be <abc123@bucknell.edu>)
> - The start and end positions of the first capture group (look in the `groups` array - it contains pairs of indices where each capture group starts and ends)
>
>(Q3.2) What happens if you try to call `group(1)` when there's no match? (change the code and then rerun the debugger)
>
>(Q3.3) Why do we check `matches` before trying to get `group(1)`?

### 3. Understanding find() vs matches()

The time pattern uses `find()` instead of `matches()`. Let's see why:

1. Click the red square Stop button to stop any current debug session if necessary
2. Then, modify your conditional breakpoint to use: `testType.contains("time")`
3. Start debugging again - now we'll pause only during time pattern tests
4. Step Over (F8) the `find()` call, watching the variables panel then seem how the `find()` call works for:
   - A simple time string "15:23:45"
   - A log message "[INFO] 15:23:45 Server started"

>[!Important] **Debug Task 2**
>Add to `answers.txt`:
>
>(Q3.4) When using `find()`, what part of the string gets matched in:
>
> - "15:23:45" (just the time)
> - "[INFO] 15:23:45 Server started" (a log message containing a time)

### 4. Scientific Method in Debugging

Now that you've seen both print debugging and the debugger in action, let's apply the scientific method to understand pattern matching behavior:

1. **Observation**: Notice a pattern's behavior
2. **Hypothesis**: Form a theory about why it behaves that way
3. **Experiment**: Use debugging tools to test your theory
4. **Analysis**: Examine the results and refine your understanding

Let's practice this with email validation:

1. **Observation**: Some valid-looking email addresses are rejected
2. **Hypothesis**: The pattern might be too strict about allowed characters
3. **Experiment**:
   - Add breakpoints before and after `matches()`
   - Try different usernames with special characters
   - Watch how the matcher processes each character
4. **Analysis**:
   - Look at which characters cause the match to fail
   - Check the matcher's state when it rejects input

>[!Important] **Debug Task 3**
>Add to `answers.txt`:
>
>(Q3.5) Using the scientific method, analyze why this email is invalid: 
> `"<first.last@bucknell.edu>"`
>
>- What was your initial hypothesis?
>- What did you observe in the debugger?
>- What was your conclusion?

### 5. Comparing Debugging Approaches

Throughout this exercise, you've used both print debugging and the debugger to understand regex pattern matching. Let's analyze the strengths and limitations of each approach:

#### Print Debugging

Print debugging is quick to implement and provides persistent output:

**Advantages:**

- Quick to add print statements
- Output remains in the console for review
- Works in any environment
- Great for logging in production
- Easy to share output with others

**Limitations:**

- Requires modifying source code
- Can't modify variables during execution
- No way to pause execution
- Must clean up debug statements
- Can make output hard to read

#### Using the Debugger

The debugger provides powerful interactive analysis tools:

**Advantages:**

- No source code modification needed
- Interactive variable inspection
- Can pause and control execution
- Ability to modify variables
- Advanced features like conditional breakpoints

**Limitations:**

- Takes time to learn effectively
- Only works in development environment
- State resets on each restart
- Can be slower for simple issues

#### Choosing the Right Approach

The best debugging approach depends on your specific situation:

1. Use print debugging when:
   - You need to log behavior in production
   - You're debugging in a remote environment
   - You want to share debug output with others
   - You need to track behavior over time

2. Use the debugger when:
   - You need to inspect complex object state
   - You want to step through code execution
   - You need to modify variables during execution
   - You're analyzing complex logic flows

>[!Important] **Debug Task 4**
>Based on your experience with both approaches, add your reflections to `answers.txt`:
>
>(Q3.6) Compare print debugging with using the debugger:
>
>- What kind of information would be easier to track with print statements vs. the debugger?
>- What debugging tasks are better suited for the debugger vs. print statements?
>- In what situations would you choose print debugging over using the debugger?
>
>Think about factors like:
>
>- Persistence of debugging information
>- Ease of adding/removing debug code
>- Ability to inspect program state
>- Debugging in different environments (local vs. production)

#### Error Handling Strategies

When debugging regex patterns, follow these error handling practices:

1. **Error Identification**:
   - Use the debugger to inspect the exact point of failure
   - Look for common regex issues (e.g., unescaped special characters)
   - Check for off-by-one errors in group indices

2. **Error Recovery**:
   - Always check if matches exist before accessing groups
   - Use try-catch blocks for potential regex exceptions
   - Provide meaningful error messages

3. **Error Prevention**:
   - Test patterns with edge cases
   - Document pattern limitations
   - Use named capture groups for clarity

> 🔍 **Checkpoint**: Before moving on, verify that:
>
> - You understand how capture groups work in regex patterns
> - You can explain the difference between `find()` and `matches()`
> - You know how to analyze pattern matching behavior using the debugger
> - You've completed all debug tasks in answers.txt

## Save Your Work - Exercise 3

Verify what files are uncommitted:

```bash
git status
```

Stage your changes:

```bash
git add src/main/java/jam03/answers.txt
```

Commit your work:

```bash
git commit -m "jam03: Complete scientific debugging exercises"
```

Your working directory should now be clean.

:::info
🔧 **Important**: If you still have a debugging session running, make sure to stop it before proceeding:

1. Click the red square Stop button in the debugger window or press `Cmd+F2` (Mac) / `Ctrl+F2` (Windows)
2. The debugger will close and your program will stop running

:::
