Consistent code style makes SSJS easier to read, review, and debug. These conventions reflect community norms and practical experience.

Variables

Always use var. Never use let or const — they throw runtime errors in SSJS.

// CORRECT
var subscriberKey = Platform.Variable.GetValue("@subscriberKey");
var isLoggedIn = false;
var MAX_ROWS = 500;

// WRONG — will throw runtime errors (do not uncomment)
// let subscriberKey = "value";
// const MAX_ROWS = 500;

Use descriptive names. Avoid single-letter variables outside of short loops.

// OK for loops
for (var i = 0; i < rows.length; i++) { /* ... */ }

// Not OK for meaningful data
var x = Platform.Request.GetFormField("email");  // unclear
var email = Platform.Request.GetFormField("email");  // clear

Show test script
<script runat="server">
/*
 * Chapter: Variables
 * Proves:
 *   1. var works; MAX_ROWS convention.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var subscriberKey = "sk";
var isLoggedIn = false;
var MAX_ROWS = 500;
assert("var", subscriberKey, "sk");
assert("MAX_ROWS", MAX_ROWS, 500);
assert("bool", isLoggedIn ? "true" : "false", "false");
</script>

Functions

Use function declarations for utilities, function expressions for callbacks (when available).

// Named function declaration — preferred for utilities
function validateEmail(email) {
    return Platform.Function.IsEmailAddress(email);
}

Group related functions. Use the Revealing Module Pattern for namespacing:

var FormHandler = (function() {
    function validate(data) {
        if (!data.email) return false;
        if (!Platform.Function.IsEmailAddress(data.email)) return false;
        return true;
    }

    function process(data) {
        Platform.Function.InsertData("Leads",
            ["Email", "Name", "Source"],
            [data.email, data.name, data.source]
        );
    }

    return { validate: validate, process: process };
})();

Show test script
<script runat="server">
/*
 * Chapter: Functions
 * Proves:
 *   1. IsEmailAddress via declaration; module pattern return.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
Platform.Load("core", "1.1.5");
function validateEmail(email) { return Platform.Function.IsEmailAddress(email); }
assert("validateEmail", validateEmail("a@b.com") ? "true" : "false", "true");
var FormHandler = (function () {
    function validate(data) { return !!(data && data.email); }
    return { validate: validate };
})();
assert("module validate", FormHandler.validate({ email: "x" }) ? "true" : "false", "true");
</script>

Script Block Organization

Organize your script block in this order:

<script runat="server">
// 1. Platform.Load (if using Core)
Platform.Load("core", "1.1.5");

// 2. Constants and configuration
var PAGE_ID = 12345;
var DEBUG = Platform.Request.GetQueryStringParameter("debug") === "1";

// 3. Read request data once — String() converts the CLR value so === works
var method = String(Platform.Request.Method);
var rawBody = (method === "POST") ? Platform.Request.GetPostData() : "";

// 4. Helper functions
function getConfig(key) {
    return Platform.Function.Lookup("AppConfig", "value", "key", key);
}

// 5. Main logic
if (method === "GET") {
    // handle GET
} else if (method === "POST") {
    // handle POST
}
</script>

Show test script
<script runat="server">
/*
 * Chapter: Script Block Organization
 * Proves:
 *   1. Method coerce + Load order pattern.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
Platform.Load("core", "1.1.5");
var method = String(Platform.Request.Method);
assert("method GET", method, "GET");
</script>

Naming Conventions

Type Convention Example
Variables camelCase subscriberKey, isActive
Constants UPPER_SNAKE_CASE MAX_ROWS, API_BASE_URL
Functions camelCase formatDate(), validateInput()
Objects/modules PascalCase FormHandler, DataUtils
DE column access Match DE column name exactly rows[i].SubscriberKey

Show test script
<script runat="server">
/*
 * Chapter: Naming Conventions
 * Proves:
 *   1. camelCase + _prefix helper.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
function _formatName(first, last) { return first + " " + last; }
assert("_formatName", _formatName("A", "B"), "A B");
</script>

Indentation and Formatting

Use 4-space indentation (or 2-space consistently). Always use braces for if/for/while:

// GOOD
if (condition) {
    doSomething();
}

// BAD — no braces invite bugs
if (condition)
    doSomething();

Show test script
<script runat="server">
/*
 * Chapter: Indentation and Formatting
 * Proves:
 *   1. NON-ASSERTABLE: style preference.
 *   One-var-per-line still valid.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var a = 1;
var b = 2;
assert("sum", a + b, 3);
</script>

Comments

Write comments that explain why, not what:

// GOOD — explains non-obvious intent
// GetPostData() can only be called once per request — read immediately
var rawBody = Platform.Request.GetPostData();

// BAD — just narrates the code
// Get the post data
var rawBody = Platform.Request.GetPostData();

Document known bugs and workarounds:

// + "" keeps a non-string argument valid — ParseJSON throws on an object/array
var data = Platform.Function.ParseJSON(responseBody + "");

Show test script
<script runat="server">
/*
 * Chapter: Comments
 * Proves:
 *   1. Comments do not affect runtime.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
// explanatory
assert("runs", 1, 1);
</script>

Platform.Function Aliases

When using InsertData, UpdateData, UpsertData, DeleteData prefer the full names for clarity:

// Preferred — function intent is obvious
Platform.Function.InsertData("DE", ["col"], ["val"]);
Platform.Function.UpsertData("DE", ["key"], ["val"], ["col"], ["val"]);

// Also valid, just less descriptive
Platform.Function.InsertDE("DE", ["col"], ["val"]);

Show test script
<script runat="server">
/*
 * Chapter: Platform.Function Aliases
 * Proves:
 *   1. Platform.Function.Now without alias; Core Now after Load.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var n1 = Platform.Function.Now();
assert("Platform Now", n1 !== null && n1 !== undefined ? "ok" : "bad", "ok");
Platform.Load("core", "1.1.5");
assert("bare Now typeof", typeOfThunk(function () { return typeof Now; }), "function");
</script>

Error Handling

Always wrap external calls in try/catch:

var result = null;
try {
    var resp = req.send();
    result = Platform.Function.ParseJSON(String(resp.content) + "");
} catch(e) {
    // Log and handle gracefully
    logError("http_request", e.message);
    Write(Platform.Function.Stringify({ status: 502, statusMessage: "Bad Gateway", error: "External API unavailable" }));
    return;
}
Show test script
<script runat="server">
/*
 * Chapter: Error Handling
 * Proves:
 *   1. try/catch with call-form Error message.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var msg = "";
try { throw Error("fail"); } catch (e) { msg = "" + e.message; }
assert("msg", msg, "fail");
</script>

See Also