The first thing most people do when they hear 'Excel VBA' is open the Developer tab, click Record Macro, and assume they’ve just learned VBA. That’s like calling a hammer a carpentry course. You’re using one tool in a full workshop — and missing the entire blueprint.
The Setup
We’ll work with a real sales tracking sheet from AltaTech Solutions, a midsize SaaS reseller. Their team logs client renewals manually each Friday. The raw data lives in Sheet1, columns A through E, starting at A1:
| Client Name | Contract ID | Renewal Date | Amount ($) | Status |
|---|---|---|---|---|
| Sarah Chen | CT-7821 | 2024-03-15 | $45,200 | Pending |
| Marcus Lee | CT-7822 | 2024-03-18 | $12,950 | Overdue |
| Nina Patel | CT-7823 | 2024-03-22 | $31,400 | Pending |
| Diego Ruiz | CT-7824 | 2024-03-25 | $8,700 | Completed |
| Aisha Johnson | CT-7825 | 2024-03-27 | $24,100 | Pending |
| Kenji Tanaka | CT-7826 | 2024-03-29 | $18,300 | Overdue |
| Lena Dubois | CT-7827 | 2024-04-01 | $52,600 | Pending |
| Rajiv Mehta | CT-7828 | 2024-04-03 | $15,800 | Completed |
| Zara Kim | CT-7829 | 2024-04-05 | $9,400 | Overdue |
| Tariq Hassan | CT-7830 | 2024-04-08 | $37,200 | Pending |
The Challenge
You need to flag all Overdue contracts where the renewal date is more than 3 days past today — but only if the amount is over $10,000. And you need to auto-email the account manager listed in column F (which currently contains names like 'Maya Lin', 'Derek Wu', etc.).
Here’s why this isn’t a formula job: Formulas can’t send emails. They can’t loop through rows. They can’t check system dates dynamically and trigger actions. Conditional formatting won’t cut it either — it only changes appearance, not behavior. And the recorded macro? It hardcodes cell addresses like A1:E10. Run it next week on 15 new rows? It breaks.
VBA isn’t shorthand for 'macro'. It’s Visual Basic for Applications — a full object-oriented language with variables, loops, error handling, and integration with Windows APIs. That’s what ‘Excel VBA’ really means.
Walking Through It
Open the VBA editor with Alt + F11. Insert a new module (Insert → Module). Paste this stripped-down version — we’ll build it step by step:
Sub FlagAndEmailOverdue()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim i As Long
For i = 2 To lastRow
If ws.Cells(i, 5).Value = "Overdue" Then
If ws.Cells(i, 3).Value < Date - 3 And ws.Cells(i, 4).Value > 10000 Then
ws.Cells(i, 6).Value = "ACTION REQUIRED"
ws.Cells(i, 6).Interior.Color = RGB(255, 204, 204)
End If
End If
Next i
End Sub
Let’s break it down:
Dim ws As Worksheet: We declarewsas a worksheet variable — not a hardcoded reference. So if someone renames Sheet1 later, this still works.lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row: This finds the true last row in column A. Much safer than assuming data stops at row 10.If ws.Cells(i, 5).Value = "Overdue": Column E is Status — soi, 5reads rowi, column E. No “E2” or “E3” — just logic.
Run it with F5 while in the editor — or assign it to a button. Before and after:
Before (Column F is blank)
| Client Name | Status | Renewal Date | Amount ($) | Column F |
|---|---|---|---|---|
| Sarah Chen | Pending | 2024-03-15 | $45,200 | |
| Marcus Lee | Overdue | 2024-03-18 | $12,950 | |
| Zara Kim | Overdue | 2024-04-05 | $9,400 | |
| Tariq Hassan | Pending | 2024-04-08 | $37,200 |
After (Column F populated + highlighting)
| Client Name | Status | Renewal Date | Amount ($) | Column F |
|---|---|---|---|---|
| Sarah Chen | Pending | 2024-03-15 | $45,200 | |
| Marcus Lee | Overdue | 2024-03-18 | $12,950 | ACTION REQUIRED |
| Zara Kim | Overdue | 2024-04-05 | $9,400 | |
| Tariq Hassan | Pending | 2024-04-08 | $37,200 |
Notice how Zara Kim’s row stays unflagged? Her amount is $9,400 — under $10,000. The logic holds.
The Result
Here’s the final state of rows 2–11 after running the full version (including email logic — which uses CreateObject("Outlook.Application")):
| Client Name | Contract ID | Renewal Date | Amount ($) | Status | Action | Email Sent? |
|---|---|---|---|---|---|---|
| Sarah Chen | CT-7821 | 2024-03-15 | $45,200 | Pending | ||
| Marcus Lee | CT-7822 | 2024-03-18 | $12,950 | Overdue | ACTION REQUIRED | ✓ |
| Nina Patel | CT-7823 | 2024-03-22 | $31,400 | Pending | ||
| Kenji Tanaka | CT-7826 | 2024-03-29 | $18,300 | Overdue | ACTION REQUIRED | ✓ |
| Zara Kim | CT-7829 | 2024-04-05 | $9,400 | Overdue | ||
| Tariq Hassan | CT-7830 | 2024-04-08 | $37,200 | Pending |
This isn’t automation — it’s delegation. You told Excel *what* to decide and *how* to act. That’s VBA.
What Could Go Wrong
Three mistakes I’ve debugged in client files — all tied to misunderstanding what Excel VBA means:
1. Assuming Range("A1:E10") always means the same thing
You record a macro while data ends at row 10. Later, rows 11–25 get added. Your macro still only touches A1:E10 — silently ignoring new entries. Solution: Always use dynamic ranges like ws.Cells(ws.Rows.Count, "A").End(xlUp).Row.
2. Copying VBA code from forums without checking references
That slick Outlook email snippet? It fails if Outlook isn’t installed — or if the user’s Outlook profile is corrupted. Worse: it crashes silently unless you add On Error Resume Next (and then log the error). Trust me, I learned this the hard way — spent 3 hours chasing a blank screen before realizing the target PC had no mail client.
3. Putting logic inside Worksheet_Change instead of a button-triggered Sub
Some folks paste VBA into the sheet’s code pane thinking “it’ll run every time something changes.” But if your logic checks 500 rows on every keystroke? Excel freezes. Rule of thumb: Use event procedures sparingly. Put heavy lifting behind buttons or scheduled runs.
Ready to go deeper? Try this now:
| Next Step | How to Do It | Why It Matters |
|---|---|---|
| Add error handling | Wrap your For loop in On Error GoTo ErrHandler and add a label like ErrHandler: with MsgBox "Error " & Err.Number & ": " & Err.Description | Prevents silent failures — especially critical when emailing or saving files. |
| Test with real dates | Change your system clock to April 10, 2024. Run the macro. Verify Marcus Lee and Kenji Tanaka still trigger — but not Zara Kim. | Dates are relative. Hardcoding Date - 3 only works if you understand how Excel stores dates as serial numbers. |
| Move the logic to a class module | Right-click your VBA project → Insert → Class Module. Name it clsRenewalChecker. Paste the core logic there. Then call it from your button sub. | Makes future updates cleaner — and lets you reuse the same logic across other sheets or workbooks. |