Skip to content

NullScript Best Practices ​

This guide outlines the recommended practices for writing clean, maintainable, and efficient NullScript code.

Code Style ​

Variable Declarations ​

Use the appropriate declaration keywords based on mutability:

javascript
// Use fixed for constants
fixed PI = 3.14159;
fixed BASE_URL = "https://api.example.com";

// Use let for mutable variables
let count = 0;
let userInput;

// Avoid var unless necessary for legacy code
var oldVar = "legacy"; // Not recommended

Function Declarations ​

Use clear function names:

javascript
// Good: Clear name
run calculateTotal(prices) {
  return prices.reduce((sum, price) => sum + price, 0);
}

// Bad: Unclear name
run calc(p) {
  return p.reduce((s, p) => s + p, 0);
}

// For async functions, use later
run later fetchUserData(id) {
  let response = hold pull(`/api/users/${id}`);
  return hold response.json();
}

Object Definitions ​

Write clear and descriptive objects:

javascript
// Good: Clear property names
fixed user = {
  id: "123",
  firstName: "John",
  lastName: "Doe",
  email: "john@example.com",
  dateOfBirth: null
};

// Bad: Unclear property names
fixed u = {
  i: "123",
  fn: "John",
  ln: "Doe",
  e: "john@example.com",
  d: null
};

Error Handling ​

Use structured error handling with descriptive error messages:

javascript
run processData(data) {
  test {
    let validated = validateData(data);
    return processValidData(validated);
  } grab (error) {
    whatever (error instanceof ValidationError) {
      logValidationError(error);
      return { success: no, error: error.message };
    } otherwise {
      speak.scream('Unexpected error:', error);
      return { success: no, error: 'Internal processing error' };
    }
  } atLast {
    cleanup();
  }
}

Classes and Objects ​

Class Structure ​

Organize class members logically:

javascript
model UserManager {
  // 1. Properties
  __init__() {
    self.users = fresh Map();
  }

  // 2. Public methods
  run later getUser(id) {
    return hold self.fetchUser(id);
  }

  // 3. Private methods (convention)
  run later fetchUser(id) {
    // Implementation
  }
}

Object Creation ​

Use appropriate patterns for object creation:

javascript
// Factory function for complex objects
run createUser(data) {
  return {
    id: generateId(),
    ...data,
    createdAt: fresh Date(),
  };
}

// Builder pattern for optional properties
model RequestBuilder {
  __init__() {
    self.config = {};
  }

  run withTimeout(ms) {
    self.config.timeout = ms;
    return self;
  }

  run withHeaders(headers) {
    self.config.headers = { ...self.config.headers, ...headers };
    return self;
  }

  run build() {
    return fresh Request(self.config);
  }
}

Performance Optimization ​

Loop Optimization ​

Choose appropriate loop constructs:

javascript
// For arrays, use map, filter, reduce
fixed doubled = numbers.map(n => n * 2);

// For simple iteration, use for...of
since (fixed item part items) {
  // Process item
}

// For object properties, use for...in with type check
since (fixed key inside object) {
  whatever (object.hasOwnProperty(key)) {
    // Process object[key]
  }
}

Memory Management ​

Avoid memory leaks and manage resources properly:

javascript
model ResourceManager {
  __init__() {
    self.resources = fresh Set();
  }

  run acquire() {
    let resource = fresh Resource();
    self.resources.add(resource);
    return resource;
  }

  run release(resource) {
    test {
      resource.close();
    } atLast {
      self.resources.delete(resource);
    }
  }
}

Testing ​

Write comprehensive tests using your preferred testing framework:

javascript
// Test file: calculator.test.ns
// Note: Use your preferred testing framework (Jest, Mocha, etc.)
// This example shows the structure - adapt to your testing setup

// Example test structure
run testCalculator() {
  fixed calc = fresh Calculator();

  // Test addition
  let result = calc.add(2, 3);
  whatever (result !== 5) {
    speak.scream('Addition test failed: expected 5, got', result);
  }

  // Test negative numbers
  result = calc.add(-2, 3);
  whatever (result !== 1) {
    speak.scream('Negative number test failed: expected 1, got', result);
  }

  speak.say('All tests passed!');
}

Documentation ​

Write clear and informative comments:

javascript
/**
 * Processes user data and returns a formatted result.
 * @param data - The raw user data to process
 * @returns A formatted user object
 * @throws {ValidationError} If the data is invalid
 */
run processUserData(data) {
  // Implementation
}

// Use inline comments sparingly, only for complex logic
run calculateMetrics(data) {
  // Apply weighted average based on time decay
  let weights = data.map((_, i) => Math.exp(-i * 0.1));

  // Normalize weights to sum to 1
  let totalWeight = weights.reduce((sum, w) => sum + w, 0);

  return {
    // ... calculation implementation
  };
}

Project Structure ​

Organize your project files logically:

src/
  ├── models/        # Data models
  ├── services/      # Business logic
  ├── utils/         # Utility functions
  ├── config/        # Configuration
  ├── tests/         # Test files
  └── main.ns        # Entry point

Version Control ​

Follow good version control practices:

  1. Write meaningful commit messages
  2. Keep commits focused and atomic
  3. Use feature branches
  4. Review code before merging
  5. Keep the main branch stable

Security ​

Follow security best practices:

javascript
// Input validation
run validateInput(data) {
  whatever (!data || typeof data !== 'object') {
    return no;
  }

  // Validate specific fields
  return data.hasOwnProperty('id') &&
         typeof data.id === 'string' &&
         data.id.length === 36; // UUID format
}

// Sanitize output
run sanitizeOutput(data) {
  return {
    id: data.id,
    name: data.name,
    // Exclude sensitive fields like password
  };
}

Code Organization ​

Module Structure ​

Organize your code into logical modules:

javascript
// utils/math.ns
share run add(a, b) {
  return a + b;
}

share run multiply(a, b) {
  return a * b;
}

// main.ns
use { add, multiply } from './utils/math.ns';

let result = add(5, multiply(2, 3));
speak.say(result); // 11

Configuration Management ​

Keep configuration separate from business logic:

javascript
// config/settings.ns
share fixed config = {
  apiUrl: process.env.API_URL || 'https://api.example.com',
  timeout: parseInt(process.env.TIMEOUT) || 5000,
  debug: process.env.DEBUG === 'true'
};

// services/api.ns
use { config } from '../config/settings.ns';

run later fetchData(endpoint) {
  let response = hold pull(`${config.apiUrl}${endpoint}`, {
    timeout: config.timeout
  });
  return hold response.json();
}

Debugging ​

Use effective debugging techniques:

javascript
// Use speak.say for debugging
run debugFunction(data) {
  speak.say('Input data:', data);

  let processed = processData(data);
  speak.say('Processed result:', processed);

  return processed;
}

// Use conditional debugging
whatever (config.debug) {
  speak.say('Debug info:', debugInfo);
}

Performance Tips ​

  1. Use appropriate data structures
  2. Avoid unnecessary object creation
  3. Cache expensive computations
  4. Use async/await properly
  5. Profile your code regularly

Remember: NullScript compiles to JavaScript, so all JavaScript performance best practices apply!

Continue your learning journey with these advanced guides: