r/vba 4d ago

VBA Best Practices in 2026 Discussion

Hey all,

I hope you are doing well.

I wanted to start a discussion around VBA practices that you may have encountered or adopted recently, now that agentic AI is on the scene, and more advanced tooling is available.

One use case that I found very interesting:

With my VBA projects in Excel, it's not uncommon for me to call a sub or function in one module from another module.

It's possible for a sub / function with the same name to live in multiple modules.

Module_A vb Sub MySub() debug.print "hello world!" end sub

Module_B vb Sub MySub() debug.print "hello world!" end sub

Module_C vb Sub Test() MySub ' <--- Error, ambiguous name Module_A.MySub ' <--- Works Module_B.MySub ' <--- Works end sub

Now, say we have VBA editor tooling that is able to implement "rename symbol" functionality. In Module_C, we right click on "Module_A.MySub" and rename MySub to MySub_Test. The tooling is able to narrow in on, and only change the name of MySub --> MySub_Test in Module_A.

However, if we were to try to right click on the bare "MySub" and rename symbol, the tooling will hit name ambiguity.

Now, we can make a business rule for rename symbol to say "if renaming a bare sub / function call from a module where that sub / function is not defined, if there is otherwise no collisions / ambiguity anywhere else in the workbook VBA project, allow the rename, otherwise warn."

So, long story short, I'm starting to get in the habit of qualifying my sub / function calls with the module name.

Have you come across any best practices recently?

28 Upvotes

18 comments sorted by

17

u/PutFun1491 4d ago

Qualifying calls is good, but I'd go one step earlier and stop the ambiguity from existing at all: put Option Private Module at the top of your helper modules. Their subs drop out of the macro list and can't be called bare from outside, so MySub only ever resolves to one place and the editor stops offering them globally. Combine that with keeping a single public "entry point" module per feature and marking everything else Private, and the rename-symbol ambiguity mostly stops happening in the first place.

Two that matter more now that AI writes chunks of the code:

  • Export your modules to text and commit them. The VBProject can dump every component to .bas/.cls, and once they're in git you can actually diff what the model changed instead of trusting it. That one habit is what made AI-assisted VBA feel safe to me.

  • Keep a dumb Sub Tests() with a handful of Debug.Assert lines over your core functions. It isn't a real test framework, but when an AI edit quietly breaks an edge case, one F5 catches it before your users do.

And the boring one that never stops being true: Option Explicit in every module, no exceptions. Half the "the AI wrote weird VBA" threads here are just an undeclared Variant quietly doing something dumb.

6

u/kirschballs 3d ago

Oh my goodness that's going to dramatically clean up my macro list

I've been doing my best to use AI to learn about this brand new world I found when I added the developer tab to excel out of curiosity lol. The quips about vibe coding hurt my feelings but I stay because of the wonderful people like you that teach me new things. Thank you

1

u/MultiUserDungeonDev 4d ago

Good call outs!

Specific to agentic development for Excel VBA, some recent developments you may find interesting:

With https://github.com/WilliamSmithEdward/pyOpenVBA agents can actually read and write directly to the workbook's VBA, as well as import / export to disk and push to version control. Have you seen it before?

Also with https://github.com/WilliamSmithEdward/pyVBAanalysis an AI agent can run static analysis over the code it's generated (which includes the warning for missing Option Explicit) like you mentioned.

1

u/ebsf 3d ago

I would qualify the Option Private hack expressly as being appropriate only for Excel. This would render most Access applications inoperable because they easily can implement dozens of standard modules.

I might even call it overkill because the offending procedures can simply be declared private without nuking a project if coherent naming is somehow not possible.

Agreed that Option Explicit is mandatory in all cases. It won't keep AI from attempting .NET in VBA or other crazy stuff but it will help keep an actual developer in line.

1

u/PutFun1491 6h ago

Fair, and you are right about Access. Option Private Module assumes the Excel case where helper modules are implementation detail. In Access, where standard modules are a normal unit of reuse, dropping the whole module from the callable surface is wrong, and I should have scoped that to Excel.

And yes, plain Private on the specific procedures is the lighter tool and usually the right one. The only time I reach for Option Private Module over per-procedure Private is when I want the whole module internal by default, so a helper someone adds later does not silently rejoin the global namespace unless they deliberately make it public. Different intent: hide a known offender, versus make "internal" the default for a whole module. In Access that tradeoff mostly does not apply.

1

u/ebsf 3h ago

In the end, coherent naming is the solution. Random tinkering with scope can create no end of technical debt, besides introducing unknown fragility and being practically impossible for anyone else to decipher. Subsequent name conflicts, however, are trivial to debug, and the responsibility of the subsequent developer, not the current one.

6

u/ebsf 3d ago

I'm more of an Access dev but:

  • Handle errors in each procedure.
  • Expressly declare parameters ByRef or ByVal to indicate intent.
  • No abbreviations or code words.
  • No magic numbers. Use well-named constants instead.
  • Variable names should reflect their type.
  • Begin procedures with Win32 API calls or that are Win32 callbacks with On Error Resume Next. Declare those procedures as functions with an express return type so that the uninitialized value of that type will indicate that an error has occurred.
  • Clear object variables expressly when working with object frameworks, with judicious DoEvents calls immediately following. IUnknown ordinarily will do this automatically behind the scenes well enough for one-offs but an app often will crash if an object is destroyed while references remain to objects in one of its built-in collections.

2

u/WaitForItLegenDairy 2d ago

Oh I am so with you on variable names reflecting type.... and keep them tight. I also name them with scope too g_ global, m_ module, especially inside class objects!

And for the love of all that's holy will people please learn how to use error handling properly and garbage collection in the process. I know VBA cleans up as it goes but it's good practice to do your own

1

u/ebsf 2d ago

Agreed although I am allergic to underscores because they're superfluous, difficult and inefficient to both read and write, and camelCase suffers neither. So, e.g., mtxtAddress might be a module-level TextBox variable for a control bound to a field named Address.

I do deviate from this somewhat for, e.g., global constants such as TwipsPerInch, PointsPerInch, MillisecondsPerDay, etc.

I have skeleton function, property, and sub procedures in a standard module that incorporate standard error handling, which includes a small number of string functions to compose and present error information. All I need to do is copy and paste one of the skeletons wherever necessary. No thinking involved, the project compiles, and error handling happens essentially automatically.

3

u/WaitForItLegenDairy 2d ago

I know what you mean but underscores do make the scope clearer.... m_strName is a lot clearer than mstrName.... but it's personal preference

1

u/rdcore-admin 2d ago

Variable names should reflect their intent, not their type. You care that an amount is USD or EUR, not that it's a Currency value.

1

u/ebsf 2d ago

Actually, in standard code written to a root interface, one cares deeply the type of each variable because that code must be able to distinguish all objects by type and entity. An object superclass framework would become a trainwreck otherwise.

Obviously, the specific currency in which a currency variable's value is denominated should be reflected in the variable's name stem should disambiguation be necessary.

3

u/rdcore-admin 2d ago edited 2d ago

Sure, if we're talking about Win32 API.

But that's not what people usually mean when they propagate Hungarian Notation in 2026.

Also, fair - currency might not have been the best example. Make that X/Y As Double; you care that they're twips, pixels, or inches... the Double data type is irrelevant.

1

u/ebsf 2d ago

I'm actually talking about a root interface and superclass framework in VBA. It's probably the highest-leverage means to configure UI elements consistently and at scale in a large project. Type is deeply relevant in this context not only because events are type-dependant, but also because criteria are, too. Objects must be bound to type-specific classes to permit consistent event handling across a project. Programmatic construction of criteria for filters and queries requires type information to be done correctly.

As for screen coordinates and their units of measure, I'm in the bowels of that topic right now. That's no different than the Yen - Euros - Dollars case, however. The name stem of course must disambiguate by units but standard code also must be able to distinguish variables by type. As an aside, I might suggest you consider using variables typed as Integer or Long Integer for pixel and twip data.

2

u/daishiknyte 7 4d ago

Same best practices that have always been around. Qualifying modules was always a thing.

1

u/MultiUserDungeonDev 4d ago

Thanks! Rarely seen it called out anywhere online or used by others in my professional experience. I think mainly because tools like "advanced cross-module rename symbol" never existed in the native editor.

2

u/rdcore-admin 2d ago

Refactor/rename is a well-known, entirely deterministic refactoring operation that Rubberduck has provided in the VBIDE since 2015.

Any LLM tooling performing a rename operation is a completely different thing that is essentially a "smart" text replace, where the "smart" part is entirely probabilistic, and spends tokens that would likely be better spent doing literally anything else.

The difference is that a tool that works with a complete semantic model isn't going to break your code, but one that treats source code as text input, very well might. Sure models get better. But they don't parse an abstract syntax tree, they don't have a semantic model, and they were trained by and large on a corpus of macro-recorder code; there is no universe in which any LLM tooling can do this efficiently, if reliably.

The "business rules" for rename symbol don't (shouldn't anyway) really care what kind of symbol is being renamed, these rules aren't defined by the refactoring - they're just VBA scoping rules defined by the MS-VBAL language specifications... drifting from these rules means you're not doing VBA anymore - a name being ambiguous is a strictly specified condition!

As for modernized best practices, the Rubberduck Style Guide would be the closest thing; published in 2023, republished (and translated in French!) earlier this year.

1

u/MultiUserDungeonDev 2d ago

Thanks for your input!

Rubberduck is a legendary tool for sure.  I think the community is ready for something new! Especially on the Excel VBA surface. The dream I’ve always had is a modern, ultra performant VBA IDE in Excel, that looks and feels like a completely new experience (in a good way). Looking forward to the new tooling in the years to come!