Programming Standards
A standardized and consistent code base is an important aspect of any programming project. The standards below ensure TradeSkillMaster's code is easy to read and consistent between developers. Much of the stuff on this page was based on the equivalent Auctioneer programming standards page, but has been rewritten / changed to fit TradeSkillMaster.
Contents |
Commenting
TradeSkillMaster contains tens (of not hundreds) of thousands of lines of code. The reasons for commenting code go beyond simply making it easier for other developers to understand. Often times, even code that you originally wrote may not make any sense when you look back at it a month later. Most importantly, commenting can be a very effective tool in debugging and improving your code since it forces you to study and analyze what you've written.
No developer likes to comment their code. They always think they will get around to it later, once the code is working. Don't fall into this trap. Comment your code as you write it (or better yet, during design time, before you write it).
A balance between commenting every line and one comment per 100 lines is needed. Any function of reasonable length / complexity should have some comments, but commenting every line of code is excessive. As a general rule, write a comment for any code that isn't immediately obvious by inspection or may benefit from context or justification.
If creating a public API, either as a global function or a function other modules might use through TSMAPI:GetData(), extra effort should be put into the comments to make it obvious what the parameters and return values are and explain any context / pre-conditions / edge cases as necessary.
Comments can be provided in block format, in-line, or at the tail end of a line of code - whichever pleases you.
Locals vs. Globals
Put simply, locals should always be used if possible. Globals should be avoided for the following reasons:
- They are inefficient. Looking up a global reference takes significantly longer than looking up a local one.
- They can be easily overwritten by other addons. Any addon (or in game script/macro) can access / edit your global, often without meaning to.
- There exists the possibility of tainting secure functions. If blizzard accidentally use a global with the same name, you can taint secure code paths and cause problems far beyond the normal scope of your addon.
- They make the code easier to maintain. Keeping your addon's data local prevents other addons from accessing them directly (and possibly inappropriately). Public APIs should always be created for sharing data / functions with other addons. Also, using locals forces you to organize your data and makes it easier to control where in the addon certain data / functions are used.
Minimize Variable Scope
Along with using locals instead of globals, ensuring you're declaring variables as close to where you're using them as possible is important to maintaining clean and readable code. This includes both declaring your variables in the same scope you use them as well as declaring them within as few lines of using them as is practical. For example, if you are only using a variable inside one branch of an if statement, declare the variable inside the if statement instead of before the if statement. Also, if variables are only used once and the code wouldn't become confusing by not using variables, they may be avoided completely.
Example:
-- AVOID local function Foo(a) local b = a % 2 -- lots of code not using 'a' or 'b' if something then foo2(b) end -- more code not using 'a' or 'b' end -- BETTER local function Foo(a) -- lots of code not using 'a' or 'a % 2' if something then local b = a % 2 foo2(b) end -- more code not using 'a' or 'a % 2' end -- ARGUABLY BEST (personal preference) local function Foo(a) -- lots of code not using 'a' or 'a % 2' if something then foo2(a % 2) end -- more code not using 'a' or 'a % 2' end
Variable / Function Naming
As with all programming languages, variables should have concise, but descriptive names. There is no standard on capitalization but generally variable names are camel case with their first letter lower case (ie "craftingCost"). In addition, function names usually have their first letter capitalized as well. Constants (and global variables) should be in all caps with words separated by an underscore (ie COPPER_PER_GOLD, TSMAPI).
Tab Spaces
For TradeSkillMaster, hard tabs (\t) are preferred to soft tabs (using spaces). The reason for this is that some developers use different tab space sizes than others (Sapu uses 3) and if they are replaced with spaces, they won't show up well for other developers. Generally, it's acceptable to leave long lines and not wrap them around to the next line, but if it is necessary to do so (usually for passing tables / anonymous functions), the following line should be indented an additional tab from the starting line. Otherwise, normal tabbing rules should be followed.
Example:
local function foo(a, b)
local c = {a, b}
sort(c, function(a,b)
if a.value == b.value then
return a.name < b.name
end
return a.value < b.value
end)
return c
end