From 22fbff551dc4b3b74f2f3c9269bf60032c322cc0 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Tue, 24 Feb 2026 13:54:43 +0800 Subject: [PATCH 1/2] Add VBA best practices skill Co-Authored-By: Claude Sonnet 4.5 --- skills/vba-best-practices/SKILL.md | 114 +++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 skills/vba-best-practices/SKILL.md diff --git a/skills/vba-best-practices/SKILL.md b/skills/vba-best-practices/SKILL.md new file mode 100644 index 0000000..e191558 --- /dev/null +++ b/skills/vba-best-practices/SKILL.md @@ -0,0 +1,114 @@ +--- +name: vba-best-practices +description: VBA (Visual Basic for Applications) coding standards for Excel, Word, and Office automation. Trigger this skill when the user asks for help writing or reviewing VBA macros, wants to improve code quality, or shares VBA code asking "is this good?" or "how can I improve this?" +--- + +# VBA Best Practices + +## Code Structure + +**Always start every module with `Option Explicit`** — forces explicit variable declaration, catches typos at compile time. Enable globally via Tools > Options > Editor > "Require Variable Declaration". + +**Declare variables at the top of each procedure**, one per line, with descriptive names. Avoid single letters (`i`, `j`) and reserved words. + +**Naming conventions:** +- Subs/Functions → `PascalCase` +- Local variables → Hungarian prefix (`iCount`, `sName`, `bFlag`) +- Module-level → `PascalCase`; globals prefix with `g_` +- Constants → `ALL_CAPITALS`; helper functions prefix with `fn`/`fx` + +**Indent with tabs.** Everything inside loops, conditionals, and `With` blocks gets one level deeper. + +--- + +## Variables & Data Types + +- **Avoid `Variant`** — it's 16 bytes and slow. Use it only when the type is genuinely unknown or when passing arrays of mixed types. +- **Never use `Option Base 1`** — keep the default array base of 0. +- **Never use `Option Compare Text`** — keep string comparisons case-sensitive by default. +- **Use `vbNullString` instead of `""`** for empty string assignment; use `Len(s) = 0` to check emptiness — both are faster than comparing against `""`. +- **Use `vb` string constants** instead of `Chr()` calls: `vbTab`, `vbLf`, `vbCr`, `vbNewLine` (fastest), `vbNullChar`. + +--- + +## Parameters & Scope + +- **Always declare `ByVal` or `ByRef` explicitly** — VBA defaults to `ByRef`, which can cause silent side effects. Use `ByVal` unless you intentionally need to modify and return the argument. +- **Minimize global variables.** Use `Public`/`Private` at module level (not `Global`/bare `Dim`). If a global is unavoidable, prefix with `g_`. +- **Validate arguments** at the top of every Sub/Function before using them. + +--- + +## Error Handling + +**Never use `On Error Resume Next`** — it silently swallows bugs. Instead, add a custom error handler to every procedure: + +```vb +Sub DoSomething() + On Error GoTo ErrorHandler + ' ... code ... + Exit Sub +ErrorHandler: + Debug.Print "Error in DoSomething: " & Err.Description + ' Log, re-raise, or recover as appropriate +End Sub +``` + +Handle the error in the same procedure where it occurs. + +--- + +## Control Flow & Objects + +- **No `GoTo`** for program logic — use `If/ElseIf`, `Select Case`, and loops. (`GoTo ErrorHandler` is the one accepted exception.) +- **Use `With … End With`** whenever you reference the same object more than once — fewer dot-lookups means faster execution: + +```vb +With ws.Range("A1").Font + .Bold = True + .Size = 12 +End With +``` + +- **Never reference `ActiveCell`, `ActiveSheet`, or `ActiveWorkbook`** — always bind to explicit object variables: + +```vb +Dim ws As Worksheet +Set ws = ThisWorkbook.Sheets("Data") +ws.Range("A1").Value = "Hello" +``` + +- **Use built-in constants and enumerations** with full prefix (`VBA.vbMsgBoxResult.vbYes`, `msoFileType.msoFileTypeExcelWorkbooks`) for readability and IntelliSense support. Group related custom constants into `Enum` blocks. + +--- + +## Performance + +- **Bulk data via arrays** — read a range into an array, process in memory, write back in one shot. Never loop over individual cells for large datasets: + +```vb +Dim arData As Variant +arData = Sheet1.Range("A1:D500").Value ' one read +' ... process arData() in memory ... +Sheet1.Range("A1:D500").Value = arData ' one write +``` + +- **`ReDim Preserve` inside a loop is forbidden.** Oversize the array upfront; `ReDim` (without `Preserve`) once at the end if needed. +- **Minimize `Application.ScreenUpdating = False`** — use only when necessary, and always restore it (including inside error handlers). + +--- + +## Quick Checklist + +| Rule | Why it matters | +|---|---| +| `Option Explicit` in every module | Prevents undeclared-variable bugs | +| Descriptive names + Hungarian prefixes | Readability & self-documentation | +| `ByVal`/`ByRef` always explicit | No silent side effects | +| Custom `GoTo ErrorHandler`, not `Resume Next` | Errors surface instead of hiding | +| `With…End With` for repeated object refs | Speed + clarity | +| No `ActiveCell`/`ActiveSheet`/`ActiveWorkbook` | Robust against user selections | +| `vbNullString` / `Len()` for strings | Faster than `""` comparison | +| Arrays for bulk range I/O | Orders-of-magnitude speed gain | +| No `Option Base 1` or `Option Compare Text` | Predictable defaults | +| No `GoTo` except for error handler label | Structured, readable flow | From 3c073b73e2872380049c7c80048c85a85ac9c2ca Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Fri, 27 Feb 2026 13:37:20 +0800 Subject: [PATCH 2/2] Add guidance on duplicate Dim declarations in VBA Clarify that VBA uses procedure-level scope and declaring the same variable more than once causes compile errors. Include examples of correct and incorrect patterns. Co-Authored-By: Claude Sonnet 4.5 --- skills/vba-best-practices/SKILL.md | 59 +++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/skills/vba-best-practices/SKILL.md b/skills/vba-best-practices/SKILL.md index e191558..43468e2 100644 --- a/skills/vba-best-practices/SKILL.md +++ b/skills/vba-best-practices/SKILL.md @@ -29,6 +29,62 @@ description: VBA (Visual Basic for Applications) coding standards for Excel, Wo - **Use `vbNullString` instead of `""`** for empty string assignment; use `Len(s) = 0` to check emptiness — both are faster than comparing against `""`. - **Use `vb` string constants** instead of `Chr()` calls: `vbTab`, `vbLf`, `vbCr`, `vbNewLine` (fastest), `vbNullChar`. +### No Duplicate `Dim` Declarations Within a Procedure + +VBA uses **procedure-level scope** — a variable declared anywhere inside a Sub/Function is scoped to the entire procedure. Declaring the same variable name more than once (even in separate `If/Else` branches or nested loops) causes a **compile error**. + +**Rule: declare every variable exactly once, at the top of the procedure.** + +```vb +' ❌ Compile error — same name declared twice in different branches +Sub BadExample() + If condition Then + Dim x As Integer ' first declaration + x = 1 + Else + Dim x As Integer ' DUPLICATE — compiler rejects this + x = 2 + End If +End Sub + +' ✅ Correct — declared once at the top, assigned freely in any branch +Sub GoodExample() + Dim x As Integer ' single declaration + + If condition Then + x = 1 + Else + x = 2 + End If +End Sub +``` + +The same rule applies to loop variables reused across multiple `For Each` loops: + +```vb +' ❌ Compile error — rawMat declared inside two separate loops +For Each rawMat In rawMaterials ' implicit Dim inside loop + allRawMaterials.Add rawMat +Next rawMat + +For Each rawMat In allRawMaterials ' duplicate Dim — compile error + ' ... +Next rawMat + +' ✅ Correct — declare once at the top, reuse freely +Dim rawMat As Variant + +For Each rawMat In rawMaterials + allRawMaterials.Add rawMat +Next rawMat + +For Each rawMat In allRawMaterials + ' ... +Next rawMat +``` + +**Tip:** A quick way to catch duplicates is to search for `Dim ` within the same procedure. With `Option Explicit` enabled, the compiler will also surface this error on the first compile attempt. + --- ## Parameters & Scope @@ -104,6 +160,7 @@ Sheet1.Range("A1:D500").Value = arData ' one write |---|---| | `Option Explicit` in every module | Prevents undeclared-variable bugs | | Descriptive names + Hungarian prefixes | Readability & self-documentation | +| Each variable declared **once**, at procedure top | Duplicate `Dim` = compile error | | `ByVal`/`ByRef` always explicit | No silent side effects | | Custom `GoTo ErrorHandler`, not `Resume Next` | Errors surface instead of hiding | | `With…End With` for repeated object refs | Speed + clarity | @@ -111,4 +168,4 @@ Sheet1.Range("A1:D500").Value = arData ' one write | `vbNullString` / `Len()` for strings | Faster than `""` comparison | | Arrays for bulk range I/O | Orders-of-magnitude speed gain | | No `Option Base 1` or `Option Compare Text` | Predictable defaults | -| No `GoTo` except for error handler label | Structured, readable flow | +| No `GoTo` except for error handler label | Structured, readable flow | \ No newline at end of file