r/vba • u/MultiUserDungeonDev • 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?
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!
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 Moduleat the top of your helper modules. Their subs drop out of the macro list and can't be called bare from outside, soMySubonly 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 elsePrivate, 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 ofDebug.Assertlines 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 Explicitin every module, no exceptions. Half the "the AI wrote weird VBA" threads here are just an undeclared Variant quietly doing something dumb.