├── .github └── FUNDING.yml ├── LICENSE └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.buymeacoffee.com/todar'] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Robert Todar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VBA Style Guide() 2 | 3 | Buy Me A Coffee 4 | 5 | *A mostly reasonable approach to VBA.* 6 | 7 | > Note: I will be adding to it as I can! 8 | 9 | ## Table of Contents 10 | 11 | 1. [Naming Conventions](#naming-conventions) 12 | 1. [Variables](#variables) 13 | 1. [Functions](#functions) 14 | 1. [Comments](#comments) 15 | 1. [Performance](#performance) 16 | 1. [Design](#design) 17 | 18 | ## Naming Conventions 19 | 20 | 21 | - [1.1](#single--letter--names) Avoid single letter names. Be descriptive with your naming. 22 | 23 | ```vb 24 | ' bad 25 | Public Function Q () 26 | Dim i as Long 27 | ' ... 28 | End Function 29 | 30 | ' good 31 | Public Function QueryMySQL () 32 | Dim recordIndex as Long 33 | ' ... 34 | End Function 35 | ``` 36 | 37 | 38 | - [1.2](#pascal--case) Use PascalCase as the default naming convention for anything global. 39 | ```vb 40 | ' good 41 | Public Function GreetUser () 42 | ' ... 43 | End Function 44 | ``` 45 | 46 | 47 | - [1.3](#camel--case) Use camelCase for parameters and local variables and functions. 48 | 49 | > [Microsofts convention](https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/program-structure/naming-conventions) is **PascalCase** for everything. The most important thing is to be consistent in whatever convention you use. 50 | ```vb 51 | ' good 52 | Private Function sayName (ByVal name as string) 53 | ' ... 54 | End Function 55 | ``` 56 | 57 | 58 | - [1.4](#underscore--case) Do not use underscore case. 59 | 60 | > Why? VBA uses underscores for pointing out events and implementation. In fact, you can't implement another class if the other class has any public methods or properties with an underscore in the name otherwise you will get the error [Bad interface for Implements: method has underscore in name](https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/bad-interface-for-implements-method-has-underscore-in-name). 61 | ```vb 62 | ' bad 63 | Dim first_name as String 64 | 65 | ' good 66 | Dim firstName as String 67 | ``` 68 | 69 | 70 | - [1.5](#system--hungarian) Do not use Systems Hungarian. 71 | 72 | > Why? These are useless prefixes that serve no purpose and can obscure the variables name. 73 | ```vb 74 | ' very bad 75 | Dim strString as String 76 | Dim oRange as Range 77 | 78 | ' bad 79 | Dim sName as String 80 | Dim rngData as Range 81 | Dim iCount as Integer 82 | 83 | ' good 84 | Dim firstName as String 85 | Dim queryData as Range 86 | Dim rowIndex as Integer 87 | ``` 88 | 89 | 90 | - [1.6](#abbreviations) Do not use abbreviations. 91 | ```vb 92 | ' bad 93 | Public Function GetWin() 94 | ' ... 95 | End Function 96 | 97 | ' good 98 | Public Function GetWindow() 99 | ' ... 100 | End Function 101 | ``` 102 | 103 | 104 | - [1.7](#descriptive--names) Be descriptive and use easily readable identifier names. Programming is more about reading code! 105 | ```vb 106 | ' very bad 107 | Dim x As Boolean 108 | 109 | ' bad 110 | Dim scrollableX As Boolean 111 | 112 | ' good 113 | Dim canScrollHorizontally As Boolean 114 | ``` 115 | 116 | **[⬆ back to top](#table-of-contents)** 117 | 118 | ## Variables 119 | 120 | 121 | - [2.1](#declare-variables-where-used") Declare and assign variables next to where they are going to be used, but place them in a reasonable place. 122 | 123 | > Why? This makes maintaining the code much easier. When you have a wall of declarations at the top of a procedure it is difficult to modify and refactor if needed. Also, you have to scroll up and down to see if a variable is used or not. 124 | ```vb 125 | ' bad 126 | Private Sub someMethod(ByVal path As String) 127 | Dim fileSystem As Object 128 | Dim folder As Object 129 | Dim files As Object 130 | Dim file As Object 131 | 132 | Set FSO = CreateObject("Scripting.FileSystemObject") 133 | Set folder = FSO.GetFolder(path) 134 | Set files = folder.Files 135 | 136 | For Each file In files 137 | '... 138 | Next 139 | End Sub 140 | 141 | ' good 142 | Private Sub someMethod(ByVal path As String) 143 | Dim FSO As Object 144 | Set FSO = CreateObject("Scripting.FileSystemObject") 145 | 146 | Dim folder As Object 147 | Set folder = FSO.GetFolder(path) 148 | 149 | Dim files As Object 150 | Set files = folder.Files 151 | 152 | Dim file As Object 153 | For Each file In files 154 | '... 155 | Next 156 | End Sub 157 | ``` 158 | 159 | 160 | - [2.2](#keep-variables-local) Prefer to keep variables local using the `Private` keyword. We want to avoid polluting the global namespace. Captain Planet warned us of that. 161 | ```vb 162 | ' bad 163 | Public Const FileName as string = "C:\" 164 | 165 | ' good 166 | Private Const fileName as string = "C:\" 167 | ``` 168 | 169 | 170 | - [2.3](#no-unused-variables) Disallow unused variables. 171 | 172 | > Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers. 173 | 174 | ```vb 175 | ' bad 176 | Dim someUnusedVariable as String 177 | 178 | ' good 179 | Dim message as string 180 | message = "I will be used!" 181 | Msgbox Messgae 182 | ``` 183 | 184 | 185 | - [2.4](#use-option-explicit) Use [`Option Explicit`](https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/option-explicit-statement) to ensure all variables are explicitly declared. 186 | 187 | ```vb 188 | ' good 189 | Option Explicit 190 | 191 | Sub doSomething() 192 | x = 1 ' Compile error: Variable not defined 193 | End Sub 194 | ``` 195 | 196 | 197 | - [2.5](#no-one-line-declarations) Use one `Dim` declaration per variable or assignment. 198 | 199 | > Why? It's easier to read and debug going back. It also prevents variables from accidentally being declared as Variants. 200 | 201 | ```vb 202 | ' very bad 203 | Dim lastRow, lastColumn As Long ' lastRow is a Variant, NOT a long 204 | 205 | ' bad 206 | Dim lastRow As Long, lastColumn As Long 207 | 208 | ' good 209 | Dim lastRow As Long 210 | Dim lastColumn As Long 211 | ``` 212 | 213 | 214 | - [2.6](#decalare-variable-types) Declare all variable types explicitly. 215 | 216 | ```vb 217 | ' bad 218 | Dim row 219 | Dim name 220 | Dim cell 221 | 222 | ' good 223 | Dim row As Long 224 | Dim name As String 225 | Dim cell As Range 226 | ``` 227 | 228 | **[⬆ back to top](#table-of-contents)** 229 | 230 | ## Functions 231 | 232 | 233 | - [3.1](#functions--mutate-params") Prefer `ByVal` for parameters. 234 | 235 | > Why? Reassigning and mutating parameters can lead to unexpected behavior and errors. `ByRef` is very helpful at times, but the general rule is to default to `ByVal`. 236 | 237 | ```vb 238 | ' bad 239 | Private Function doSomething(name As String) As String 240 | 241 | ' ok 242 | Private Function doSomething(ByRef outName As String) As Boolean 243 | 244 | ' good 245 | Private Function doSomething(ByVal name As String) As String 246 | ``` 247 | 248 | 249 | ## Comments 250 | 251 | 252 | - [4.1](#description-header-comment) Above the function should be a simple description of what the function does. Keep it simple. 253 | ```vb 254 | ' Adds new element(s) to an array (at the end) and returns the new array length. 255 | Function PushToArray(ByRef SourceArray As Variant, ParamArray Element() As Variant) As Long 256 | '... 257 | End Function 258 | ``` 259 | 260 | 261 | 262 | - [4.2](#doc--comment) Just above the function is where I will put important details. This could be the author, library references, notes, Ect. I've styled this to be similar to [JSDoc documentation](https://devdocs.io/jsdoc/). 263 | 264 | > I've decided to make this the same as JSDoc because there is no reason to recreate the wheel. This is a very known way of documenting and has plenty of documentation and examples already out there. This will keep your code consitent and easy for anyone to understand what is going on. 265 | 266 | ```vb 267 | '/** 268 | ' * Adds new element(s) to an array (at the end) and returns the new array length. 269 | ' * @author Robert Todar 270 | ' * @param {Array} SourceArray - can be either 1 or 2 dimensional array. 271 | ' * @param {...Variant} Element - Are the elements to be added. 272 | ' * @ref No Library references needed =) 273 | ' */ 274 | Function PushToArray(ByRef SourceArray As Variant, ParamArray Element() As Variant) As Long 275 | '... 276 | End Function 277 | ``` 278 | 279 | 280 | - [4.3](#descriptive--comment) Notes should be clear and full sentences. Explain anything that doesn't immediately make sense from the code. 281 | ```vb 282 | ' Need to check to make sure there are records to pull from. 283 | If rs.BOF Or rs.EOF Then 284 | Exit Function 285 | End If 286 | ``` 287 | 288 | 289 | - [4.4](#comment-newline) Add a newline above a comment when the previous code is on same indention level. Otherwise, don't have a line break. 290 | ```vb 291 | ' bad 292 | Private Sub doSomething() 293 | 294 | ' Different indention from line above, no need for newline above. 295 | Application.ScreenUpdating = False 296 | ' Same indention as previous code, must add a newline above to make it easy to read. 297 | Application.ScreenUpdating = True 298 | End Sub 299 | 300 | ' ok 301 | Private Sub doSomething() 302 | ' Different indention from line above, no new line. 303 | Application.ScreenUpdating = False 304 | 305 | ' Same indention as previous code, add a newline above. 306 | Application.ScreenUpdating = True 307 | End Sub 308 | ``` 309 | 310 | 311 | - [4.5](#actionitems) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you’re pointing out a problem that needs to be revisited, or if you’re suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME: -- need to figure this out` or `TODO: -- need to implement`. 312 | 313 | **[⬆ back to top](#table-of-contents)** 314 | 315 | ## Performance 316 | 317 | 318 | - [5.1](#avoid-using-select) Avoid using `Select` in Excel. See this [Stackoverflow Post](https://stackoverflow.com/q/10714251/8309643). 319 | 320 | > Why? It slows down code and also can cause runtime errors. `Select` should only be used for visual reasons such as the users next task is doing something in that specific cell. 321 | 322 | ```vb 323 | ' bad 324 | Range("A1").Select 325 | ActiveCell.Value = "Darth Vader" 326 | 327 | ' ok 328 | Dim cell As Range 329 | Set cell = ActiveSheet.ActiveCell 330 | cell.Value = "Lando Calrissian" 331 | 332 | ' good 333 | With Workbooks("Star Wars").Worksheets("Characters").Range("Hero") 334 | .Value = "Luke Skywalker" 335 | End With 336 | ``` 337 | 338 | **[⬆ back to top](#table-of-contents)** 339 | 340 | ## Design 341 | 342 | - Functions should be small and do only a single action (think lego blocks). 343 | - Functions should be pure — meaning they should not mutate outside state and always return the same value when given the same inputs. 344 | - Anytime there is a section of code that is separated by a giant comment block, ask yourself if this needs to get extracted into it's own function. 345 | 346 | **[⬆ back to top](#table-of-contents)** 347 | --------------------------------------------------------------------------------