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.
| Question | Short 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 type | Table | Notes |
|---|---|---|
| Script Include | sys_script_include | One file, one record. The script goes into the script field |
| Business Rule | sys_script | Condition and script, one record |
| Scheduled Job | sys_trigger | One record |
| System Property | sys_properties | One record |
| Table / Field | sys_db_object / sys_dictionary | Fields become individual dictionary rows, as they always have. Not a Build Agent difference |
| UI Action | sys_ui_action | One record |
| ACL | sys_security_acl | One record, plus sys_security_acl_role for role conditions, which is standard |
| REST Message | sys_rest_message | Parent plus HTTP methods, the usual structure |
| Notification | sysevent_email_action | One 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 record | Table | Why it exists |
|---|---|---|
| JS bundle | sys_ux_lib_asset | The compiled React app |
| Source map | sys_ux_lib_asset | Debug mapping |
| CSS asset | sys_ux_theme_asset | Compiled styles |
| Source archive | sn_glider_source_artifact + sys_attachment | Source code for IDE editing |
| Link records | sn_glider_source_artifact_m2m | Connects 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 record | Table | Why it exists |
|---|---|---|
| Macroponent definitions | sys_ux_macroponent | Component definitions |
| Data broker transforms | sys_ux_data_broker_transform | Data wiring |
| Component registrations | sys_ux_lib_component | Component registry |
| Source resources | sys_ux_lib_source_resource | Component source code |
| Client scripts | sys_ux_client_script | Event 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 record | Table | Why it exists |
|---|---|---|
| Action type definitions | sys_hub_action_type_definition | Custom action schemas |
| Step inputs / outputs | sys_hub_step_ext_input / sys_hub_step_ext_output | Step interface definitions |
| Variable values | sys_variable_value | Default values and configurations |
| Flow entries | sys_hub_flow | The 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
| Record | Table | Why it exists |
|---|---|---|
| Table record | sys_db_object | The table definition |
| Column records | sys_dictionary | One per field |
| Number record | sys_number | Auto-number configuration |
| Module | sys_app_module | Navigation module |
| Default ACLs | sys_security_acl | Table and field-level ACLs |
| Label entries | sys_documentation | Field 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.
| Signal | What it means |
|---|---|
| HTML contains @fluent-import-html | BYOUI page, with external bundle assets |
| HTML references a .jsdbx file | The compiled JS bundle lives in sys_ux_lib_asset |
| Record has entries in sn_glider_source_artifact_m2m | It has linked source artifacts |
| Created by Build Agent / admin with sys_mod_count: 0 | It was deployed as a batch, so it likely has companion records |
| keys.ts references multiple IDs | Each 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"
| Question | Traditional answer | Build Agent answer |
|---|---|---|
| Where do I look to understand the current state? | The instance record | The source files in the workspace |
| What happens if instance and source disagree? | Instance wins | Source wins (the next deploy overwrites the instance) |
| Can I safely edit on the instance? | Yes, always | Only if you remove the @fluent-import-html marker or the record from keys.ts |
| Where is the history? | sys_update_version on the instance | Your 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 exists | What is missing |
|---|---|
| Build Agent tracks dependencies internally | No "export everything for this solution" button |
| Update sets capture individual records | No automatic "include all related Build Agent artifacts" |
| keys.ts lists all managed records | No UI to visualize this outside the IDE |
| Scoped apps package automatically | Global 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.

No comments:
Post a Comment