Sunday, October 25, 2026

ServiceNow Build Agent vs Traditional Development: Hidden Records, keys.ts and the Source-of-Truth Inversion

ServiceNow Build Agent vs Traditional Development

Traditional ServiceNow development has one comfortable rule: you create a record, you see the record, you move the record. A Business Rule is one row in sys_script. A Script Include is one row in sys_script_include. Whatever you can open on the instance is what goes into the update set.

Build Agent changes that rule quietly. Some things it creates still map one-to-one to a platform record. Others come with a set of companion records you never opened, never named and may not notice until a promoted page loads blank on the next instance. This article puts the two approaches side by side: what stays the same, what is different, how to spot hidden dependencies, and how to deploy without leaving pieces behind.

QuestionShort answer
Is Build Agent different for simple metadata?No. Script Includes, Business Rules, ACLs, properties and similar items are still one source definition = one platform record
Where do the surprises come from?UI Pages (BYOUI / React), UX pages and workspaces, Flow Designer artifacts, Record() seed data and table definitions
What is the single most important file?keys.ts, the inventory of every record the SDK manages
What is the biggest mindset change?The source files become the source of truth. Instance records become compiled output
What is the biggest practical risk?Promoting only the record you can see and leaving its companion records behind

1. Metadata That Behaves the Same Way (1:1)

Start with the reassuring part. For a large set of common metadata, it makes no difference whether you create it traditionally or through Build Agent. One definition becomes one platform record, and that record is safe to share, review and move on its own.

Metadata typeTableNotes
Script Includesys_script_includeOne file, one record. The script goes into the script field
Business Rulesys_scriptCondition and script, one record
Scheduled Jobsys_triggerOne record
System Propertysys_propertiesOne record
Table / Fieldsys_db_object / sys_dictionaryFields become individual dictionary rows, as they always have. Not a Build Agent difference
UI Actionsys_ui_actionOne record
ACLsys_security_aclOne record, plus sys_security_acl_role for role conditions, which is standard
REST Messagesys_rest_messageParent plus HTTP methods, the usual structure
Notificationsysevent_email_actionOne record

What to actually learn: do not treat Build Agent as universally risky. For this list, your existing update set habits still work as they are.

2. Metadata That Creates Hidden Records (1:Many)

Now the part that catches people off guard. In these cases one thing you define in source produces several platform records, and some of them are records you would never think to look for.

UI Page (BYOUI / React): the biggest offender

Hidden recordTableWhy it exists
JS bundlesys_ux_lib_assetThe compiled React app
Source mapsys_ux_lib_assetDebug mapping
CSS assetsys_ux_theme_assetCompiled styles
Source archivesn_glider_source_artifact + sys_attachmentSource code for IDE editing
Link recordssn_glider_source_artifact_m2mConnects page to source

Risk if missing: a blank page. The HTML shell loads, but the actual application never runs. This is the failure that looks the most mysterious, because the sys_ui_page record is present and looks perfectly healthy.

UX Page / Workspace Experience (UI Builder)

If you use Build Agent to define UX Framework pages such as UI Builder pages or workspace experiences, expect a different set of companions.

Hidden recordTableWhy it exists
Macroponent definitionssys_ux_macroponentComponent definitions
Data broker transformssys_ux_data_broker_transformData wiring
Component registrationssys_ux_lib_componentComponent registry
Source resourcessys_ux_lib_source_resourceComponent source code
Client scriptssys_ux_client_scriptEvent handlers

Risk if missing: the workspace page loads, but components are missing, broken or showing errors.

Flow / Subflow / Action

When Build Agent creates Flow Designer artifacts, the flow definition is only part of the picture.

Hidden recordTableWhy it exists
Action type definitionssys_hub_action_type_definitionCustom action schemas
Step inputs / outputssys_hub_step_ext_input / sys_hub_step_ext_outputStep interface definitions
Variable valuessys_variable_valueDefault values and configurations
Flow entriessys_hub_flowThe flow definition itself

Risk if missing: the flow fails at runtime, actions report "definition not found," and steps have missing inputs.

Seed data with Record()

When you use Record() in Fluent to seed data, it creates real data records in whatever table you target: sys_choice, sys_user_role, sys_properties, custom tables and so on. It is also tracked in keys.ts, so if you remove a Record() from source, the SDK may want to delete it on the next deploy.

Risk if missing: the configuration data your app depends on does not exist. Choice values are missing, roles are undefined, properties are absent.

Table definitions

RecordTableWhy it exists
Table recordsys_db_objectThe table definition
Column recordssys_dictionaryOne per field
Number recordsys_numberAuto-number configuration
Modulesys_app_moduleNavigation module
Default ACLssys_security_aclTable and field-level ACLs
Label entriessys_documentationField labels and help text

This is similar to the traditional approach, because creating a table has always created child records. The difference to watch for is that Build Agent may also generate ACLs and modules that you did not explicitly define.

What to actually learn: the record you can see in a list view is not always the whole artifact. Before you call something "one record," check what it brings with it.

3. How to Tell Whether Something Has Hidden Dependencies

You do not need to memorize the tables above. These signals tell you when to stop and look closer.

SignalWhat it means
HTML contains @fluent-import-htmlBYOUI page, with external bundle assets
HTML references a .jsdbx fileThe compiled JS bundle lives in sys_ux_lib_asset
Record has entries in sn_glider_source_artifact_m2mIt has linked source artifacts
Created by Build Agent / admin with sys_mod_count: 0It was deployed as a batch, so it likely has companion records
keys.ts references multiple IDsEach ID is a separate managed record

4. keys.ts: Your Source of Truth

Every Build Agent workspace has a keys.ts file that lists every record the SDK manages. Treat it as the definitive inventory. A simplified example:

// Example keys.ts
export default {
  'sys_ui_page:8d935077...': 'incident_xyz',
  'sys_ux_lib_asset:abc123...': 'global/incident-xyz',
  'sys_ux_lib_asset:def456...': 'global/incident-xyz.js.map',
  'sys_ux_theme_asset:ghi789...': 'styles',
  'sys_script_include:1a9582...': 'XyzUtil',
  // ... every managed record listed here
}

One page, one Script Include, and already four more records than you would have counted by eye. The rule is simple: if it is in keys.ts, it needs to travel with the solution.

5. The Source-of-Truth Inversion

This is the deepest difference between the two approaches, and it is a mindset change more than a technical one.

Traditional:
  Instance record IS the source of truth
        |
        v
  Export to update set for transport

Build Agent:
  Source files in the workspace ARE the source of truth
        |
        v
  Build compiles to instance records
        |
        v
  Instance records are "compiled output"
QuestionTraditional answerBuild Agent answer
Where do I look to understand the current state?The instance recordThe source files in the workspace
What happens if instance and source disagree?Instance winsSource wins (the next deploy overwrites the instance)
Can I safely edit on the instance?Yes, alwaysOnly if you remove the @fluent-import-html marker or the record from keys.ts
Where is the history?sys_update_version on the instanceYour source files, plus whatever version control you use

What to actually learn: a hotfix made directly on the instance is a normal move in traditional development. With Build Agent-managed records it can be silently overwritten by the next deploy. Decide up front which side owns each record.

6. The Packaging Gap

Put the pieces together and a real tooling gap appears, particularly in Global scope.

What existsWhat is missing
Build Agent tracks dependencies internallyNo "export everything for this solution" button
Update sets capture individual recordsNo automatic "include all related Build Agent artifacts"
keys.ts lists all managed recordsNo UI to visualize this outside the IDE
Scoped apps package automaticallyGlobal scope has no equivalent

The platform assumes you are either in a scoped app, where packaging is automatic, or doing traditional development, where one record is one artifact. Build Agent in Global scope sits in an awkward middle ground between the two. Until that gap closes, the discipline has to come from your process.

7. A Practical Deployment Habit: Name the Update Set at Install Time

One thing that helps right away: Build Agent supports an updateSetName parameter when it runs build_install. If your naming convention is known at build time, the deployment can go straight into a properly named update set instead of the SDK's default. For example, if your convention is change number plus description:

build_install with updateSetName = "CHG0012345 - Incident XYZ Fix"

The SDK then creates or uses that update set name. For teams with a strict change process, this is probably the cleanest fit.

A short checklist that works alongside it:

  • Open keys.ts before every promotion and count the managed records
  • Confirm every ID listed is present in the update set you are about to move
  • Check for the signals in section 3 on any UI Page or workspace page
  • Decide whether a record is owned by the source or by the instance, and write it down
  • After promotion, load the page or run the flow on the target instance. A blank page is the classic sign of a missing bundle asset

8. Test Yourself

Knowing the tables is one thing. Spotting the problem under pressure is another. Try these two before reading the answers.

Question 1. A Build Agent-created BYOUI page works on Dev. After promotion, Test shows a blank page, although the sys_ui_page record is present. What is the most likely cause?

Question 2. A developer fixes a typo directly on the instance for a record that Build Agent manages. Two days later the fix disappears. Why?

Answers: (1) The companion records did not travel, most likely the JS bundle in sys_ux_lib_asset and the CSS in sys_ux_theme_asset. The HTML shell loads but the app never runs. (2) Source wins. The next deploy rebuilt the instance record from the workspace source files and overwrote the manual edit.

If you enjoy thinking through situations like these, I turned this style of thinking into a full practice book: ServiceNow Build Agent Mastery Quiz: 120 Scenario-Based Questions with Detailed Answers and Explanations for Developers, Admins, Architects and Teams. Every question has its own page, and the answer with a full explanation sits on the next page, so you can commit to an answer before you check it. It covers hidden records, keys.ts, team dynamics, troubleshooting and role-specific scenarios. It is available on Amazon KDP if you would like to take a look. It is entirely optional, and everything in this article stands on its own.

9. Frequently Asked Questions

Is Build Agent riskier than traditional development?
Not across the board. For common 1:1 metadata such as Script Includes and Business Rules it behaves the same way. The extra care is needed for UI Pages, UX pages, Flow artifacts, seed data and table definitions.

What is a hidden record?
A platform record created alongside the thing you defined, such as the compiled JS bundle behind a BYOUI page, which you did not create or name yourself.

Where do I find the full list of records Build Agent manages?
In keys.ts in the workspace. It is the inventory of every managed record.

Can I edit a Build Agent record directly on the instance?
Only if you first remove the @fluent-import-html marker or the record from keys.ts. Otherwise the next deploy overwrites your change.

Does this problem exist in scoped apps?
Scoped apps package their records automatically, so the gap described here is most visible in Global scope.

Wrap-Up

Build Agent does not replace what you already know about ServiceNow. It adds a layer on top of it: a source-first workflow, a manifest in keys.ts, and a set of records that arrive in groups instead of one at a time. Most of your habits survive. The ones that do not are the ones that assume the visible record is the whole story.

If there is one habit to take from this article, it is the question to ask before every promotion: what else came with this record? If you want to sharpen that instinct with realistic scenarios, the Build Agent Mastery Quiz is there for exactly that purpose. Either way, ask the question before you deploy, not after the page goes blank.


Note: Table names, record behavior and tooling details can change between releases. Verify them on your own instance and in the official ServiceNow documentation before relying on them. ServiceNow is a trademark of ServiceNow, Inc. This blog is independent and not affiliated with or endorsed by ServiceNow, Inc.