view codeDoc/policies.txt @ 15:4608be19ebe2

Use OS paths (linux only for now), merging multiple paths. Init changes regarding options. Reorganised policies.txt a little. Implemented mde.resource.paths to read config from appropriate paths (currently linux only). Changed Init to load options before all other delegates are run and set logging level from options. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 14 Mar 2008 11:39:45 +0000
parents
children 5f90774ea1ef
line wrap: on
line source

--- Introduction ---
This is a collection of all coding policies for the mde engine as a whole. Policies for individual packages should be put in the individual package directory or elsewhere.

These are principles, not cast-iron rules, which I (Diggory Hardy) generally try to adhere to. If any other programmers have better principles to apply over these rules, they may do so for their own coding providing they have a good reason (i.e. not simply wanting to be a little different).

A warning: I have several times changed my mind about items I've written here. So don't expect all existing code to conform, and if you feel that a principle listed is not a good idea don't think you have to conform to it.



+++ CONTENTS +++

0   Introduction

1   Coding conventions

2   Documentation

3   Package design principle

4   Initialisation / cleanup

5   Testing

6   Logging

7   Exceptions

8   Translating strings



--- Coding conventions ---
Mostly stick to those provided in the D specification.

Indentation: Preferably indent with four spaces and align comments either with tabs or spaces. If you do use tabs, use a tab-width of 8.

Long lines: Aim to break long lines at around 100 chars (particularly with documentation); this isn't essential but provides a good guide and keeps text looking reasonable. With code, however, breaking lines doesn't always produce better-looking code so just try to keep it neat.

Identifiers: as for the D spec, use descriptive words for identifiers, although try to keep them from being overlong. Use capital letters to show separate words, not underscores. E.g.: file, readFile; not: rdFile, read_file, readFileUsingMyMethodNow. Use CAPS for consts (with underscores as word separators), Capitalisation for class/interface/struct/union names, smallLetters for variables and class instances.

Module/file names: If the module corresponds directly to a single class, use the class name with correct Capitalisation as the module name. Otherwise, preferably use lower case. Always use the correct case when importing a module to keep code portable.

Spelling (within code): I am not going to stipulate British/American/other spellings, but keep to good English and use some consistancy (e.g. don't use both "defense" and "instance" together, unless unavoidable due to existing code).



--- Documentation ---
Documentation falls in to the following four categories:

Source file comments: Code comments only aimed at yourself or other programmers.

Source file DDoc: Documentation of the general working of modules, function purposes, syntaxes and operations, enums, classes, etc. Often only for public items. Also mostly only relevant to programmers.

codeDoc directory: For large amounts of documentation on the general working/principle/whatever of a module/package or mde in general. Directory hierarchy should mirror that of the source directory and files should have appropriate names and extensions (.txt if plain text). The most important difference between the codeDoc and doc directories is that information in codeDoc is only aimed at programmers, wheras information in doc is aimed at all users.

doc directory: Documentation aimed at the end user covering building, running, modding (excluding by programming), etc. Files should be appropriately named with an appropriate extension (.txt for plain text). Documentation regarding modding should be in a "modding" subdirectory. Documentation regarding translating may also be added.



--- Package design principle ---
Use a separate package for each module of the engine. In most packages where there is only one module (file) imported by other parts of the engine, that module should have the same name as the package and be designed to have a standardised interface to the package so that the package could be replaced with another as a drop-in replacement (written with the same interface). Of course in many cases it may not be possible to swich one package for another quite this easily, but holding to this principle should at least minimise the amount of work necessary when doing so.

Module imports: don't use public imports much, although for importing interfaces it may make sense. Use static/renamed/selective imports when importing a module containing a lot of module-level symbols unless it is a very closely related module.

Chain dependancies: If at all possible, avoid chain dependancies (e.g. use interfaces). Chain dependancies can cause many problems.



--- Initialisation / cleanup ---
This can be done in two ways:

mde.Init: This class is designed to handle all but the simplist startup and cleanup code, and thus allow it to be threaded where appropriate and allow user feedback of loading progress.

static [~]this(): Use of static CTORs/DTORs should be limited to very small operations which can definitely be run safely at this stage.



--- Testing ---
Testing should, as far as reasonably possible, be done by unittests, defined either in the appropriate module or another module. Any modules containing unittests must be imported by test.mdeTest.

Unittests must be wrapped in "debug(mdeUnitTest)" statements. (These may also be used to wrap imports and "static this()" only needed by the unittest.)

Unittests (last block in the module where multiple unittest blocks are used) should end with:
    logger.info ("Unittest complete.");
No more logging should be needed, since if it fails whoever runs the unittest will know about it, and logging messages cannot be used to tell how complete the unittesting is. These messages just confirm that the unittests ran really.



--- Logging ---
Logging should be handled by tango's Logger class. A logger with the name of the form mde.package.module or mde.package.module.X where X is a symbol within the module should be used for each module.

In general the levels should be used as follows:
	Trace	Where required or thought highly useful for debugging, and only compiled in debugging mode.
	Info	Sparingly, for informational purposes (e.g. when parsing a file). Should not generally be used repetitively (within loops, etc.). Not for reporting unexpected behaviour.
	Warn	For small errors which can be overlooked, even if they MAY cause bigger problems later. I.e. something unexpected, but not necessarily a major problem, happens.
	Error	For errors which directly:
		• cut-short a (reasonably large) operation (e.g. reading a file).
		• cause a significant change in program operation, but do not directly cause the program to terminate.
	Fatal	For errors directly (i.e. definately and almost immediately) ending the program.

For all levels bar trace, messages should if possible be understandable to end users, while (for warn and above) including enough information to fix the problem when it is due to data files rather than code.
Thus:
	Trace output should only be available when compiled in debug mode.
	When run by an end-user (with info-level logging enabled),
	• info messages normally occur and should be understandable to end users;
	• warn messages may occur, and may or may not indicate problems;
	• error messages indicate that something big is wrong, and if the program still runs it is unlikely to be usable as intended;
	• fatal messages indicate a problem preventing the program from running.

Log/exception messages can be divided into two categories: those aimed at end users or modders and those only aimed at developers. A short string, either a brief English message or just a code, should be defined in the code, which can either be translated to a full message by I18nTranslation or output directly. The code string need not be a long description since it can be looked up in the code.



--- Exceptions ---
Thrown errors should, where documented, be documented with a log message; an exception message may be used to produce the final log message but must be output via a log message.

Thrown errors should use an exception class specific to at least the package involved to enable specific catching of errors. Exception classes should be defined within a module exception.d in the package directory. Exception classes should generally follow the conventions within mde/exception.d to aid in providing reasonable error messages.



--- Translating strings ---
User output (internationalization support): i18n.I18nTranslation is designed to fetch a full string and optionally an associated descripion given an identifier. The identifier may also be a code symbol, and should be brief, in English, and give at least some idea of its meaning, since if no translation is available the identifier will be output. For example:  "example message" or "exampleMessage", not "An example of a message". For any new entry created, at least one full entry should be added to the i18n database for some form of English so that:
    (a) there is something available to translate to other locales
    (b) English developers can understand what it means