TIBET Logo

TIBET Coding Standards

Coding Standards

wins

  • Minimize coding errors due to JavaScript idiosyncrasies.
  • Minimize errors from browser quirks or markup glitches.
  • Maximize use of tools to enforce chosen coding standards.
  • Write code maintainable by large-scale development teams.
  • Isolate TIBET and TIBET consumers from browser evolution.

contents

concepts

cookbook

JavaScript
XHTML
DOM
CSS

code


concepts

The great thing about the core web languages (JavaScript, CSS, HTML) is that they're dynamic, flexible languages with a lot of expressive power. The down side is they demand discipline if you're going to avoid some of the common bugs and inconsistencies they contain.

This document lists the coding standards we've arrived at over our almost 20 years developing software for the web, standards that helped TIBET survive almost two decades of browser and web standard evolution with minimal impact.

Reading these you might consider some of these archaic or unnecessary, and with some of them you'd be right with respect to modern browsers. Some of these rules arose from early work bringing TIBET to IE4 and Nav4 (Yes, it ran; Yes, it was crazy.). Since then they've helped us port TIBET to modern HTML5 browsers with minimal change. Simply put, they work.

Coding Sample

Sample TIBET Source


Like any set of coding standards ours will undoubtedly cause heated discussion and dissent. That's fine, they're our standards not yours, but if you "use the source" you'll see them in TIBET.

Ultimately your coding standard is the one your tools enforce. Here are ours.

JavaScript (ESLint)

We use eslint to enforce JavaScript standards. See TIBET's .eslintrc file for details.

JSON (require/JSON.parse)

The tibet lint command loads/parses JSON files to ensure they're well-formed.

XHTML/XML (xmllint, Xerces)

The tibet lint command checks all XML is well-formed. No validation is done.

CSS/Less (stylelint)

We run stylelint to keep our styles clean. See TIBET's .stylelintrc file for details.


cookbook

JavaScript

Bug Avoidance

This first section of standards is strongly recommended since they focus on habits that will help you avoid a number of common JavaScript coding traps.

JavaScript may look like C/C++ a bit, and it may share the 'Java' prefix with another popular language, but it is a unique language with its own unique requirements. Write JavaScript like it was some other language and you’ll pay the price.

Write fully-bracketed code.

One of the frequent errors when using C-style languages is adding statements to an if or while statement when that statement has not been fully bracketed. Always use fully bracketed code, even if there is only a single statement:

Do this:

if (foo.equalTo(bar))
{
    alert('Foo does indeed equal bar');
}

Not this:

//  DON'T DO THIS
if (foo.equalTo(bar))
    alert('Foo does indeed equal bar');

Terminate every line with a semi-colon.

JavaScript’s automatic semicolon insertion feature can cause hard-to-find bugs. We recommend authoring with intent and letting tools do the minification. Don't hand-minify your code at the risk of introducing bugs.

if (foo.equalTo(bar)) {
    alert('Foo does indeed equal bar');
 // <= Semicolon here.
}  // <= Not here (on blocks) if you're using modern JS linters.

var myFunc = function () {
  ...
};  // <= Here since this is an assignment statement.

Declare local variables explicitly.

Variables in JavaScript are scoped at either the function or global level. If a variable is declared it will be functionally scoped. Undeclared variable assignments create globals. Make sure your variables have function scope. Use var (or let if you're authoring in ES6`).

This:

var count = 1;

Not this:

//  CREATES A GLOBAL!
count = 1;

Your goal should be to have zero global variables leaking from your code.

Place 'var' statements at the top of each function.

One of the common misconceptions about JavaScript is that because its syntax is very C-like its behavior is as well. This is not the case. The following code does not do what most programmers from block-scoped languages expect:

var index = 1;

function myFunc() {
    alert('index: ' + index);   // undefined!
    for (var index = 0; index < myStuff.length; index++) {
        alert('blah blah #' + index);
    }
}

JavaScript's var has no concept of block scope, just function scope. In JavaScript the declaration of index in the for-loop is hoisted and occurs as if you'd declared it prior to the first alert() call.

Your code actually runs as if you'd written:

var index = 1;

function myFunc() {
    var index;
    for (index = 0; index < myStuff.length; index++) {
        alert('blah blah #' + index);
    }
}

Our recommendation is write the code the way the JavaScript engine will run it. Use let if you're style requires you to "inline" your variable declarations...the TIBET rule is use var and use it at the top of the function to make all declarations easy to spot.

Don't use parameters as LVALUEs.

Older browsers could exhibit serious problems when a non-null parameter value was used as an LVALUE. Historically TIBET code would always create a separate var for any parameter whose value could be altered during function execution. This rule has been loosened slightly over time, but it's still the way most TIBET code is authored.

This:

var myFunction = function (paramA, paramB) {
    var pA = paramA;    //  do this.
    if (pA != "foo") {
        pA = "foo"
    }
};

Not this:

//  DON'T DO THIS
var myFunction = function (paramA, paramB) {
    if (paramA != "foo") {
        paramA = "foo";     //  Throws an exception in some versions of IE.
    }
};

Use test functions for null, undefined, and isNaN.

JavaScript's automatic type conversions can cause problems when tests are done with non-Boolean values, values like [], 0, '', (but not {}) which resolve to false after Boolean type conversion. As a result tests for null and undefined often go astray.

JavaScript's isNaN function doesn't just return true for NaN, it also returns true for things like native constructors. That might be "accurate" in the sense that Date is "not a number" but it's not accurate if you were testing explicitly for NaN.

To assure conceptual clarity while avoiding bugs relating to automatic type conversions use TIBET's TP.isNull(), TP.notNull(), TP.isDefined(), TP.notDefined(), TP.isValid(), TP.notValid(), TP.isNaN() and similar encapsulation functions.

This:

if (TP.isValid(param)) {
    ...
}

Not this:

if (param === null || param === undefined) {
    ...
}

Don't test equality when identity will work.

Using == will trigger automatic type conversion in JavaScript, and the results are often unexpected. Use === whenever possible.

This:

if (x === 0) {
}

if (x === '') {
}

if (x === false) {
}

if (x === null) {
}

if (x === undefined) {
}

if (x === y) {
}

Not this:

if (x == 0) {
}

if (x == '') {
}

if (x == false) {
}

if (x == null) {
}

if (x == undefined) {
}

if (x == y) {
}

Use test functions for equality checks.

JavaScript's == and === operators don't always operate as you might expect.

In most OO languages an equality test works against an object's content. If two objects have the same value they are considered equal.

JavaScript does not carry this concept through to reference types (Arrays and Objects in particular). Your custom types will also fail to compare properly using the == operator.

TIBET provides TP.equal() and TP.identical() functions which leverage the objects themselves as part of the comparison. The result is that you can truly test for objects which are equal or identical based on your specific types.

This:

if (TP.equal(array1, array2)) {
  ...
}

Not this:

if (array1 == array2) {
  ... // This will never be invoked.
}

While using TP.identical() can be cumbersome (compared to ===) when working with instances of your custom types it does offer the opportunity for managing proxy objects which we've found to be invaluable in certain designs. Use it as needed.

Use single quotes for JavaScript strings.

HTML and XHTML use double quotes for attribute values. Since markup generation is a common task you should make it easier by using single quotes for JavaScript strings, reserving double quotes for embedded markup in those strings.

This:

var something = '<a href="#">Noop</a>';

var theString = 'Hi there.'

Not this:

var something = "<a href=\"#\">Noop</a>";

var theString = "Hi there.";

Never trust the browser: encapsulate.

We wish we could say calling native browser functions or accessing native properties directly was a solid, robust approach to client-side development... one that would stand the test of time. Just the opposite.

Many of the properties and functions you know and love turn out to have either outright bugs or limitations that become problematic faster than you think.

For example:

  • .getElementById() doesn’t work consistently across browsers regarding XML and HTML DOMs.
  • .getElementsByTagName() can’t handle XML namespaces consistently,
  • .getAttribute() returns different values on different browsers – and even within the same browser – depending on whether the attribute exists, is empty, or is valid,
  • typeof incorrectly misses certain function objects on older versions of IE.
  • properties accessible on one browser throw an exception if you try to access them in another browser.

Bottom line? Never trust the browser. Encapsulate everything.

A function call is the cheapest insurance policy you can buy to protect your code's future.

Run a linter.

Catching a missing var declaration, or a property assignment to a keyword you overlooked, or a window method (status is a common one), etc. can save you a lot of pain and suffering later.

Common JavaScript linting tools, such as ESLint, JSLint or JSHint can save a lot of time. TIBET's tools also have built-in linting tools, courtesy of ESLint. In fact, TIBET's project templates all have prebuilt ESLint configuration files that reflect TIBET's style.

Style & Format

Pick a standard and enforce it.

Programmers can argue about whitespace and code formatting all day long. These arguments are not particularly useful. What is useful is picking a single strategy and enforcing it via automated tools your team can't avoid using.

We highly recommend that coding standards be enforced 'organizationally' :-).

Write comments. Seriously.

TIBET code is commented as if the next developer to work on it will a) be less experienced with the project, b) have no access to the original authors, c) be under pressure to fix a bug. Kind of like your project's next developer may be ;).

We write comments and documentation accordingly. You should too.

Comment inside functions

TIBET is tooled for live, interactive, development. As a result we often reflect on objects and methods within a live running application. During development this application code isn't stripped of comments and whitespace for debugging purposes.

By placing your method comments inside the function they describe you make it possible to view the comment in any browser whenever you display the function instance.

Coding Sample

Sample Function Comment


TIBET's doclint command is also built to scan your application's live code and to extract method comments from the function body rather than assuming a comment is associated by proximity rather than containment.

Follow naming conventions.

Note, none of the conventions which follow are currently enforced, however future TIBET lint checks are planned which will allow you verify conformance.

Namespaces

Namespaces should be lowercase and descend from the top-level TP or APP global provided by the core TIBET framework.

This:

TP.lang
TP.sys
TP.meta
TP.{ns}

Not this:

TP.Foo
TP.BAR

NOTE: The TP global is reserved for use by the TIBET library For your projects, you should be using the APP global:

APP.mycorp.*
Types

Types should be named using CamelCaseNames with an initial capital letter.

Constants

Constants should be named using ALL_UPPERCASE names with underscores.

Attributes

If a property is private prefix it with a single dollar sign ($). Private means only the object/type in question should really be using that property or method. Subtypes should not access private variables.

If a property is protected prefix it with a single underscore (_). In TIBET, as with languages like Smalltalk, Ruby, or Objective-C, a protected variable is a variable that’s private outside of a type and its subtypes/descendants.

If a property is public no special character is used. This indicates that any consumer can access that property without concern for violating encapsulation rules. Even so, code should be using get and set, never direct property access.

TIBET further reserves a double dollar-sign ($$) prefix for internal variables. Use these at your own risk - they're never guaranteed between releases.

Methods/Functions

Assume the function had a Smalltalk or Objective-C syntax where each parameter is named as in:

myObject at: someIndex put: someValue.   "Smalltalk"
[myObject at: someIndex put: someValue]; // Objective-C

Separate the parameter names and values, preserving the order. Titlecase all parameter names other than the first and concatenate them, resulting in:

myObject.atPut(someIndex, someValue);   // TIBET

TIBET Specifics

Using get('foo') and set('foo').

TIBET development accesses object attributes by calling get() or set() rather than doing a direct slot access.

This:

var lname = employee.get('lastname');

Not this:

var lname = employee.lastname;

Likewise, if you're setting a value on an object you should invoke set():

This:

employee.set('lastname', 'Smith');

Not this:

employee.lastname = 'Smith';

In the former case TIBET's get() call will optimize access to the attribute while offering some level of encapsulation.

In the later case TIBET will ensure that the value is actually changing and if so will automatically send a Change signal to any observers.

Using get() vs. $get().

TIBET's get() and set() calls both take a first step of looking for a matching method which provides encapsulated access to the property in question.

In other words, if you call get('lastname') TIBET will check for a getLastname function and call it if it's found. If the method isn't found the get() routine will invoke $get(), a primitive slot access routine.

If you're implementing the getLastname routine itself you don't want to call get('lastname') or you'll create an endless recursion. Therefore getters you implement should always use the $get() call.

This:

Employee.Inst.defineMethod('getLastname', function() {
  return this.$get('lastname');
});

Not this:

Employee.Inst.defineMethod('getLastname', function() {
  // NO!! THIS WILL RECURSE!!!
  return this.get('lastname');
});

The same approach should be used for setters. Within a setter call $set() to perform the actual slot update while retaining change notification support.

Using get('lastname') vs. getLastname().

From a best-practice perspective TIBET coding standards suggest that you only call a fully-formed accessor method from within the type itself. This allows the type to leverage the accessor without imposing an API dependency.

If you're concerned about performance don't be. Firstly, function calls of this type are exceptionally fast. Secondly, a simple pass through a (future) pre-processor can easily expand get('foo') calls into getFoo() for production code deployment.

For more information about a variety of differences between regular JS and TIBET, see JavaScript: The TIBET Parts.

(X)HTML

TIBET is an XML platform in part so we can verify markup using XML tools including well-formed checks and XMLSchema if desired. Using XML also allows us to seamlessly integrate common XML formats like RSS, KML, XMPP, XBRL, and other business and government standards.

XHTML Standards

As mentioned earlier and elsewhere, TIBET is an XHTML5 platform, not an HTML5 one. That means our authoring standards for markup are grounded in writing proper XML.

The markup standards provided here should help you keep your markup-related issues to a minimum and your applications running faster and more efficiently.

Use an HTML5 DOCTYPE.

All HTML files should include a DOCTYPE which identifies their content accurately to the browser and ensures that you’re not running in "quirks mode". You should use HTML Strict DOCTYPE entries, unless you’re working around a bug that requires you to use the Transitional entry:

<!DOCTYPE html>

Use well-formed XHTML.

For your TIBET markup you must enforce an XHTML standard regarding markup structure. In other words, make your files well-formed XML with properly formatted XHTML.

In short:

  • element and attribute names should be lowercase,
  • attribute values should always be quoted,
  • non-empty elements require end tags: <foo>stuff</foo>,
  • empty elements also require end tags: <foo/> or <foo></foo> as needed.

Target Section 508 and other accessibility standards.

Craft your markup to be as accessible as possible – even if you’re not currently working toward compliance with a particular accessibility standard.

Use semantic markup.

There are almost 100 unique element types in the XHTML specification. Use the one that most closely matches the intended use. For example, use H1 thru H6 for heading-like content whenever possible, styling with CSS as needed.

Use markup with the right display mode.

If you are going to use a more generic element, such as a <div> or <span>, use the one with the display mode closest to your requirements. In other word, use <div> when you need block-level and <span> when you need inline rendering.

Use markup with the right data structure.

  • If you have tabular data by all means use a <table>.
  • If you have a list of values use a list of some form, either <ul> or <ol>.
  • If you have key/value pairs use <dl>, <dt>, <dd> or a semantic alternative.

Match the data structure of your model to markup suited to that structure.

Use a background div.

Experience shows it’s a valuable technique to put all of your page content in an enclosing div element rather than having multiple children on the body element. The extra control you get seems to far outweight the cost.

Use a <tbody> in your <table> elements.

Certain browsers exhibit subtle CSS bugs when you leave out the <tbody> in XHTML. If you’re going to use a <table> then use at least one <tbody> to contain its content.

Test your markup without CSS.

When laying out the structure of your page you should test early/often with little or no CSS, allowing the markup’s natural structure to render into the page. Your page should still be readable, even if it doesn’t lay out other than linearly down the page. This is not only a good test for keeping the markup semantically clear; it’s a great first-cut accessibility test.

Namespace-qualify your extensions.

If you’re going to extend the markup with additional elements or attributes be a good XML citizen and use a proper namespace declaration and qualifiers:

<html xmlns:mycorp="http://www.mycorp.com">
    ...
    <mycorp:customtag attr="a" />
    <span mycorp:customattr="a"/>
    ...

Use a well-formed checker.

Ensuring your markup is well-formed XHTML is key. Integrate a well-formed checker into your CI system or build process.

DOM

The XHTML you write defines a structure reflected in the Document Object Model or DOM. This structure is often a key player in the JavaScript you write, but failing to follow a few simple coding standards here can cause you untold hours of debugging.

Don’t assume a node is an element.

Perhaps the biggest source of bugs in DOM-related code comes from assuming that all nodes in the .childNode list for an element are themselves Elements. This is a dangerous assumption. As part of the HTML5 specification, the new .children Array containing only Element-type children is available. Use it or, better, TIBET-supplied methods like TP.nodeGetChildElements().

Different browsers will treat certain forms of whitespace differently, resulting in text nodes (#text) rather than the element node you expected. As a result you’ll end up with intermittent bugs across browsers.

Always check using a node’s .nodeType to ensure it's type 1 (Node.ELEMENT_NODE).

Don’t assume JS namespace handling is correct.

At the time of this writing XML namespace support in the various browsers was spotty and error prone. Moreover it is inconsistent between the major browsers.

While we recommend being a "good XML citizen" and using namespaces for your proprietary extensions to the XHTML you write, we recommend using wrapper functions to help ensure namespaces are properly dealt with.

Don’t manipulate the DOM while iterating over childNodes.

Certain DOM collections (e.g. the .childNodes nodelist for an element) are mutable and cause problems if you alter an element’s structure while iterating. The best approach is to either make a copy of the list or use traversal tied to a query such as .nextSibling.

Build UI in a non-windowed document and append once.

Performance is significantly higher when avoiding UI reflow due to constant .appendChild calls. When using the DOM to construct UI elements, build them offscreen in a document fragment created for that purpose and append once.

NOTE that in some cases it’s even faster to use .innerHTML, although that’s not officially a standardized DOM call. It is supported in most major browser platforms now, though.

Leverage XSLT.

Processing XML (or XHTML for that matter) is significantly faster using XSLT than native JavaScript. Whenever possible you should try to leverage XSLT, particularly for larger data sets. TIBET has full support for XSLT v1 built-in.

Encapsulate, encapsulate, encapsulate.

As mentioned in the section on JavaScript standards, experience has shown that encapsulation is your friend. This is particularly true around the W3C DOM APIs in modern browsers.

CSS

CSS Standards

While your XHTML may define the structure of your documents, it’s CSS that defines how it all looks and where different elements reside in the visual landscape of the page.

Use a CSS "reset" baseline.

For a number of reasons you should always include a CSS 'reset' which ensures that the values set by each user-agent stylesheet are normalized for your applications.

Don’t rely on traditional CSS "hacks/filters" based on bugs.

The first thing to do to help ensure you’re future-proofed in your CSS development is to stop using "hacks" relying on parser bugs or other issues in current browsers. Most of these hacks will break in modern browsers.

To give you control without relying on CSS parser bugs use a common CSS-compliant style sheet as your starting point, developing that sheet as you work in Mozilla, then load extra sheets as needed to deal with issues specific to other browsers.

The lines below can be placed in any HTML file and will ensure that all browsers share a common sheet, and then load their own "overrides" as needed.

<!--    the following three link entries give us a "future-proof" way
    of dealing with browser-specific style sheets -->

<!--    the shared sheet for all browsers comes first -->
<link id="std_css" rel="stylesheet" href="../css/std.css" type="text/css"/>

<!--    sheet for mozilla-specific tweaks, IE will ignore this because
        it will observe the disabled="true" setting here -->
<link id="moz_css" rel="stylesheet" href="../css/moz.css" type="text/css" disabled="true"/>

<link id="ie11_css" rel="stylesheet" href="../css/ie11.css" type="text/css"/>

Do not use inline style attributes.

As mentioned in the HTML standards section you should leverage class and id attributes. Don’t use the style attribute -- it’s bad form to inline style declarations. If you need something specific to an element, use an id selector ('#myElement') and put an id on the element.

AJAX techniques can be managed and maintained far more effectively by leveraging either attribute selectors (in supporting browsers) or by managing the class attribute on the fly.

Don’t default unit specifications.

If you are going to use specific unit designations always specify the unit.

This:

width: 10px;

Not this:

width: 10;

When the value is 0 you can leave off the units.

Use cellspacing="0" on <table> and CSS for the rest.

Use CSS to style your tables, but retain the cellspacing attribute to turn off extra spacing of cells. Don’t place width, border, cellpadding, or other attributes on your table elements - use their CSS equivalents.

Read great books on modern CSS3 techniques.

There are too many specific CSS tricks, traps, and workarounds to cover in this document, but reading great books on modern CSS design (remember that TIBET only runs in environments that are CSS3 compliant) while keeping the previous rules in mind – particularly those regarding hacks -- will give you the best foundation possible.


code

All code in TIBET conforms to the standard enforced by the tools leveraged by tibet lint. That said, a few representative files include:

  • ~lib/cmd/make/build.js (tibet make cmd)
  • ~lib/dna/default/plugins/proxy.js (TDS plugin)
  • ~lib/src/tibet/kernel/TIBETLogging.js (kernel file)

See TIBET's .eslintrc file for our current standard for JS.

See TIBET's .stylelintrc file for our current CSS rules.