‹ All interview questions

Core Concepts

In ASP.NET, you separate concerns with Controllers, Services, and Repositories.

The question

In ASP.NET, you separate concerns with Controllers, Services, and Repositories. In ServiceNow, there's something called 'Script Includes' that people say is like a reusable class library. But then there's also 'GlideAjax' which apparently bridges client and server. Explain the ServiceNow layered architecture to someone who thinks in MVC.

The Intent Behind This Question

This tests whether you can map ServiceNow's server-client architecture to the layered patterns you've built in .NET. The key insight: ServiceNow's UI is server-rendered (think classic ASP.NET Web Forms / Razor Pages), not a detached SPA. The client-server boundary is explicit and enforced.

The Architecture Map

// ASP.NET MVC Architecture (what you know)
//
// View (.cshtml)          → Client Scripts + UI Policies
// Controller              → Business Rules (server triggers)
// Service Layer           → Script Includes (reusable classes)
// Repository / DbContext  → GlideRecord (data access)
// AJAX calls (fetch/axios) → GlideAjax (client-to-server bridge)
// ViewComponents          → UI Scripts, Widgets (Service Portal)
// [Authorize] attribute   → ACLs (access control lists)
// Startup.cs middleware   → System Properties + Before Query BRs

Script Includes — Your Service Layer

// This is like a C# class in your Services/ folder
var IncidentHelper = Class.create();
IncidentHelper.prototype = Object.extendsObject(
  AbstractAjaxProcessor, { // Extend this to expose via GlideAjax
  
  // Server-only method (like a private service method)
  calculateSLA: function(incidentGR) {
    var priority = incidentGR.getValue('priority');
    var slaHours = {1: 4, 2: 8, 3: 24, 4: 72};
    return slaHours[priority] || 72;
  },
  
  // Client-callable method (like a [HttpGet] API endpoint)
  getIncidentSLA: function() {
    var incId = this.getParameter('sysparm_incident_id');
    var gr = new GlideRecord('incident');
    if (gr.get(incId)) {
      return this.calculateSLA(gr);
    }
    return -1;
  },
  
  type: 'IncidentHelper'
});

GlideAjax — Your AJAX Calls

// Client Script calling GlideAjax
// This is like: fetch('/api/incident/getSLA?id=123')
var ga = new GlideAjax('IncidentHelper'); // The Script Include name
ga.addParam('sysparm_name', 'getIncidentSLA'); // Method to call
ga.addParam('sysparm_incident_id', g_form.getUniqueValue());
ga.getXMLAnswer(function(answer) {
  // 'answer' is the return value from the server method
  g_form.setValue('u_sla_hours', answer);
});

The Critical Boundary

  • Client-side (runs in browser): Client Scripts, UI Policies, UI Scripts, GlideAjax calls. You have g_form (form manipulation API — like jQuery for ServiceNow forms) and g_user (current user context).
  • Server-side (runs on instance): Business Rules, Script Includes, Scheduled Jobs, Flow Designer. You have GlideRecord, GlideSystem (gs), and full database access.
  • You cannot call GlideRecord from a Client Script. This is the #1 mistake .NET devs make — assuming they can query the DB from the browser like they would with Entity Framework in a Blazor Server app. The bridge is always GlideAjax.
💡 .NET Developer Pro Tip

Think of GlideAjax as your fetch('/api/...') calls. The Script Include with AbstractAjaxProcessor is your API Controller. The method you call via sysparm_name is the action method. The parameters you pass are your query string. Everything you know about async client-server communication applies — just with different syntax.