Development/Programming Standards

Från Norganna's AddOns

Hoppa till: navigering, sök

In an effort to standardize code and to maintain code readability, the team follows a set of guidelines when writing code. These guidelines are confirmed to be followed during Code Reviews. Violations of these standards qualify as a Minor Defect during the code review process, with defect code "Not conforming to standards." All Developers should remember these standards while developing and attempt to conform to them whenever possible. In the event that a standard cannot be followed, it can be ignored (preferably with comments indicating justification), but the issue should also be brought up to a Core Developer to re-evaluate the standard so it can be rewritten without exceptions.

Innehåll

Code Commenting

Norganna's AddOns are an open-source effort. We have had multitudes of developers come and go over the years. Our code has also been in use for over five years now - even the same developer can have trouble remembering the reasoning behind certain code designs five years later.

The solution: effective code commenting. We get it, developers never like 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).

We're not looking for War and Peace here, and please don't comment every line of code you write (including the obvious ones), but any time you do something that isn't clearly obvious by inspection, or any time there is relevant context you can provide which may help some other developer understand what you're doing or why you're doing it, just suck it up and write down some code comments.

When defining a public interface API, you should generally provide code comments (either at the top of the file, or just prior to the API definition) which indicate the purpose of the function, the parameters it requires, any return values, and any other relevant information about its use or purpose.

Comments can otherwise be provided in block format, in-line, or at the tail end of a line of code - we're not picky.

Locals vs. Globals

Always use locals whenever possible. Use of globals should be avoided whenever possible, due to:

  • Their inefficiency (looking up a global reference takes longer than looking up a local one)
  • Their potential to be overrun by other addons (any addon can take your global and arbitrarily change it, often without meaning to)
  • The possibility of tainting secure functions (sometimes Blizzard makes mistakes too, and if they accidentally use a global which we also use, we can taint secure code paths and cause problems far beyond the normal scope of our addons)
  • Better maintainability: keeping internal data structures local prevents the temptation of having other addons refer to them directly (and possibly inappropriately), prevents other addons from changing your internal data without your knowledge, and forces you to provide a clean, transparent public interface which you can more easily support and more reliably provide backwards compatibility with

Even when referring to a global created by other addons, it is often preferable to first create a local reference to that global, then refer to the local in your own code.

If you commit code to SVN which creates new globals, our ^nbot daemon will notice, and will generate a warning message about global pollution in the main #norganna IRC channel. Heed these warnings well, and correct the issue if it wasn't intentional. (Or explain why it was intentional to one of the core devs.)

Minimize Variable Scope

As a corollary to the above, variables should also be reduced to the lowest level block possible for their purpose. In other words, if a variable is only needed and used inside of a loop or other control structure, it should be declared inside of that block as well, and not "pre-declared" at a higher block level. This ensures the garbage collector can do its job efficiently, ensures that variables are protected from rogue code in other areas, and is overall simply cleaner.

Moreover, variables should always be declared (and initialized, if appropriate) as close to the implementation as possible. This reduces the amount of work spent trying to determine the origin of the variable in question.

Prefer:

function foo()
     -- Long sequence of code
     local a = 1;
     bar(a);
end


Instead of:

function foo()
     local a;
     -- Long sequence of code
     a = 1;
    bar(a);
end


Prefer:

function foo(i)
     if i > 5 then
          local n = 1;
          for x = 2, i do
               n = n * x;
          end
          return n;
     else
          local n = 0;
          for x = 1, i do
               n = n + x;
          end
          return n;
     end
end

Instead Of:

function foo(i)
     local n;
     if i > 5 then
          n = 1;
          for x = 2, i do
               n = n * x;
          end
    else
          n = 0;
          for x = 1, i do
              n = n + x;
          end
    end
    return n;
end
          

Variable Naming

At present, the team does not have any hard and fast rules about variable naming, you as the developer are free to use any naming convention you feel is appropriate. If possible, we prefer using descriptive english names, and avoiding overly jargony conventions or notations (e.g. Hungarian).

Obviously, you should do your best to make sure variable names are descriptive and unambiguous, to maximize the transparency of your code to other developers. When appropriate, feel free to use variable names which are standard shorthand (e.g. for k,v in pairs(...)).

Try to avoid using multiple variables with the same name at various levels of block scope. This generally just leads to confusion.

Constant Variables

Any code that contains constants should be declared as such. Containing magic numbers within code is detrimental to maintainability, so all constants should contain descriptive names about what those values represent, instead of hard-coding the numbers. This allows for (1) future developers to more quickly identify what the constant is supposed to represent, and (2) easy modification in the event the value needs to change in the future. Even values which are expected to never change (such as the number of seconds in a minute) should be pulled to a constant to allow for distinction between it and other similar values (such as the number of minutes in an hour).

Constants should always be in ALL_CAPS using underscores to represent spaces between words. Constants should always be locals following the convention specified earlier if not needed outside of the file in which they are declared, or in the module's table if they should be accessible elsewhere.

Avoid:

function numDays(secs)
   return secs / 86400
end

Better:

local SECONDS_IN_DAY = 24 * 60 * 60;
function numDays(secs)
   return secs / SECONDS_IN_DAY;
end

Best:

local HOURS_IN_DAY = 24
local MINUTES_IN_HOUR = 60;
local SECONDS_IN_MINUTE = 60;
local SECONDS_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR * SECONDS_IN_MINUTE;
function numDays(secs)
   return secs / SECONDS_IN_DAY;
end
   

Code Tabulation

All blocks should be tabulated in the normal fashion. Any code block (such as if...end, do...end, while...end, etc.) should have its contents tabbed out one indentation. Indentation form is not standardized, though spaces are preferred for consistency across editors.

Lines which continue on multiple lines should have their additional lines tabbed out an additional mark from the first line. In the event that this is being done due to a large parameter list, parameters and parenthesis pairs should be columnar-aligned.

Example:

function foo()
     if a == b then
          bar("Hello world",
              a,
              b,
              1
             );
          baz();
     else
          bar(c, d);
    end
end
Personliga verktyg